createComponent

patch

合并配置

总结

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

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

生命周期

放一张经典的生命周期图
组件化 - 图1

beforeCreate & created

_init
src/core/instance/init.js

  1. Vue.prototype._init = function (options?: Object) {
  2. // ...
  3. initLifecycle(vm)
  4. initEvents(vm)
  5. initRender(vm)
  6. callHook(vm, 'beforeCreate')
  7. initInjections(vm) // resolve injections before data/props
  8. initState(vm)
  9. initProvide(vm) // resolve provide after data/props
  10. callHook(vm, 'created')
  11. // ...
  12. }

beforeMount && mounted

mountComponent
src/core/instance/lifecycle.js

  1. export function mountComponent (
  2. vm: Component,
  3. el: ?Element,
  4. hydrating?: boolean
  5. ): Component {
  6. vm.$el = el
  7. // ...
  8. callHook(vm, 'beforeMount')
  9. let updateComponent
  10. /* istanbul ignore if */
  11. if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
  12. updateComponent = () => {
  13. const name = vm._name
  14. const id = vm._uid
  15. const startTag = `vue-perf-start:${id}`
  16. const endTag = `vue-perf-end:${id}`
  17. mark(startTag)
  18. const vnode = vm._render()
  19. mark(endTag)
  20. measure(`vue ${name} render`, startTag, endTag)
  21. mark(startTag)
  22. vm._update(vnode, hydrating)
  23. mark(endTag)
  24. measure(`vue ${name} patch`, startTag, endTag)
  25. }
  26. } else {
  27. updateComponent = () => {
  28. vm._update(vm._render(), hydrating)
  29. }
  30. }
  31. // we set this to vm._watcher inside the watcher's constructor
  32. // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  33. // component's mounted hook), which relies on vm._watcher being already defined
  34. new Watcher(vm, updateComponent, noop, {
  35. before () {
  36. if (vm._isMounted) {
  37. callHook(vm, 'beforeUpdate')
  38. }
  39. }
  40. }, true /* isRenderWatcher */)
  41. hydrating = false
  42. // manually mounted instance, call mounted on self
  43. // mounted is called for render-created child components in its inserted hook
  44. if (vm.$vnode == null) {
  45. vm._isMounted = true
  46. callHook(vm, 'mounted')
  47. }
  48. return vm
  49. }

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

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

  1. const componentVNodeHooks = {
  2. // ...
  3. insert (vnode: MountedComponentVNode) {
  4. const { context, componentInstance } = vnode
  5. if (!componentInstance._isMounted) {
  6. componentInstance._isMounted = true
  7. callHook(componentInstance, 'mounted')
  8. }
  9. // ...
  10. },
  11. }

beforeUpdate & updated

beforeDestroy & destroyed

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

activated & deactivated

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

总结

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

组件注册

全局注册

  1. Vue.component('my-component',{
  2. })

局部注册

  1. import HelloWorld from './components/HelloWorld';
  2. export default {
  3. components: {
  4. HelloWorld,
  5. }
  6. }

总结

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

异步组件

普通异步组件

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

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

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

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

高级异步组件

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

总结

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