Vue单页面项目中的组件和路由懒加载
一个略有规模的前端项目,或者页面内容比较多,但不需要组件同时显示的这类应用,都可以使用 懒加载 的方式将不用立刻显示的路由或者组件异步加载,这样做的目的是提高用户首次加载页面的速度,增强用户体验。
路由懒加载
由于 Vue 中的 Router 也是通过组件来控制的,所以路由懒加载本身还是异步组件问题,所以全部按照异步组件处理即可。
在 Vue 中,官方提供的异步组件需要返回 Promise ,按照这个要求我们在使用时可以手动返回 Promise 对象,或者通过 Webpack 支持的 ES6 中 import() 来返回 Promise 。
异步组件
Vue 中对异步组件的介绍:
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。例如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
可以看到异步组件是用过组件工厂函数来异步处理的,使用场景有两种,如果是基于 Webpack 构建的 Vue 项目,那么异步组件可以被分割成代码块分别输出,然后在需要的时候再通过异步加载的方式载入。
例如:
export default {
components: {
HelloWorld: resolve => (require(["@/components/HelloWorld.vue"], resolve))
}
}
或者写一个定时器来模拟,HelloWorld 组件将在 3 秒加载。
export default {
name: 'Home',
components: {
HelloWorld: resolve => (
setTimeout(() => {
require(["@/components/HelloWorld.vue"], resolve)
}, 3000)
)
}
}
以这种方式打包后,页面初次启动时将不会显示 HelloWorld 组件,大约 3 秒后,浏览器想服务器发起一个请求,获取 HelloWorld 组件的定义,之后在页面中渲染并显示出来。
使用 import()
使用 import() 将使得上述方法变得简单,Webpack 完全可以处理 import() ,并将其打包为不同的 chunk ,使用如下:
export default {
name: 'Home',
components: {
HelloWorld: () => import( "@/components/HelloWorld.vue")
}
}
在 Router 中可以直接 import 需要异步加载的组件:
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
]
由于异步组件和懒加载的路由都需要返回一个 Promise ,所以使用 require 返回 Promise 也是可以的:
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: resolve => (require(["../views/About.vue"], resolve))
}
]
引申:异步组件的切换
某些情况下,我们需要在页面中按需切换一个组件,不过这个组件可能是异步组件,应该怎么实现?
以用户的单击事件为例,借助 is 属性绑定组件名称的特征,可以这样做:
export default {
components: {
Hardware: () => import( "@/components/Hardware.vue"),
Software: () => import( "@/components/Software.vue")
},
methods: {
onClick(name, propData) {
this.propData = propData;
this.componentName = name;
}
}
}
组件模板:
<template slot-scope="scope">
<component
:is="componentName"
:prop-data="propData"
></component>
<button @click="onClick('Hardware', 'somedata')"> Hardware </button>
<button @click="onClick('Software', 'somedata')"> Software </button>
</template>
以上,通过点击 button 就可以切换组件,而组件会异步加载。
引申:异步组件通讯
上面异步组件切换其实也包含了部分组件通讯,异步组件可以像普通组件那样通 props 属性像其传值,也可以通过监听子组件的事件来实现子组件像父组件传值。
<template slot-scope="scope">
<component
:is="componentName"
:prop-data="propData"
@onEvent="onComponentEvent"
></component>
<button @click="onClick('Hardware', 'somedata')"> Hardware </button>
<button @click="onClick('Software', 'somedata')"> Software </button>
</template>
添加事件监听:
export default {
...
methods: {
onComponentEvent(...params) {
// 处理异步组件传递过来的数据
}
}
}