前篇
前端发展迅猛,当前最流行的框架非react和vue莫属,vue凭借着傻瓜式编程,使得前端开发更加简单高效,能够更快速的进行业务迭代。 文章将介绍Vue2源码,包括Vue的声明和实例的运行,从全方位解读Vue的实现和逐行解读。 文章对应Vue2的版本为:2.6.12,所使用浏览器为chrome,只介绍浏览器层面,不涉及小程序 作者所使用电脑node版本为:v12.13.0,npm版本为:6.13.0。
Vue声明-平台相关
Vue文件有两个入口,一个是带编译的src/platforms/web/entry-runtime-with-compiler.js;一个是不带编译的src/platforms/web/entry-runtime.js;第一个带编译的入口也会引用第二个入口,so咱们从第一个入口开启探索之旅。
文件:src/platforms/web/entry-runtime-with-compiler.js
// 保存runtime的$mount函数
const mount = Vue.prototype.$mount
// 重写$mount函数
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
return mount.call(this, el, hydrating)
}
从上面可以看出,mount是下面runtime里面声明的mount函数进行重写,挂载到Vue的原型对象上面。
文件:src/platforms/web/runtime/index.js
// 配置平台相关方法
// 判断是否是必须绑定prop的元素,比如input必须有value属性等
Vue.config.mustUseProp = mustUseProp
// 检测是否是保留标签,包含HTML标签和svg标签
Vue.config.isReservedTag = isReservedTag
// 检测是否是保留属性,包含class和style
Vue.config.isReservedAttr = isReservedAttr
// 获取标签命名空间
Vue.config.getTagNamespace = getTagNamespace
// 检测是否是未知类型的元素
Vue.config.isUnknownElement = isUnknownElement
// 挂载平台相关指令,platformDirectives 包含v-model和v-show
extend(Vue.options.directives, platformDirectives)
// 挂载平台相关组件,platformComponents 包含Transition和TransitionGroup
extend(Vue.options.components, platformComponents)
// 挂载patch函数,即vnode dom diff函数
Vue.prototype.__patch__ = inBrowser ? patch : noop
// 挂载$mount 函数
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
从上面可以看到,runtime里面,除了声明了$mount函数外,还挂载了平台相关的两个指令(model和show)以及两个组件(Transition和TransitionGroup),同时对Vue的config进行了扩展,添加了平台相关的几个方法。
Vue声明-核心
前面介绍了与平台(web类型,浏览器)相关的入口,现在了解到在此进行的一些配置、指令和组件,下面咱们来看核心部分的代码
文件:src/core/index.js
// 初始化配置
initGlobalAPI(Vue)
// 定义是否是服务端渲染
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
// 定义服务端渲染的全局对象
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// 定义服务端渲染时的函数
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = '__VERSION__'
文件:src/core/global-api/index.js
export function initGlobalAPI (Vue: GlobalAPI) {
const configDef = {}
configDef.get = () => config
// def Vue配置config
Object.defineProperty(Vue, 'config', configDef)
// 定义util对象
Vue.util = {
warn, // 警告函数
extend, // 扩展,继承函数
mergeOptions, // 对options进行合并的函数
defineReactive // 对属性进行设置的函数
}
// 定义set函数
Vue.set = set
// 定义delete函数
Vue.delete = del
// 定义nextTick函数
Vue.nextTick = nextTick
//暴露observable函数
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
//创建options空对象
Vue.options = Object.create(null)
// 定义components,directives,filters,空对象
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// 定义base
Vue.options._base = Vue
// 挂载组件,KeepLive
extend(Vue.options.components, builtInComponents)
// 定义Vue.use函数
initUse(Vue)
// 定义Vue.mixin函数
initMixin(Vue)
// 定义Vue.extend函数
initExtend(Vue)
// 定义Vue.compoenent、Vue.directive、Vue.filter函数
initAssetRegisters(Vue)
}
initGlobalAPI文件里面对Vue的util对象、config对象、options和Vue本身进行了扩展。
文件:src/core/instance/index.js
// 本命函数
function Vue (options) {
// 初始化,主函数
this._init(options)
}
// 往Vue函数上绑定原型函数,例如上面的_init函数
initMixin(Vue)
// 初始化$data、$props、$set、$delete、$watch等函数
stateMixin(Vue)
// 定义$on、$once、$off、$emit等函数
eventsMixin(Vue)
// 定义update、$forceUpdate、$destroy等函数
lifecycleMixin(Vue)
// 定义$nextTick、render等函数
renderMixin(Vue)
上面instance文件对Vue函数进行了声明,并在Vue的原型对象上添加了各种属性方法。
简单流程图如下