首页>>前端>>Vue->Vue2源码解析☞ 2 ☞ 初始化

Vue2源码解析☞ 2 ☞ 初始化

时间:2023-11-30 本站 点击:0

活着,最有意义的事情,就是不遗余力地提升自己的认知,拓展自己的认知边界。

在搭建源码调试环境一节中,我们已经找到了Vue的构造函数,接下来开始探索Vue初始化的流程。

一个小测试

在精读源码之前,我们可以在一些重要的方法内打印一下日志,熟悉一下这些关键节点的执行顺序。(执行npm run dev后,源码变更后会自动生成新的Vue.js,我们的测试html只需要刷新即可)

在初始化之前,Vue类的构建过程?

在此过程中,大部分都是原型方法和属性,意味着实例vm可以直接调用

注意事项:

1、以$为前缀的属性和方法,在调用_init原型方法的那一刻即可使用

2、以_为前缀的原型方法和属性,谨慎使用

3、本章旨在了解Vue为我们提供了哪些工具(用到时,深入研究,不必要在开始时花过多精力,后边遇到时会详细说明)

4、类方法和属性在new Vue()前后都可以使用,原型方法和属性只能在new Vue()后使用

定义构造函数

// src/core/instance/index.jsfunction Vue (options) {  //形式上很简单,就是一个_init方法  this._init(options)}

挂载原型方法:_init

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }

挂载与state相关的原型属性和原型方法

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }

挂载与事件相关的原型方法

// src/core/instance/events.js  const hookRE = /^hook:/  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {}  Vue.prototype.$once = function (event: string, fn: Function): Component {}  Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {}  Vue.prototype.$emit = function (event: string): Component {}

挂载与生命周期相关的原型方法

// src/core/instance/lifecycle.js  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}  Vue.prototype.$forceUpdate = function () {}  Vue.prototype.$destroy = function () {}

挂载与渲染相关的原型方法

  // install runtime convenience helpers  installRenderHelpers(Vue.prototype)  Vue.prototype.$nextTick = function (fn: Function) {}  Vue.prototype._render = function (): VNode {}

挂载Vue类方法和类属性

// src/core/global-api/index.js// config  const configDef = {}  configDef.get = () => config  Object.defineProperty(Vue, 'config', configDef)  Vue.util = {    warn,    extend,    mergeOptions,    defineReactive  }  Vue.set = set  Vue.delete = del  Vue.nextTick = nextTick  // 2.6 explicit observable API  Vue.observable = <T>(obj: T): T => {    observe(obj)    return obj  }  Vue.options = Object.create(null)  ASSET_TYPES.forEach(type => {    Vue.options[type + 's'] = Object.create(null)  })  Vue.options._base = Vue  extend(Vue.options.components, builtInComponents)  initUse(Vue)  //挂载类方法use,用于安装插件(特别特别重要)  initMixin(Vue) //挂载类方法mixin,用于全局混入(在Vue3中被新特性取代)  initExtend(Vue) //实现Vue.extend函数  initAssetRegisters(Vue)//实现Vue.component, Vue.directive, Vue.filter函数

挂载平台相关的属性,挂载原型方法$mount

// src/platforms/web/runtime/index.js// install platform specific utilsVue.config.mustUseProp = mustUsePropVue.config.isReservedTag = isReservedTagVue.config.isReservedAttr = isReservedAttrVue.config.getTagNamespace = getTagNamespaceVue.config.isUnknownElement = isUnknownElement// install platform runtime directives & componentsextend(Vue.options.directives, platformDirectives)extend(Vue.options.components, platformComponents)// install platform patch functionVue.prototype.__patch__ = inBrowser ? patch : noopconsole.log('挂载$mount方法')// public mount methodVue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {}

拓展$mount方法

// src/platforms/web/entry-runtime-with-compiler.jsconst mount = Vue.prototype.$mount //保存之前定义的$mount方法Vue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  //执行拓展内容  return mount.call(this, el, hydrating) //执行最初定义的$mount方法}

Vue的初始化过程(很重要哦!!!)

熟悉了初始化过程,就会对不同阶段挂载的实例属性了然于胸,了解Vue是如何处理options中的数据,将初始化流程抽象成一个模型,从此,当你看到用户编写的options选项,都可以在这个模型中演练。

前边我们提到过,Vue的构造函数中只调用了一个_init方法

执行_init方法

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {    const vm: Component = this //此刻,Vue的实例已经创建,只是雏形,但Vue的所有原型方法可以调用    // a flag to avoid this being observed //(observe会在后面的响应式章节详细说明)    vm._isVue = true    // merge options    if (options && options._isComponent) {// 在后面的Vue组件章节会详细说明      // 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(//合并options        resolveConstructorOptions(vm.constructor),//主要处理包含继承关系的实例()        options || {},        vm      )    }    // expose real self    vm._self = vm    initLifecycle(vm) //初始化实例中与生命周期相关的属性    initEvents(vm) //处理父组件传递的事件和回调    initRender(vm) //初始化与渲染相关的实例属性    callHook(vm, 'beforeCreate') //调用beforeCreate钩子,即执行beforeCreate中的代码(用户编写)    initInjections(vm) // resolve injections before data/props 获取注入数据    initState(vm) //初始化props、methods、data、computed、watch    initProvide(vm) // resolve provide after data/props 提供数据注入    callHook(vm, 'created') //执行钩子created中的代码(用户编写)    if (vm.$options.el) {//DOM容器(通常是指定id的div)      vm.$mount(vm.$options.el) //将虚拟DOM转换成真实DOM,然后插入到DOM容器内    }  }

initLifecycle:初始化与生命周期相关的实例属性

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }0

initEvents(vm):处理父组件传递的事件和回调

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }1

initRender(vm):初始化与渲染相关的实例属性

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }2

CallHook(vm, 'beforeCreate'):执行beforeCreate钩子

执行options中,用户编写在beforeCreate中的代码

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }3

initInjections(vm): resolve injections before data/props 获取注入数据

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }4

initState(vm):初始化props、methods、data、computed、watch(划重点啦!!!)

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }5

initProps: 初始化props

此处概念比较多,propsData、 props、vm._props、 propsOptions,后续会结合实例来分析其区别,此处只做大概了解。

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }6

initMethods:初始化methods

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }7

initData: 初始化data

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }8

initComputed:初始化computed 选项

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {  }9

initWatch:初始化watch

createWatcher:本质上执行了vm.$watch(expOrFn, handler, options)

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }0

initProvide(vm): 提供数据注入

为什么provide初始化滞后与inject,后续补充

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }1

CallHook(vm, 'created'): 执行created钩子中的代码

callHook的相关逻辑,参考上面的callHook(vm, 'beforeCreate')

执行挂载

执行$mount扩展

通过下面的代码可知:当用户代码中同时包含render,template,el时,它们的优先级依次为:render、template、el

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }2

$mount方法中,首先获取挂载容器,然后执行mountComponent方法

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }3

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }4

在_update方法中,通过_vnode属性判断是否初次渲染,patch 其实就是patch方法,关于patch的详细逻辑,将在diff算法章节详细说明。

// src/core/instance/state.js  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    //略  }5
原文:https://juejin.cn/post/7100124377567461383


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3794.html