vue异步组件

1,443 阅读3分钟

背景

在Vue脚手架项目中,所有以import方式导入的组件的html+css+js等都会编译到app.js文件中----所以这个文件会很大,但是这个文件必然在首屏加载的时候下载。 这就导致了首屏加载慢!

import  About from '../components/About.vue';
//一旦这样写,后续项目打包结束将会直接打包到app.js文件中const routes = [
   {
     path: '/about',
     component: About      //引入组件     
   }
]

异步组件是什么

异步组件是指在加载时什么都没有做,等到实例化组件时才开始加载渲染组件。

定义异步组件时,由原来的选项参数替换为一个函数或返回promise的函数,例如按需加载组件就是它的一个应用,按需加载采用import方式加载组件,实际上就是一个Promise。结合wabpack使用的话,组件会打包成单独的js文件存储在static/js文件夹里面,在调用时使用ajax请求回来插入到html中。

使用方式

普通函数异步组件

Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。可以在工厂函数内执行异步操作,等待异步操作执行完毕后调用resolve()。

Vue.component('async-example', function (resolve, reject) {
    setTimeout(function () {
        // 向 `resolve` 回调传递组件定义
        resolve({
            template: '<div>I am async!</div>'
        })
    }, 1000)
})

promise异步组件

原来构建组件的选项对象替换成了函数,并且返回一个promise。

Vue.component('bb', function(){
    return Promise.resolve({
        template: '<div>I am bb</div>'
    })
})

如果是wabpack构建的项目,还可以采用import写法。相信这种写法很常见,这就是我们使用的组件懒加载。而import实际上也是返回了一个promise。

Vue.component('aa', () => import('./aa.js'));

高级异步组件

采用这种写法可以定义加载中应当渲染的组件,出错时的组件等。

const cc = () => ({
    // 需要加载的组件。应当是一个 Promise
    component: new Promise(resolve => {
        setTimeout(() => {
            resolve({
                template: '<div>I am cc</div>'
            })
        }, 1500);
    }),
    // 加载中应当渲染的组件
    loading: loadingComponent,
    // 出错时渲染的组件
    error: ErrorComp,
    // 渲染加载中组件前的等待时间。默认:200ms。
    delay: 200,
    // 最长等待时间。超出此时间则渲染错误组件。默认:Infinity
    timeout: 3000
})
Vue.component('cc', cc)

如果是wabpack项目里还可以采用以下写法。

const util = {
    lazyLoadView: function (asyncView, loadingComponent) {
        const AsyncHandler = () => ({
            component: asyncView,
            loading: loadingComponent,
            delay: 1000,
            timeout: 10000,
        })
        return AsyncHandler
    }
}
export default util;
import util from './base/util.js'
import loading from './components/common/loading.vue'export default {
  name: 'App',
  components: {
    'user': util.lazyLoadView(import('./components/home/user.vue'), loading),
  },
}

但是上述写法应用于路由组件切换时,可能会导致路由钩子函数无法生效,所以可以考虑使用函数组件的写法来改写组件。

const util = {
    lazyLoadView: function (asyncView, loadingComponent) {
        const AsyncHandler = () => ({
            component: asyncView,
            loading: loadingComponent,
            delay: 1000,
            timeout: 10000,
        })
        return Promise.resolve({
            functional: true,
            render(h, { data, children }) {
                return h(AsyncHandler, data, children);
            }
        });
    }
}
​
export default util;
import util from './base/util.js'
import loading from './components/common/loading.vue'export default {
  name: 'App',
  components: {
    'user': () => util.lazyLoadView(import('./components/home/user.vue'), loading),
  },
}

至此,对于高级异步组件还有一点疑问。指定加载中应当渲染的组件loading有啥用,加载一个组件的代码应该是非常快的,只是在这期间显示loading吗,通常组件里面的数据都是网络请求回来的,我们需要的是在数据回来之前都显示loading,而不只是加载组件代码期间。