Vue 版本:v2.6.11

文件夹结构

src

  • compiler # 编译【暂定不展开】涉及到 mount 挂载
  • global-api # 全局方法
    • assets.js
    • extend.js
    • index.js
    • mixin.js
    • use.js
  • core # 核心代码
    • components #组件
    • global-api #公共api
    • instance #实例
      • render-helpers
      • event.js #初始化事件
      • index.js #实例 Vue
      • init.js #给实例的属性添加方法、生命周期初始化等,然后再调用原型链上的_mount 方法进一步解析 HTML,再根据 ast 树渲染虚拟 dom
      • inject.js
      • lifecycle.js #生命钩子函数添加进实例
      • proxy.js #具体不知道,只知道多了 proxy 方法
      • render.js #实例添加方法、Vue的原型链上添加 _render 渲染函数,然后创建虚拟 dom
      • state.js #初始化实例的 props、methods、data、computed、watch
    • observer #观察
      • array.js #数组的处理
      • dep.js #收集依赖
      • index.js
      • scheduler.js
      • traverse.js
      • watcher.js #watcher 类
    • util #工具函数
    • vdom #虚拟 dom
      • helpers
      • modules
      • create-components.js
      • create-element.js #_render 渲染时会调用它,然后根据逻辑判断生成 VNode
      • create-functional-components.js
      • patch.js #虚拟 dom => 真实 dom
      • vnode.js #虚拟 dom 的各个属性添加、创建不同虚拟 dom 的方法
    • config.js
    • index.js
  • platforms # 不同平台的支持(web、小程序)
  • server # 服务端渲染
  • sfc # .vue 文件解析
  • shared # 公共方法

Vue 的文件一目了然,但是实际上阅读时会有点绕圈子,要时不时返回去看看。

global-api

它给 Vue 对象添加了 util、set、delete、nextTick、options 等属性对象,再依次调用 extend、initUse 等方法。本次研究对象是 component,因为 Vue 的其中一个重要特点就是组件化。

Vue.extend

在看 Vue.options.component 时要先看到 initExtend 方法,在[global-api/extend.js]文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function initExtend (Vue: GlobalAPI) {
Vue.cid = 0
let cid = 1
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
// 创建了 VueComponent 构造方法,目的是继承 Vue 原型链上的方法属性。
const Sub = function VueComponent (options) {
this._init(options)
}
// 这里的 Object.create() 是浅拷贝
Sub.prototype = Object.create(Super.prototype)
// 但是它的原型还是要指向自己
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// 下面就是 VueComponent 也要拥有 Vue 本身的方法
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
if (name) {
Sub.options.components[name] = Sub
}

Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
cachedCtors[SuperId] = Sub
return Sub
}
}

因此 Vue.extend 返回的是一个 VueComponent 方法,并且它拥有了 Vue 原型链上的方法。

Vue.options.component 方法

Vue.options 上有3个对象,分别是component、directive 和 filter。
它还继续调用 initAssetRegisters 方法,给 Vue.options.component 添加了方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
initAssetRegisters (Vue: GlobalAPI) {
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
if (process.env.NODE_ENV !== 'production' && type === 'component') {
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
// this.options._base 指的是 Vue
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
this.options[type + 's'][id] = definition
return definition
}
}
})
}

这里有2个参数,id、definition,通过调用 Vue.extend 方法将 definition 转化为 VueComponent 对象,并存入 Vue.options.components 里。
这样就做成了一个组件的库了。