Skip to content

Remove inline-template #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 13, 2019
Merged

Remove inline-template #98

merged 4 commits into from
Dec 13, 2019

Conversation

yyx990803
Copy link
Member

@yyx990803 yyx990803 commented Nov 14, 2019

@yyx990803 yyx990803 added breaking change This RFC contains breaking changes or deprecations of old API. core 3.x This RFC only targets 3.0 and above labels Nov 14, 2019
@imacrayon
Copy link

imacrayon commented Nov 15, 2019

I build a lot of Laravel sites. A common situation where I rely on inline-template is when I need a navigation bar at the top of the page that is toggled with a "hamburger" button. I usually use inline-template when building responsive navigation components like this because, from what I know, the main navigation of a website is important for SEO. Rather than having all the markup for the navigation tucked away in a JS file I can dump it all on the page using inline-template. I wonder if using a <script type="text/html"> tag in place of inline-template might have any negative SEO impacts? It seems like search engines are pretty good at indexing all kinds of stuff nowadays so maybe this shouldn't even be a concern?

@willtpwise
Copy link

It seems like search engines are pretty good at indexing all kinds of stuff nowadays so maybe this shouldn't even be a concern?

There's a bunch of articles out nowadays suggesting that Google would handle the "hidden in a JS file" scenario pretty well. A key takeaway from this article is that Google will wait 5-20 seconds before taking a snapshot of the page. So I'd say you're fine in that regard.

See: https://medium.com/@l.mugnaini/spa-and-seo-is-googlebot-able-to-render-a-single-page-application-1f74e706ab11

@imacrayon
Copy link

There's a bunch of articles out nowadays suggesting that Google would handle the "hidden in a JS file" scenario pretty well. A key takeaway from this article is that Google will wait 5-20 seconds before taking a snapshot of the page. So I'd say you're fine in that regard.

You're right, but I think most other search engines lag behind Google a bit, that's mostly the cause for my hesitation. Check out:
https://moz.com/blog/search-engines-ready-for-javascript-crawling
https://www.smashingmagazine.com/2019/05/vue-js-seo-reactive-websites-search-engines-bots/

@leopiccionia
Copy link

A common situation where I rely on inline-template is when I need a navigation bar at the top of the page that is toggled with a "hamburger" button.

Would it be possible to refactor it to use a default slot?

@imacrayon
Copy link

Would it be possible to refactor it to use a default slot?

Yep! I think that’s a great solution. Thanks!

@yyx990803
Copy link
Member Author

@yyx990803
Copy link
Member Author

This RFC is now in final comments stage. An RFC in final comments stage means that:

The core team has reviewed the feedback and reached consensus about the general direction of the RFC and believe that this RFC is a worthwhile addition to the framework.
Final comments stage does not mean the RFC's design details are final - we may still tweak the details as we implement it and discover new technical insights or constraints. It may even be further adjusted based on user feedback after it lands in an alpha/beta release.
If no major objections with solid supporting arguments have been presented after a week, the RFC will be merged and become an active RFC.

@mpawelski
Copy link

Hi everyone.
I'm in the process of introducing Vue to our current project and we had plans to use inline-templates because of this advantages:

  1. no additional build step
  2. easy support for some kind of "server side rendering" (mitigate flickering with careful use of v-cloak)

Replacement 1 (templates in script tags) gives us 1) and replacement 2 (default slots) gives us 2).
However I'm not sure how the transition from component using inline template to component using default slot should look like. I came up with something like that:

