这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战
Promise异步组件
异步组件的第二种写法是在工厂函数中返回一个promise
对象,我们知道import
是es6
引入模块加载的用法,但是import
是一个静态加载的方法,它会优先模块内的其他语句执行。因此引入了import()
,import()
是一个运行时加载模块的方法,可以用来类比require()
方法,区别在于前者是一个异步方法,后者是同步的,且import()
会返回一个promise
对象。
具体用法:
Vue.component('asyncComponent', () => import('./test.vue'))
源码依然走着异步组件处理分支,并且大部分的处理过程还是工厂函数的逻辑处理,区别在于执行异步函数后会返回一个promise
对象,成功加载则执行resolve
,失败加载则执行reject
.
var res = factory(resolve, reject);
// res是返回的promise
if (isObject(res)) {
if (isPromise(res)) {
if (isUndef(factory.resolved)) {
// 核心处理
res.then(resolve, reject);
}
}
}
其中promise
对象的判断最简单的是判断是否有then
和catch
方法
// 判断promise对象的方法
function isPromise (val) {
return (isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function')
}
高级异步组件
为了在操作上更加灵活,比如使用loading
组件处理组件加载时间过长的等待问题,使用error
组件处理加载组件失败的错误提示等,Vue
在2.3.0+版本新增了返回对象形式的异步组件格式,对象中可以定义需要加载的组件component
,加载中显示的组件loading
,加载失败的组件error
,以及各种延时超时设置,源码同样进入异步组件分支。
Vue.component('asyncComponent', () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
}))
异步组件函数执行后返回一个对象,并且对象的component
执行会返回一个promise
对象,因此进入高级异步组件处理分支。
if (isObject(res)) {
if (isPromise(res)) {}
// 返回对象,且res.component返回一个promise对象,进入分支
// 高级异步组件处理分支
else if (isPromise(res.component)) {
// 和promise异步组件处理方式相同
res.component.then(resolve, reject);
···
}
}
异步组件会等待响应成功失败的结果,与此同时,代码继续同步执行。高级选项设置中如果设置了error
和loading
组件,会同时创建两个子类的构造器,
if (isDef(res.error)) {
// 异步错误时组件的处理,创建错误组件的子类构造器,并赋值给errorComp
factory.errorComp = ensureCtor(res.error, baseCtor);
}
if (isDef(res.loading)) {
// 异步加载时组件的处理,创建错误组件的子类构造器,并赋值给errorComp
factory.loadingComp = ensureCtor(res.loading, baseCtor);
}
如果存在delay
属性,则通过settimeout
设置loading
组件显示的延迟时间。factory.loading
属性用来标注是否是显示loading
组件。
如果在timeout
时间内,异步组件还未执行resolve
的成功结果,即resolve
没有赋值,则进行reject
失败处理。
接下来依然是渲染注释节点或者渲染loading
组件,等待异步处理结果,根据处理结果重新渲染视图节点,相似过程不再阐述。
wepack异步组件用法
webpack
作为Vue
应用构建工具的标配,我们需要知道Vue
如何结合webpack
进行异步组件的代码分离,并且需要关注分离后的文件名,这个名字在webpack
中称为chunkName
。webpack
为异步组件的加载提供了两种写法。
require.ensure
:它是webpack
传统提供给异步组件的写法,在编译时,webpack
会静态地解析代码中的require.ensure()
,同时将模块添加到一个分开的chunk
中,其中函数的第三个参数为分离代码块的名字。修改后的代码写法如下:
Vue.component('asyncComponent', function (resolve, reject) {
require.ensure([], function () {
resolve(require('./test.vue'));
}, 'asyncComponent'); // asyncComponent为chunkname
})
import(/* webpackChunkName: "asyncComponent" */, component)
: 有了es6
,import
的写法是现今官方最推荐的做法,其中通过注释webpackChunkName
来指定分离后组件模块的命名。修改后的写法如下:
Vue.component('asyncComponent', () => import(/* webpackChunkName: "asyncComponent" */, './test.vue'))
至此,我们已经掌握了所有异步组件的写法,并深入了解了其内部的实现细节。我相信全面的掌握异步组件对今后单页面性能优化方面会起到积极的指导作用。