同步操作将从 MiyueFE/blog 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
本文内容基于 Vue 2.6.12,主要解析需要编译的运行时部分。
最外层打包入口(主文件入口)位于
vue-dev/src/platforms/web/entry-runtime-with-compiler.js
Vue
构造函数初始化function Vue(options)
定义构造函数,并检查是否使用 new
关键字调用。
initMixin(Vue)
为 Vue.prototype
原型上添加 _init(options)
的方法,以供组件实例化的时候调用。
stateMixin(Vue)
重新定义原型下的 $data
与 $props
的 get(), set(newValue)
的方法。为 Vue.prototype
添加 $set(target, key, val)
、$delete(target, key)
与 $watch(expOrFn, cb, options)
方法。
eventsMixin(vue)
为 Vue.prototype
原型上添加 $on(event, fn)
、$once(event, fn)
、$off(event, fn)
与 $emit(event, fn)
方法。
在使用 $on
注册事件的时候,会将 .vue
文件中对应的生命周期的方法 push
到当前组件 vm._events[event]
中(event
是对应的事件的名称标识),如果 .vue
中确实定义了某个生命周期对应的处理函数,则还会在当前组件下定义一个标识符 vm._hasHookEvent = true
$off
在传入参数为空时,会将 vm.events
置为 null
,清除当前组件的所有定义的事件。
$emit
在注册事件的时候,会判断非生产环境下提示将非小写事件名转换成*小写事件名与短横线连接(kebab-case
)*的格式,因为 html
对大小写不敏感。
$on(), $off()
传入参数event
可以是数组,内部会遍历数组重新调用$on(), $off()
,走非数组(即只有一个事件定义)处理过程。
lifecycleMixin(Vue)
为 Vue.prototype
原型上添加 _update(vnode, hydrating)
、$forceUpdate()
与 $destroy()
方法。
_update
方法会获取组件的挂载元素,以及组件原来的虚拟节点 vm._vnode
(根据组件是否存在 _vnode
来判断是否首次渲染),调用 __patch__
方法将对比虚拟节点的修改,然后后更新为对应的真实 dom
节点,并更新到组件的 $el
属性上;之后将新产生的虚拟节点更新到组件上;如果组件的父组件是一个高阶组件,则还会继续更新父元素的 $el
属性,将值设置为当前组件的 $el
。
$forceUpdate
则是判断组件下是否存在观察者 watcher
,调用 watcher
的 update()
方法来触发更新。
$destory
首先会判断组件的销毁状态对应的标志位 _isBeingDestroyed
,为真时直接返回不再继续执行后续代码;为假时则继续执行,触发 beforeDestroy
生命周期钩子函数,并将标志位置为 true
,表示已经执行过销毁方法。之后在父组件的 children
中移除该组件(父组件未被销毁且不是抽象组件时执行),接着移除所有观察者 watchers
与相关的依赖;然后设置组件销毁结束状态 vm._isDestroyed = true
,执行 vm.__patch__(vm._vnode, null)
来销毁对应的真实节点,触发 destroyed
生命周期钩子函数;最后移除所有事件,修改组件的相关属性为 null
。
renderMixin(Vue)
调用 installRenderHelpers(Vue.prototype)
为 Vue.prototype
原型上添加渲染相关方法:
Vue.prototype._o
: markOnce
-- v-once
解析助手,会将当前节点标记为静态节点Vue.prototype._n
: toNumber
-- 将输入值转换为数字。如果转换失败,则返回原始字符串。Vue.prototype._s
: toString
-- 将值转换为实际显示的字符串Vue.prototype._l
: renderList
-- v-for
解析助手Vue.prototype._t
: renderSlot
-- <slot>
标签解析助手Vue.prototype._q
: looseEqual
-- 检测两个值是否相同Vue.prototype._i
: looseIndexOf
-- 返回数组中检查到的第一个与 val 相同的值的下标Vue.prototype._m
: renderStatic
-- 渲染静态 dom
树的辅助程序Vue.prototype._f
: resolveFilter
-- 过滤器解析程序Vue.prototype._k
: checkKeyCodes
-- config.keyCode
检测Vue.prototype._b
: bindObjectProps
-- v-bind
合并到 VNode
的辅助程序Vue.prototype._v
: createTextVNode
-- 创建普通的文本虚拟节点Vue.prototype._e
: createEmptyVNode
-- 创建空虚拟节点Vue.prototype._u
: resolveScopedSlots
-- 反向代理 slot
插槽Vue.prototype._g
: bindObjectListeners
-- 处理 v-on
绑定的监听程序Vue.prototype._d
: bindDynamicKeys
Vue.prototype._p
: prependModifier
-- 事件修饰符标记动态添加到事件上为 Vue.prototype
添加 $nextTick()
、_render()
方法。
这个过程中,会首先为 Vue
构造函数添加 util, set, delete, nextTick, observable
这些全局方法,并注册一个全局组件 keep-alive
。
Vue.util
包含 warn, extend, mergeOptions, defineReactive
四个方法,但是不包含在文档中。
在构造函数下还有一个 options
属性,包含 component, directive, filter, _base
几个属性,其中 _base
指向构造函数本身。
initUse(Vue)
定义 Vue.use(plugin)
方法,用于安装插件。
initMixin(Vue)
与 1.2 initMixin(Vue)
不同,这里主要定义 Vue.mixin(mixin)
混入方法。
在打包后的
vue.js
中,代码位于function initMixin$1(Vue){}
中,源码位于src/core/global-api/mixin.js
initExtend(Vue)
定义 Vue.extend(extendOptions)
方法,提供 “使用 Vue.extend
基础构造方法来创建一个组件" 的功能(ElementUI
的 message
组件就是采用的这种方式)。
initAssetRegister(Vue)
主要定义 Vue.component(id, definition)
、Vue.directive(id, definition)
、Vue.filter(id, definition)
三个方法,主要用于定义全局的组件、指令、过滤器。
在任意一个地方使用这三个方法时,都需要传入 id
与 definition(组件定义部分)
,如果定义部分未传,则会直接返回 Vue 内部与传入 id
一致的内置组件,否则返回 undefined
。
Vue.component(id, definition)
:首先对 id
和定义部分进行合法性检查,之后调用 Vue.extend()
(源码是调用的 this.options._base.extend(definition)
)来完善这个组件。
Vue.directive(id, definition)
:首先判断传入定义是否为函数,是函数则修改定义的格式为规定格式(对象 { bind: definition, update: definition
})
最后生成的这些产物都会作为 Vue
构造函数的一个子属性,挂载在 Vue.options[ASSET_TYPE + 's'][id] = definition
, 注意这里的 definition
是验证修改后的合法格式的定义。
源码中使用遍历
ASSET_TYPES
的方式来定义三个方法,分别用于创建全局组件实例、全局指令实例与全局过滤器指令。
Vue
在初始化构造函数时还定义了其他方法,这里不多做表述。
__patch__
基于Snabbdom的虚拟DOM修补算法。
$mount
定义了公共的挂载方法。
new Vue()
创建 Vue
实例const APP = new Vue(options)
在存在组件时,也是由根节点
APP
开始解析的
_init(options)
vm._uuid
Observer
观察者实例化的标志位 vm._isVue = true
vm.$options
上
Vue
内部组件(options._isComponent === true
), 使用 initInternalComponent(vm, options)
mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)
,利用合并策略合并传入参数,并修改为指定格式initProxy(vm)
判断当前环境是否是生产环境,是否有 Proxy
,来配置对应的渲染函数的代理。 this._renderProxy = new Proxy(vm, hanlders) || vm
this._self = vm
暴露当前实例initLifecycle(vm)
初始化生命周期,添加$parent
、$root
、$children
等属性
vm.$parent = parent
,并将当前组件 push
到父组件的 children
数组中root
组件,初始化 $root
:vm.$root = parent? parent.$root : vm
$children
属性,并初始化一个空数组$refs
属性,并初始化一个空对象vm._watcher = null
vm._inactive = null
vm._directInactive = null
vm._isMounted = null
标志是否已经触发过 Mounted
钩子函数vm._isDestroyed = null
标志组件是否已经被销毁vm._isBeingDestroyed = null
标志位,为 true
则不会继续触发 beforeDestroy
和 destroyed
钩子函数initEvents(vm)
初始化事件监听
vm._events
属性,并初始化为空 vm._events = Object.create(null)
vm._hasHookEvent
属性,标识是否存在生命周期钩子的相关事件,初始化为 false
(使用布尔值可以直接判断,减少性能开销)if (vm.$options._parentListeners) updateComponentListeners(vm, vm.$options._parentListeners)
initRender(vm)
添加虚拟 dom
节点,slot
等属性
_vnode
子节点树的虚拟根节点和 _staticTrees
单次渲染的缓存树的初始值为 null
callHook(vm, "beforeCreate")
调用 beforeCreate
生命周期钩子initInjections(this)
初始化祖先组件的注入依赖initState(vm)
this._watcher = []
创建新数组保存该实例中的所有 Watcher
实例initProps
判断是否有 props
,初始化 props
部分的数据,并对其添加观察者initMethods
判断是否有 methods
,初始化 methods
initData
判断是否有 data
,有就调用 initData
初始化 data
数据,没有则将 data
作为空对象并转为响应式initComputed
判断是否有 computed
,初始化 computed
initWatch
判断是否有 watch
,初始化 watch
initProvide(vm)
初始化向下注入的依赖数据callHook(vm, "created")
调用声明周期钩子函数vm.$mount(this.$options.el)
将组件挂载到对应的Dom节点下,在这一步之前会判断传入的 options
是否有 el
属性,此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。