HerringtonDarkholme / av-ts

A modern, type-safe, idiomatic Vue binding library
MIT License
216 stars 11 forks source link

Async Components (Lazy Loading) #46

Closed gazugafan closed 7 years ago

gazugafan commented 7 years ago

Is it possible to create Async Components? I don't see a way to translate something like this...

Vue.component('async-webpack-example', function (resolve) {
  // This special require syntax will instruct Webpack to
  // automatically split your built code into bundles which
  // are loaded over Ajax requests.
  require(['./my-async-component'], resolve)
})

into an av-ts decorated component class. Maybe I'm missing something obvious?

HerringtonDarkholme commented 7 years ago

You just need a layer of indirection:

Vue.component('async-webpack-example',  function (resolve) {
   require.ensure('some dep').then(() => {
       @Component  class Test {}
        resolve(Test)
     })
})

Rule of thumb is av-ts is just a thin wrapper around Vue.extend

gazugafan commented 7 years ago

Thanks so much for the quick reply! I'm feeling a little in over my head, and probably trying to take in too many new things at once. I've got the following simple component...

    import { Vue, Component, Lifecycle } from 'av-ts';

    @Component
    export default class Hello extends Vue
    {
        @Lifecycle mounted() {
            console.log('hello mounted')
        }
    }

And trying to have it lazy load. So, using your suggestion I've changed it to...

    import { Vue, Component, Lifecycle } from 'av-ts';

    Vue.component('hello', function (resolve) {
        require.ensure('./hello.vue').then(() => {
            @Component class Hello extends Vue
            {
                @Lifecycle mounted() {
                    console.log('hello mounted')
                }
            }
            resolve(Hello)
        })
    })

... which throws a compilation error of "Module '"hello.vue"' has no default export.". Keeping the "export default" in front of the class declaration throws "Modifiers cannot appear here.".

Maybe I should be doing this in whatever parent component is importing this component? I played around with that approach as well, but no luck. Any other suggestions?

HerringtonDarkholme commented 7 years ago

It probably is your webpack configuration problem. https://herringtondarkholme.github.io/2016/10/03/vue2-ts2/

You need to understand what's code splitting is. You also need to understand what vue-loader export in plain js mode.

If your hello.vue is as you pasted, the following snippet should work already.

Vue.component('async-webpack-example', function (resolve) {
  require(['./my-async-component'], resolve)
})
gazugafan commented 7 years ago

Thanks again! Yeah, it was way less complicated than I was trying to make it. The above snippet does indeed work. For anyone else interested, my eventual solution when moving to routes was something like...

import NormalComponent from './components/normalcomponent.vue';
const routes = [
    { path: '/normalcomponent', component: NormalComponent},
    { path: '/AsyncComponent', component: async('./components/asynccomponent') }
];

function async(name) {
    return function(resolve) {
        require([name + '.vue'], m => resolve(m.default));
    }
};

... the important bit being the function in the second parameter of require. It seems that, when using export default to export the class in the .vue file, just using something like require([name + '.vue'], resolve); doesn't fit. It would cause a runtime Vue warning of "Failed to mount component: template or render function not defined."