5.生命周期调用

74 阅读2分钟

前言

vue2的生命周期采用Vue.Mixin()去全局混入,然后在使用订阅发布的模式去调用生命周期

以数组的形式去订阅 格式:{created:[a(),b(),c()],....}

src目录改动新增代码目录:


- index.js
- global-api // 混入
    - index.js  

- utils  
    - index.js  // 实现订阅生命周期

- lifeCycle.js  // 生命周期的调用
- init.js // 订阅生命周期 及调用

Vue全局混入代码global-api/index.js

import { mergeOptions } from "../utils/index"
export function initGlobApi(Vue) { 
  Vue.options = {}
  Vue.Mixin = function (mixin) { 
    // 对象的合并
    this.options = mergeOptions(this.options, mixin)
    console.log(this.options);
    // console.log(Vue);
  }
}

以数组的形式去订阅生命周期 utils/index.js

生成的格式 {created:[a(),b(),c()],....}

export const HOOKS = [
  "beforeCreate",
  "created",
  "beforeMount",
  "mounted",
  "beforeUpdate",
  "updated",
  "beforeDestroy",
  "destroyed",
]

// 策略模式
let starts = {}
starts.data = function (parenVal, childVal) {
 return childVal
 }
starts.computed = function () { }
starts.watch = function () { }
starts.methods = function () { }

HOOKS.forEach(hooks => { 
  starts[hooks] = mergeHook
})
function mergeHook(parentVal,childVal) { 
  if (childVal) {
    if (parentVal) {
      return parentVal.concat(childVal)
    } else {
      return [childVal]
    }
  } else {
    return [parentVal]
  }
}

// 订阅生命周期
export function mergeOptions(parent, child) { 
  const options = {}
  for (let key in parent) { 
    mergeField(key)
  }
  for (let key in child) { 
    mergeField(key)
  }
  function mergeField(key) { 
    // 根据key 策略模式
    if (starts[key]) {
      options[key] = starts[key](parent[key], child[key])
    } else { 
      options[key] = child[key]
    }
  }
  return options
}

根目录index.js

import { initMixin } from "./init"
import { lifeCycleMixin } from "./lifeCycle";
import { renderMixin } from "./vnode/index";
import { initGlobApi } from "./global-api/index";
/**
 * @author xwya
 * @since 2023-12-11
 * @description  Vue 构造函数
 * @param {Object} options - Vue 的初始化选项。
 * @returns {void} - 没有返回值。
 */
function Vue(options) {
  // 初始化
  this._init(options);
}
initMixin(Vue)
lifeCycleMixin(Vue) // 添加生命周期
renderMixin(Vue) // 添加_render
// 调用向实例上添加混入 
initGlobApi(Vue)
export default Vue;

init.js文件

import { initState } from "./initState";
import { compileToFunction } from "./compile/index";
import { mounetComponent,callHook } from "./lifeCycle";
import { mergeOptions } from "./utils/index";
/**
 * @description 初始化vue
 * @param {Object} Vue
 * @returns {void}
 */
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    let vm = this
    vm.$options = mergeOptions(Vue.options, options) // 订阅生命周期
    callHook(vm, 'beforeCreate')
    // 初始化状态
    initState(vm)
    callHook(vm, 'created')
    // 渲染模板
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
  // 创建$mount 进行模板编译
  Vue.prototype.$mount = function (el) {
    let vm = this
     // 获取dom元素
    el = document.querySelector(el)
    vm.$el= el
    let options = vm.$options
    // 没有render函数
    if (!options.render) { 
      let template = options.template
      if (!template && el) { 
        // 获取html
        el = el.outerHTML
        // 转换成ast语法树  vnode(虚拟dom)   [ast语法树是能操作js和css 虚拟dom只能操作一个节点]
        let render = compileToFunction(el)
        // render函数 
        options.render = render
      }
    }
    // 执行生成模板
    mounetComponent(vm, el)

  }
}

lifeCycle.js 文件

import { patch } from "./vnode/patch";
export function mounetComponent(vm, el) {
  // 页面加载前生命周期
  callHook(vm, 'beforeMounted')
  // (1) vm._render 将render转换成虚拟dom (vnode)
  // (2) vm._update 将vnode变成真实dom在放到页面上
  vm._updata(vm._render())
  callHook(vm, 'mounted')
}


export function lifeCycleMixin(Vue) { 
  Vue.prototype._updata = function (vnode) { 
    let vm = this
    // 传两个参数 旧的dom 和 vnode
   vm.$el= patch(vm.$el, vnode)
  }
}




// 调用生命周期方法
export function callHook(vm, hook) { 
  const handlers = vm.$options[hook]
  if (handlers) { 
    handlers.forEach(handler => {
      handler.call(this)
    })
  }

}