vuejs / vue-router

🚦 The official router for Vue 2
http://v3.router.vuejs.org/
MIT License
18.99k stars 5.06k forks source link

Discuss alternative <script> lazy-loading technique in docs #467

Closed taoeffect closed 8 years ago

taoeffect commented 8 years ago

While working on my "modern web development guide", which mentions Vue.js a good amount, I wanted to explain the various ways that third-party javascript libraries can be used by designers.

Designers are often used to having a vendor folder to plop in libraries like jQuery, which are then included using the traditional way (via a <script> tag in the HTML).

I'm writing an FAQ on lazy-loading and wanted to explore this route to see if it would work over the less intuitive lazy-loading that webpack/browserify do via require. The question I'm trying to answer is just what are all the primary techniques to reduce the size of the app.js bundle.

During my experiments, I found out a few things:

1. Including <script> tags in the HTML that gets swapped in by vue-router does nothing

This applies to both .vue and .ejs files. Adding this doesn't load jQuery when the view is swapped in:

<script src="/vendor/jquery.js" async></script>

I'm curious, why does that not work? I think this traditional (expected) way of doing things would be a welcome convenience for designers.

2. It's possible to lazy-load 3rd party libraries using <script> and vue-router

You just have to do it in a round-about way within a .vue file using the component's ready method. Example:

