Vue 源码初探(七)生命周期和Mixin实现

330 阅读2分钟

思维导图

Vue源码初探.png

抛出问题

  1. 如何给所有组件都添加生命周期函数?
  2. 生命周期是怎样实现的?钩子方法(回调函数)?

寻找思路

添加Vue.mixin方法

  • global/index.js
import { mergeOptions } from "../utils"

export function initGlobalAPI(Vue) {
  Vue.options = {}  //全局属性, 在每个组件初始化的时候将这些属性放到每个组件上

  Vue.mixin = function (options) {
    // 实例上的参数和 方法上的参数
    this.options = mergeOptions(this.options, options)
    return this
  }

  Vue.component = function () { }

  Vue.filter = function () { }

  Vue.directive = function () { }
}

缓存钩子函数

utils.js

//存放所有策略
const strategies = {}

let lifeCycle = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted'
]

lifeCycle.forEach(hook => {
  strategies[hook] = function (parentVal, childVal) {
    if (childVal) {
      if (parentVal) {
        return parentVal.concat(childVal)
      } else {
        if (isArray(childVal)) {
          return childVal
        } else {
          return [childVal]
        }
      }
    } else {
      return parentVal
    }
  }
})

// 选项的合并
export function mergeOptions(parentVal, childVal) {
  // 合并两个对象,以子对象为准
  let options = {}
  for (let key in parentVal) {
    mergeFiled(key)
  }
  for (let key in childVal) {
    //合并父对象上不存在的属性
    if (!parentVal.hasOwnProperty(key)) {
      mergeFiled(key)
    }
  }

  function mergeFiled(key) {
    let strategy = strategies[key]
    //有策略
    if (strategy) {
      options[key] = strategy(parentVal[key], childVal[key])
    } else {
      // 先取子对象上的值
      options[key] = childVal[key] || parentVal[key]
    }
  }
  return options
}

调用钩子函数

lifecycle.js

import Watcher from "./observe/watcher"
import { patch } from "./vnode/patch"

export function mountComponent(vm) {


  // vm._render()  获取到vnode 

  // vm._update(vnode) 把vnode 渲染成真实节点,更新到页面上

  let updateComponent = () => {
    vm._update(vm._render())
  }
  callHook(vm, 'beforeCreate')
  new Watcher(vm, updateComponent, () => {
    console.log('后续增添组件更新的钩子函数')
  }, true)
  callHook(vm, 'mounted')
}


export function lifeCycleMixin(Vue) {

  //给个对象,把对象渲染成真实DOM
  Vue.prototype._update = function (vnode) {
    //采用的是先序深度遍历,创建节点。
    const vm = this
    vm.$el = patch(vm.$el, vnode)
  }
}

export function callHook(vm, hook) {
  // 从$options里面拿对应的钩子函数进行调用
  let handlers = vm.$options[hook]
  handlers && handlers.forEach(fn => {
    //声明周期中的this 永远指向实例
    fn.call(vm)
  })
}

小结回顾

  1. 用一个哈希表收集所有的生命周期的钩子函数,在适当的时机去执行他。
  2. Vue.mixin函数调用之后会把mixin里面的参数合并到Vue.options上面,然后再调用vm.$options = mergeOptions(vm.constructor.options, options)把全局的参数与用户传递的参数进行合并之后生成一个新的$options给当前的实例
Vue.mixin 全局api的作用
Vue.options = {}  //全局属性, 在每个组件初始化的时候将这些属性放到每个组件上
this.options = mergeOptions(this.options, options)

然后再实例化的时候再调用
vm.$options = mergeOptions(vm.constructor.options, options)