<body>
    <div id="app">
        <h1>Test inline template</h1>
        <test-inline-template :init-counter="3" inline-template>
            <div>
                <div>counter (start from {{initCounter}}): {{counter}}</div>
                <button v-on:click="methodClick">Click</button>
            </div>
        </test-inline-template>

        <h1>Test default slot</h1>
        <test-default-slot :init-counter="4" v-slot="vm">
            <div>
                <div>counter (start from {{vm.initCounter}}): {{vm.counter}}</div>
                <button v-on:click="vm.methodClick">Click</button>
            </div>
        </test-default-slot>
    </div>

    <script>
        Vue.component("test-inline-template", {
            props: {
                initCounter: {
                    type: Number,
                    required: true
                }
            },
            data: function () {
                return {
                    counter: this.initCounter
                }
            },
            methods: {
                methodClick: function () {
                    this.counter++;
                }
            }
        });

        Vue.component("test-default-slot", {
            template: `
        <div>
            <slot
            :initCounter="initCounter",
            :counter="counter",
            :methodClick="methodClick"
            ></slot>
        </div>
        `,
            props: {
                initCounter: {
                    type: Number,
                    required: true
                }
            },
            data: function () {
                return {
                    counter: this.initCounter
                }
            },
            methods: {
                methodClick: function () {
                    this.counter++;
                }
            }
        })


        var app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vue!'
            }
        })
    </script>
</body>

And the only thing I hate about this is passing all data, props and methods to <slot></slot>.

@yyx990803 Is it possible to write some render function that will be equivalent to my template in test-default-slot component but without having to pass explicitly all props, data and methods?

I'm asking for solution for Vue 2 but showing how it could be done in Vue 3 (with current rfc) would be also helpful when we'll do migration when Vue 3 is released.

There's not much info on web about inline-templates but from this I see people use it a lot. So I'm sure I'm not the only one that wonder what's the best approach to migrate from inline-template.

@LinusBorg
Copy link
Member

Replacement 1 (templates in script tags) gives us 1) [a build step]

Maybe we use the term "build step" for different things, but this solution does not require any build tooling like webpack or whatever. Can you clarify?

@mpawelski
Copy link

@LinusBorg
I meant that I plan to write code like in 1. and 2. but I wonder if for 2. there is better approach that what I presented in my example.

I really want to avoid passing explicitly every prop, data and method that can be used in default slot.

I want this "default slot" approach (which is proposed replacement for inline-template in this RFC) to be as similar as possible to current inline-template feature. Specifying all component's props, datas and methods as slot's props is a bit burdensome.

@LinusBorg
Copy link
Member

You don't really have to provide them one by one manually. You could do this:

computed: {
  self() { return { ...this} }
}
<slot v-bind="self" />

@mpawelski
Copy link

@LinusBorg thanks! Nice workaround, but it doesn't pass other computed fields. Do you have some idea how to also pass them (without doing it one by one manually)?

<body>
    <div id="app">
        <h1>Test default slot2</h1>
        <test-default-slot2 :init-counter="4" v-slot="vm">
            <div>
                <div>counter (start from {{vm.initCounter}}): {{vm.counter}} | {{vm.counterPlusTen}}</div>
                <button v-on:click="vm.methodClick">Click</button>
            </div>
        </test-default-slot2>
    </div>

    <script>
       Vue.component("test-default-slot2", {
            template: `<div><slot v-bind=self></slot></div>`,
            computed: {
                self() { return { ...this } },
                counterPlusTen() {
                    return this.counter + 10;
                }
            },
            props: {
                initCounter: {
                    type: Number,
                    required: true
                }
            },
            data: function () {
                return {
                    counter: this.initCounter
                }
            },
            methods: {
                methodClick: function () {
                    this.counter++;
                }
            }
        })


        var app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vue!'
            }
        })
    </script>
</body>

Only {{vm.counterPlusTen}} is not recognized here.

@LinusBorg
Copy link
Member

Don't see an obvious reason why that should not work, no.

@mpawelski
Copy link

@LinusBorg hmmm... so should I create a bug on https://github.com/vuejs/vue for it?
After second check it looks that props are not working either with this approach
Here's living example https://codepen.io/mpawelski/pen/vYOVWza

@leopiccionia
Copy link

leopiccionia commented Mar 24, 2020

It probably won't work because computed properties dependencies are lazily collected -- it doesn't matter if you track it via getters, like Vue 2, or proxies, like Vue 3. (I don't know why props don't work.)

