从入口文件开始
文件结构
选择了包含编译器和运行时的版本,该版本入口文件位于src/platforms/web/entry-runtime-with-compiler.js
在该版本里先引入 Vue 主类,然后给Vue类添加了编译器和 mount 方法,伪代码如下
js
// 引入主类和编译器
import Vue from './runtime/index'
import { compileToFunctions } from './compiler/index'
// 暂存原有的 mount 方法
const mount = Vue.prototype.$mount
// 添加新 mount 方法
Vue.prototype.$mount = function (el, hydrating) {
// 对el进行归一,以及判断是否可挂载,暂且隐藏
// 取到原型里的options
const options = this.$options
if (!options.render) {
// 取模板
let template = options.template
// 参数归一
// 如果参数中有模板,则执行一些逻辑。否则就将挂载dom转换为模板
if (template) {
// 有模板执行,最后把处理过的赋值给模板
template = 'xxxxxxxx'
} else if (el) {
// 没有模板,通过dom元素生成模板
template = getOuterHTML(el)
}
// 对归一后的模板进行处理
if (template) {
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// 执行一些逻辑
}
// 取到转换后的render相关函数,并赋值给options
const { render, staticRenderFns } = compileToFunctions()
options.render = render
options.staticRenderFns = staticRenderFns
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// 执行其他逻辑
}
}
}
// 执行原有的方法
return mount.call(this, el, hydrating)
}
// 添加编译器并导出
Vue.compile = compileToFunctions
export default Vue在上面的代码中,el 是传入的需要挂载的真实 dom 节点。上面的伪代码主要执行以下几个逻辑
- 引入 vue 主类
- 将原有的 mount 方法取出并暂存
- 添加针对于该版本的新 mount 方法
- 并在挂载方法中,对模板进行参数归一化
- 对归一后的模板参数执行编译的逻辑
- 最后主类添加编译器,然后导出主类
从主类开始
runtime
该版本从 ./runtime/index 中引入主类,下面看一下这个文件中对主类做了什么。根据文件名不难看出,在这里主要添加了一些和运行时相关的方法和逻辑
js
// 引入 主类和一些必要的方法
import Vue from 'core/index'
import { patch } from './patch'
import { mountComponent } from 'core/instance/lifecycle'
// 给原型添加__patch__方法
Vue.prototype.__patch__ = inBrowser ? patch : noop
// 添加最原始的mount方法
Vue.prototype.$mount = function (el, hydrating) {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
// 执行 devtools 相关的钩子函数逻辑
if (inBrowser) {
// 添加devtools相关的回调
setTimeout(() => {}, 0)
}
export default Vue在上面的伪代码中,主要有以下几个逻辑
- 引入主类
- 添加 patch 方法
- 添加原始挂载方法
- 执行开发者工具相关逻辑
- 导出主类
core
runtime 文件中从core/index文件中引入了主类,虽然该文件所在的目录叫做核心,但引用链远没有结束
先来看核心类做了什么
js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
initGlobalAPI(Vue)
// 服务端渲染相关,不重要可忽略
Object.defineProperty(Vue.prototype, '$isServer', {})
Object.defineProperty(Vue.prototype, '$ssrContext', {})
Object.defineProperty(Vue, 'FunctionalRenderContext', {})
Vue.version = '__VERSION__'
export default Vue上面伪代码比较简短,引入主类然后执行全局的初始化函数。然后添加一些服务端渲染相关内容,最后导出
instance
这里是Vue主类引用链的最后一节
js
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) {
if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue)) {
// Vue 必须使用 new关键词进行调用
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue内容比较简洁。定义 Vue 方法,然后引入并执行初始化和混入相关的方法
总结
src/platforms/web/entry-runtime-with-compiler.jssrc/platforms/web/runtime/index.jssrc/core/index.jssrc/core/instance/index.js
从编译器版本的入口文件到最终的实例,一共引用了上面的 4 个文件,而到这里 Vue 的引用链条就结束了,将在下一章对初始化和混入进行具体分析