# 组件化

# createComponent

# patch

# 合并配置

# 总结

生命周期函数会被合并为数组。

这种合并思想值得借鉴,本身有一套默认配置,合并传入的配置,达到定制化的目的。

# 生命周期

放一张经典的生命周期图 经典图

# beforeCreate & created

_init src/core/instance/init.js

Vue.prototype._init = function (options?: Object) {
  // ...
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // resolve injections before data/props
  initState(vm)
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created')
  // ...
}

# beforeMount && mounted

mountComponent src/core/instance/lifecycle.js

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  // ...
  callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  }

  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

子组件的mounted回调,在这个方法里。

对于同步渲染的子组件而言,mounted钩子函数的执行顺序也是先子后父。 ,mixin时,会先执行原来的钩子,后执行mixin的钩子。

const componentVNodeHooks = {
  // ...
  insert (vnode: MountedComponentVNode) {
    const { context, componentInstance } = vnode
    if (!componentInstance._isMounted) {
      componentInstance._isMounted = true
      callHook(componentInstance, 'mounted')
    }
    // ...
  },
}

# beforeUpdate & updated

# beforeDestroy & destroyed

destroyed钩子函数执行顺序也是先子后父,和mounted过程一致。

# activated & deactivated

activated 和 deactivated 钩子函数是专门为keep-alive组件定制的钩子。

# 总结

created钩子中能够拿到data、props、methods属性,mounted钩子中能够访问到DOM,destroyed钩子中可以做一些定时器销毁工作。

# 组件注册

# 全局注册

Vue.component('my-component',{

})

# 局部注册

import HelloWorld from './components/HelloWorld';

export default {
  components: {
    HelloWorld,
  }
}

# 总结

通用基础组件一般全局注册,业务组件局部注册。

# 异步组件

# 普通异步组件

Vue.components('async-example', function(resole, reject) {
  // 特殊的 require 语法告诉webpack
  // 自动将编译后的代码分割成不同的块
  // 这些块将通过 Ajax 请求自动下载
  require(['./my-async-component'], resolve)
})

webpack 2+ 支持了异步加载的语法糖:() => import('./my-async-component')

# 此外还支持两种方式,Promise创建组件

Vue.component(
  'async-webpack-example',
  () => import('./my-async-component')
)

# 高级异步组件

const AsyncComp = () => ({
  // 需要加载的组件 promise
  component: import('./async-component.vue'),
  // 加载中应当渲染的组件 同步载入进来的
  loading: LoadingComp,
  // 出错时渲染的组件 
  error: ErrorComp,
  // 渲染加载中组件前的等待时间。 默认: 200ms。
  delay: 200,
  // 最长等待时间。超出此时间则渲染错误组件。默认:Infinity
  timeout: 3000,
})

Vue.component('async-example', AsyncComp)

# 总结

异步组件实现有3种方式,高级异步组件实现了loading、resolve、reject、timeout4种状态。 异步组件的实现本质是2次渲染,除了0 delay的高级异步组件会渲染loading外, 其他第一次都是渲染生成注释节点,当异步组件获取成功后,再通过forceRender强制重新渲染

Last Updated: 2019-09-26 19:30:00