vue框架需要掌握的原理--初始化原理

195 阅读3分钟

大家好,我是鼠目。「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

通过本章,你可以学到一点点esm和CommonJs的相关知识,以及vue初始化相关的知识。

本章来给大家简单介绍,vue框架中应该掌握的原理---vue的初始化做了哪些操作,同时这也是面试中常会问到的问题之一。

vue的初始化流程

   new Vue({
     el:"#app",
     ....
   })

new Vue究竟做了些什么? 相信js本身有所了解就可以看出来,这其实是一个new构造函数的行为。那它在new的过程中究竟做了些什么呢?

先看源码src/core/index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

// 1.初始化全局静态API:Vue.set/delete/component/use/....
initGlobalAPI(Vue)

Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

export default Vue

我们先对源码进行分析,然后判断它的执行顺序。
首先它是import导入的,es6中的import是编译时加载或者叫静态加载。在import的时候,并没有执行对应的代码块,而是做了一个指针指向。而CommonJs不同,它是会直接执行对应的代码块,得到一份代码的拷贝导出。 所以,当我们new Vue的时候,它会根据指针指向。执行core/index.js中的代码。

  1. 所有import是指针指向并未执行。
  2. 遇到initGlobalAPI(Vue)开始执行,然后去寻找initGlobalApi函数对应的文件和Vue函数对应的文件进行执行。
具体行为
  1. 遇到initGlobalAPI(Vue),开始执行全局Api的初始化
   Vue.set = set
   Vue.delete = del
   Vue.nextTick = nextTick
   initUse(Vue) // vue.use 用于插件挂载
   initMixin(Vue) // vue.mixin 用于mixin混入
   initExtend(Vue) // vue.extend 
   initAssetRegisters(Vue) // 注册实现vue.component/directive/filter
  1. Vue构造函数,定义实例的Api 位置:/instance/index
   import { initMixin } from './init'
   import { stateMixin } from './state'
   import { renderMixin } from './render'
   import { eventsMixin } from './events'
   import { lifecycleMixin } from './lifecycle'
   import { warn } from '../util/index'
   function Vue(options) {
      // 构造函数本身只是执行了_init函数
      this._init(options);
   }
   initMixin(Vue) //实现init函数
   stateMixin(Vue)  //状态相关api $data $props, $set, $delete, $watch
   eventsMixin(Vue) //事件相关api $on, $once, $off, $emit
   lifecycleMixin(Vue) //生命周期相关api, $forceUpdate, $destroy _update,
   renderMixin(Vue) //渲染相关api render, $nextTick
  1. 而在initMixin(Vue)中的init,进行了vue的数据初始化以及挂载的操作。
   export function initMixin(Vue) {
      //实现了_init函数
      Vue.prototype._init = function(options) {
        // 保存上下文关系
        const vm = this;
        // merge options
        // 1.选项合并:用户选项和系统默认选项需要合并
        if (options && options._isComponent) {
          // optimize internal component instantiation
          // since dynamic options merging is pretty slow, and none of the
          // internal component options needs special treatment.
          initInternalComponent(vm, options)
        } else {
          vm.$options = mergeOptions(
            resolveConstructorOptions(vm.constructor),
            options || {},
            vm
          )
        }
        //省略其他
        //执行初始化
      }
      initLifecycle(vm) // 生命周期相关的属性初始化$parent,$root,$children,$refs等
      initEvents(vm) // 自定义组件事件监听,处理父组件传递的事件和回调
      initRender(vm) // 插槽处理,$slots,$scopedSlots,_c,$createElement === render(h)
      // 调用生命周期的钩子函数,beforeCreate
      callHook(vm, 'beforeCreate')
      // 组件数据和状态初始化
      initInjections(vm) // resolve injections before data/props   inject
      initState(vm) // 按照顺序初始化props, methods,data,computed,watcher
      initProvide(vm) // resolve provide after data/props  provide
      callHook(vm, 'created')
      // 省略
      // 如果存在el,将会自动挂载
      if (vm.$options.el) {
        vm.$mount(vm.$options.el)
      }
   }
  1. 第一步合并选项,将传入的options跟默认的参数进行合并
  2. 初始化数据和生命周期
  3. 挂载。

总结一下,初始化过程其实就是创建了一个Vue构造函数的实例对象,实例对象上初始化了很多api和数据。然后将该实例对象进行了挂载。

除了了解vue初始化做了哪些工作,其实我们还应该在vue的源码中学到一些编程的技巧

  1. 将构造函数作为参数传入,在函数内部处理时,挂载原型链方法。
  2. 对一些函数的功能尽量详细地拆分,做到函数功能的单一,方便后期维护。