If you want that computed properties to be available, you should mention them explicitly:

computed: {
  counterPlusTen () {
    return this.counter + 10
  },
  self () {
    return {
      ...this,
      counterPlusTen: this.counterPlusTen,
    }
  }
},

One could create a helper (I haven't tested it):

computed: {
  // ...
  self () {
    const self = { ...this, ...this.$props }
    for (const key of Object.keys(this.$options.computed)) {
      if (key !== 'self') {
         self[key] = this[key
      }
    }
    return self
  },
}

An important point to be made is that you may not need all properties to be available to the slot; the slot just need to contain the part of the template that's really dynamic.

@yyx990803 yyx990803 added feature removal feat: template syntax Changes related to template syntax and removed final comments This RFC is in final comments period labels Jul 1, 2020
@bencodezen bencodezen mentioned this pull request Jul 6, 2020
25 tasks
@qgates
Copy link

qgates commented Jul 20, 2020

I'm left wondering at this point what the best practice is for integrating vue with lightweight, potentially nested components where the template(s) are php rendered (say) by blade or twig and client side code is contained in .vue files. Using slots isn't a drop in replacement, requiring and using <script> tags won't work for nested components (where the outer element is already a vue instance or component). The migration doc isn't too clear on any of this; the approach above from @mpawelski appears workable but isn't particularly semantic.

Since the inline-template approach has been heavily used in the community for integrating vue with php server-side systems/apps this is going to be a difficult transition. What are the best practice recommendations for such use-cases?

Apologies if this should be discussed elsewhere please point me in the right direction!

@imacrayon
Copy link

@qgates a lot of the time you can convert to slots with just a few changes to your component, but you're right, it's not a drop-in replacement. If you want to use <script> tags instead you would need to use a template feature like Blade's Stacks. This lets you drop markup into your template outside of your component's HTML. This feature is also called "Portals" in some template languages, I'm not very familiar with Twig but I think you could find a plugin that does something similar like this: https://github.com/carlcs/craft-twigportal

@qgates
Copy link

qgates commented Jul 21, 2020

@imacrayon thanks for those thoughts. It just appears as though there's no strategy for using vue in a lightweight way in larger, tradition server side applications, where is served up from SS templates and we just want to add vue reactivity. The inline-template approach has its drawbacks, but has been widely used for this purpose. I'm all for change, but there should be some advice and best practice notes for using view in this way. Wasn't a fan of it listed under 'edge cases' in the original user guide either! 😉

@yyx990803
Copy link
Member Author

For simply enhancing server-rendered markup, take a look at AlpineJS. It has Vue-inspired syntax but is specifically targeting the "enhancing server-rendered markup" use case.

If Alpine doesn't fit the scale of your application, then you really should use a build setup. It's not that hard.

@qgates
Copy link

qgates commented Jul 21, 2020

Thanks @yyx990803, and sorry if my comments came over as complaint. It's not that it's hard per se, it's just that it's harder, especially for those invested in Vue 2 in large scale projects using the inline-template approach. The suggestions assume a drop-in alternative which neither are. Some additional notes in the migration guide might be useful.

Straying off-topic, another example is the removal of delimiters to change {{ }}. I can't find any reference in docs-next to how/where this can be configured (in a webpack setup) so we're faced with the prospect of refactoring large amounts of blade/twig templates in our projects. Any advice here appreciated 🙂

If Alpine doesn't fit the scale of your application, then you really should use a build setup. It's not that hard.

Care to elaborate? We're already using webpack on the client side.

@leopiccionia
Copy link

@qgates It may please you that Vue 3.0 doesn't have the same shortcomings related to props and computed properties.
https://codepen.io/leopiccionia/pen/rNxoROd

@qgates
Copy link

qgates commented Jul 21, 2020

Thanks @leopiccionia, lots to like about Vue 3 and I'm learning all the time :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x This RFC only targets 3.0 and above breaking change This RFC contains breaking changes or deprecations of old API. core feat: template syntax Changes related to template syntax feature removal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants