Vue组件高级用法之异步组件(下)

952 阅读4分钟

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

Promise异步组件

异步组件的第二种写法是在工厂函数中返回一个promise对象,我们知道importes6引入模块加载的用法,但是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对象的判断最简单的是判断是否有thencatch方法

 // 判断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);
    ···
  }
}

异步组件会等待响应成功失败的结果,与此同时,代码继续同步执行。高级选项设置中如果设置了errorloading组件,会同时创建两个子类的构造器,

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中称为chunkNamewebpack为异步组件的加载提供了两种写法。

  • 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'))

至此,我们已经掌握了所有异步组件的写法,并深入了解了其内部的实现细节。我相信全面的掌握异步组件对今后单页面性能优化方面会起到积极的指导作用。