<script>
export default {
  ready() {
    if ( ! window.jQuery ) {
      var s = document.createElement("script")
      s.type = "text/javascript"
      s.src = "/simple/vendor/jquery.js"
      s.async = true
      this.$el.appendChild(s)
    }
  },
  methods: {
    submit: function () {
      var $ = window.jQuery
      // do something with $...
    }
  },

That actually works. 😄

Could the Lazy-loading documentation cover this technique (maybe also pros/cons)?

3. Libraries don't get unloaded when the view is swapped out

Another observation that I noticed is that once a third-party library gets loaded in a single-page app, it never gets unloaded. So if an app has a bunch of libraries, and the user visits "all the pages", then they'll be using up a good amount of memory since none of the global JS that gets attached to window ever gets unloaded. I was also wondering what "the right" way to do this is (and maybe the docs could also cover it?).

Curious to hear your thoughts!

yyx990803 commented 8 years ago

This does work, but I think it's serving a different purpose from the "lazy loading" discussed specifically in the context of vue-router and with a module system.

Things like jQuery dependencies are easy to lazy load like this because they are a well-defined piece of code that never changes. Nor does it have any module relationship to the rest of your code. But what webpack lazy loading achieves is you can write your code in small modules, and let the build tool determine the splitting and bundling of chunks for you, and most importantly, it is about lazy loading a Vue component.

So while I think your technique is useful in certain contexts, it probably doesn't fit vue-router's docs in this case.

taoeffect commented 8 years ago

@yyx990803 Do you know why simply adding a <script> tag to the <template> doesn't work?

yyx990803 commented 8 years ago

Vue filters out <script> tags when parsing the templates.

taoeffect commented 8 years ago

@yyx990803 ... Is there a reason for that? Would be neat if they could be used to reduce the app.js bundle size as per above.

taoeffect commented 8 years ago

I just had an idea, maybe it's possible to create a Vue component to do the above. It could even be a built-in component to Vue.js that would be pretty useful.

An ideal <script> component would take additional parameters that specified what globals get defined and whether or not to "unload" them when it gets swapped out.

For example, this would only load the script if jQuery hasn't already been loaded:

<script src="vendor/jquery.min.js" v-if="! window.jQuery"></script>

And something along these lines could unload jQuery when it gets swapped out:

<script src="vendor/jquery.min.js" v-if="! window.jQuery" v-unload="window.jQuery"></script>

In the meantime I'll look into implementing this myself under a script2 tag or something, but do you think this is something that could be added to Vue.js itself? (Should I open an issue for it somewhere?)

yyx990803 commented 8 years ago

I think it's an anti pattern to include <script> in component templates, as code dependency logic should be handled in JS, not templates. So I don't think this will be added to core, but feel free to implement <script2>!

taoeffect commented 8 years ago

OK, I do not see how this is an anti-pattern. It is standard HTML, and it's been how HTML development has been done since it started. I don't want to force designers to relearn "how to include jQuery using this weird new trick" if there's no good reason for it. In this case, I do not see a good reason for changing the standard behavior and using require in the JS section of a .vue file. That is weird, non-standard behavior that also bloats a global app.js file that gets included on every "page".

yyx990803 commented 8 years ago

@taoeffect you are free to think that way, but I maintain my opinion.

taoeffect commented 8 years ago

@yyx990803 It's very likely the case that I'm just not groking some part of your viewpoint.

Could you help me understand then, what you would consider to be the best + simplest way to "lazy load" a third-party "vendor" lib like jQuery in the situation shown above?

taoeffect commented 8 years ago

Also, I am running into bit of trouble coming up with a good/convincing explanation for why Vue.js prevents .ejs files from containing <script> tags. I get that you have whatever (not very clear to me, see question in comment above) reasons for wanting to keep <script> out of the <template> area, but this doesn't make as much sense when .ejs files are being used in a project that uses vue-router as there is no dedicated <script> section there to add them in.

I thought Vue.js was supposed to be a flexible framework, but it seems I've stumbled upon an area of inflexibility here. :-\

taoeffect commented 8 years ago

Also, what's weird is that the <script> tag is passed through to the page currently (for both .vue and .ejs files), but for whatever reason it doesn't get "loaded" by the page the way it does when the document.createElement technique is used:

screen shot 2016-04-24 at 4 19 32 pm

taoeffect commented 8 years ago

Hey @azamat-sharapov and @fnlctrl, it's real easy to click a button and thumbs down somebody's comment, but I don't see either of you making any actual remarks here explaining yourselves. Feel free to do so.

BTW, this Script2 thing will be available as a Vue.js plugin soon for everyone to use, and thus Vue.js users will benefit from being able to use <script> again. 😄

azamat-sharapov commented 8 years ago

The reason I finally downvoted your comments is because I now know how ungrateful person you are. A man is giving a lot of his time to this project and have been helping many people when they asked, doing his best. But you are calling him stubborn in PR's description in your project. It's not first time you are opening issue and acting like that, there was another one earlier. But I haven't seen you around helping others in forum, gitter, issues or pushing commits.

Personally I'm not interested in hacks like script2. I knew about limitation, that script doesn't get loaded, if inserted into DOM later and I have provided somebody JS solution for this in another issue, some time ago. (you have same thing in issue description). I load dependencies in JavaScript. If not, I just include it in doc head, instead of putting it in Vue component.

Hopefully I wasn't too rude, but please: I'm not looking forward for your reply. This isn't place for such discussions.

BTW, your "issue" fits better in forums.

taoeffect commented 8 years ago

But you are calling him stubborn in PR's description in your project.

Let's see:

And all I want is for HTML to behave like HTML, if not in .vue files then at least in .ejs files where that behavior is the default expectation everyone has.

So yes, ignoring such a simple request is the very definition of stubborn. I downvoted your comment for your inability to see this.

But I haven't seen you around helping others in forum, gitter, issues or pushing commits.

lol.

I will not even defend myself here, as it should be readily apparent to anyone who has spent time looking at my contributions how ridiculous your statement is.

How you can make such a statement when I've made contributions in this very thread is beyond me.

Personally I'm not interested in hacks like script2.

It's only a "hack" because of that 2 there, which is not my fault but the fault of Vue.js. Otherwise it's just standardized behavior that web developers expect and Vue.js breaks.

I knew about limitation,

Thanks for admitting it's a limitation. Thankfully though, one that I've spent time writing code to get around.

BTW, Script2 is now fully-functional and then some. It now supports inlined javascript as people expect. Plus it can ensure that inlined JS only gets executed when a global becomes available, for example:

<script2 vendor="jquery" global="jQuery"></script2>
<script2 type="text/javascript" waitfor="$">
    $('#todo').append('<li>This was inserted via jQuery using script2</li>')
</script2>

You can see its code here, and at some point I'll get around to publishing a standalone Vue.js plugin.

yyx990803 commented 8 years ago

I don't want to explain it because I'm not your personal tech support and have more inportant things to focus on. You keep throwing walls of text and demanding answers, while maybe not being inpolite, it was certainly inconsiderate and I didn't feel it was a rewarding conversation. Fair enough?

taoeffect commented 8 years ago

"Personal tech support"?

I'm not the one going around telling people I'm working on a web framework for them to use while breaking the HTML standard for no good reason and then telling people to bugger off when they report a problem because of it.

You've yet to answer my question about what I'm supposed to do to assist those I work with in accomplishing the plain, ordinary and common task of including a third-party vendor script like jQuery without including every vendor script on every page.

And no, the nonsense you have here is not an answer. People who just want to load jQuery are not loading an entirely different "route". Code-splitting doesn't make sense here, but a 1-line standardized <script> tag does, and it would work just fine with this framework if it didn't intentionally break it.

Who are you making this framework for? Just yourself? If so, then please just be clear about that, especially on your Patreon page for this project. Then you'll be free to call your users "inconsiderate" for reporting bugs in your web framework.

yyx990803 commented 8 years ago

This is not a bug, nor is it breaking any standard, because a Vue component never promises to be "standard compliant". It is a conscious design choice. The template is a DSL for mapping your state to the rendered DOM, injecting script tags with unknown side effects inside a component template is an anti-pattern in my opinion and I will not support that feature.

P.S. I have no obligation in any way to bend the design to cater to what you believe is right, even if you are a patron. Your attitude is toxic and entitled, and I couldn't care less if you use my software or not.