Skip to content

Latest commit

 

History

History
130 lines (111 loc) · 3.99 KB

异步组件.md

File metadata and controls

130 lines (111 loc) · 3.99 KB

通常为了提升性能,例如首屏渲染速度,我们会异步加载组件,Vue异步加载组件的方式有以下三种:

  • 工厂函数
  • Promise
  • 高级异步组件

工厂函数

Vue.component('async-component', function(resolve, reject) => {
  require(['./async-component.vue'], resolve)
})

上述代码,加载了一个文件名为 async-component.vue的单文件组件,将之命名为 asynv-component,并注册为全局组件。 利用上述写法,vue-loader会将 async-component.vue打包成一个独立文件,实现异步加载。

虽然是异步组件,但除了加载顺序比同步组件稍慢外,其他的逻辑和同步组件都是一样的,同样会借助 vue源码中的 src/core/vdom/create-component/js文件中的 createComponent方法进行组件的初始化和渲染,由于是异步加载,所以组件在一开始时是不会同步加载的,而是会暂时同步渲染出一个注释节点作为占位节点,源代码中的逻辑如下:

// src/core/vdom/create-component.js
// async component
let asyncFactory
if (isUndef(Ctor.cid)) {
  asyncFactory = Ctor
  Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
  if (Ctor === undefined) {
    // return a placeholder node for async component, which is rendered
    // as a comment node but preserves all the raw information for the node.
    // the information will be used for async server-rendering and hydration.
    return createAsyncPlaceholder(
      asyncFactory,
      data,
      context,
      children,
      tag
    )
  }
}

Promise

// 通过 import 返回一个 promise对象
Vue.component('async-component', () => import('./async-component.vue'))

加载这种写法的异步组件,在源码中的逻辑位于 src/core/vdom/helpers/resolve-async-component.js

if (isObject(res)) {
  if (typeof res.then === 'function') {
    // () => Promise
    if (isUndef(factory.resolved)) {
      // 加载异步组件
      res.then(resolve, reject)
    }
  }
  // ...

这里其实是利用了 webpackimport语法糖能够直接返回一个 promise的优势,从而实现异步加载,但实际上这种方法除了加载方式与工厂函数不同之外,剩下的逻辑都是一样的。

高级异步组件

// index.vue
import Vue from 'vue'
import App from './App.vue'

const LoadingComp = {
  template: '<div>loading</div>'
}

const ErrorComp = {
  template: '<div>error</div>'
}

const AsyncComp = () => ({
  // 真正需要加载的组件,应当是一个 Promise
  component: import('./components/HelloWorld.vue'),
  // 在加载完成之前进行占位渲染的组件
  loading: LoadingComp,
  // 出错时渲染的组件
  error: ErrorComp,
  // 渲染 loading组件前的等待时间(如果超出这个时间真正需要加载的组件还没来得及渲染好,则渲染laoding组件),默认 200
  delay: 200,
  // 最长等待时间,如果超过此时间,真正需要加载的组件还没渲染好,则渲染 error组件
  timeout: 1000
})

Vue.component('helloWOrld', AsyncComp)
// ...

加载这种写法的异步组件,在源码中的逻辑位于 src/core/vdom/helpers/resolve-async-component.js

else if (isDef(res.component) && typeof res.component.then === 'function') {
  res.component.then(resolve, reject)

  if (isDef(res.error)) {
    factory.errorComp = ensureCtor(res.error, baseCtor)
  }

  if (isDef(res.loading)) {
    factory.loadingComp = ensureCtor(res.loading, baseCtor)
    if (res.delay === 0) {
      factory.loading = true
    } else {
      setTimeout(() => {
        if (isUndef(factory.resolved) && isUndef(factory.error)) {
          factory.loading = true
          forceRender()
        }
      }, res.delay || 200)
    }
  }

  if (isDef(res.timeout)) {
    setTimeout(() => {
      if (isUndef(factory.resolved)) {
        reject(
          process.env.NODE_ENV !== 'production'
            ? `timeout (${res.timeout}ms)`
            : null
        )
      }
    }, res.timeout)
  }
}