为什么写这篇vue的分析文章?
对于天资愚钝的前端(我)来说,阅读源码是件不容易的事情,毕竟有时候看源码分析的文章都看不懂。每次看到大佬们用了1~2年的vue就能掌握原理,甚至精通源码,再看看自己用了好几年都还在基本的使用阶段,心中总是羞愧不已。如果一直满足于基本的业务开发,怕是得在初级水平一直待下去了吧。所以希望在学习源码的同时记录知识点,可以让自己的理解和记忆更加深刻,也方便将来查阅。目录结构
目录结构
目录结构
本文以vue的第一次 commit a879ec06 作为分析版本
├── build

└── build.js
// `rollup` 打包配置
├── dist


└── vue.js

├── package.json
├── src
// vue源码目录

├── compiler
// 将vue-template转化为render函数


├── codegen.js
// 递归ast提取指令,分类attr,style,class,并生成render函数


├── html-parser.js
// 通过正则匹配将html字符串转化为ast


├── index.js
// compile主入口


└── text-parser.js
// 编译{{}}

├── config.js
// 对于vue的全局配置文件

├── index.js
// 主入口

├── index.umd.js
// 未知(应该是umd格式的主入口)

├── instance
// vue实例函数


└── index.js
// 包含了vue实例的初始化,compile,data代理,methods代理,watch数据,执行渲染

├── observer
// 数据订阅发布的实现


├── array.js
// 实现array变异方法,$set $remove 实现


├── batcher.js
// watch执行队列的收集,执行


├── dep.js
// 订阅中心实现


├── index.js
// 数据劫持的实现,收集订阅者


└── watcher.js
// watch实现,订阅者

├── util
// 工具函数


├── component.js


├── debug.js


├── dom.js


├── env.js
// nexttick实现


├── index.js


├── lang.js


└── options.js

└── vdom

├── dom.js
// dom操作的封装

├── h.js
// 节点数据分析(元素节点,文本节点)

├── index.js
// vdom主入口

├── modules
// 不同属性处理函数


├── attrs.js
// 普通attr属性处理


├── class.js
// class处理


├── events.js
// event处理


├── props.js
// props处理


└── style.js
// style处理

├── patch.js
// node树的渲染,包括节点的加减更新处理,及对应attr的处理

└── vnode.js
// 返回最终的节点数据
└── webpack.config.js
// webpack配置

├── build

└── build.js
// `rollup` 打包配置
├── dist


└── vue.js

├── package.json
├── src
// vue源码目录

├── compiler
// 将vue-template转化为render函数


├── codegen.js
// 递归ast提取指令,分类attr,style,class,并生成render函数


├── html-parser.js
// 通过正则匹配将html字符串转化为ast


├── index.js
// compile主入口


└── text-parser.js
// 编译{{}}

├── config.js
// 对于vue的全局配置文件

├── index.js
// 主入口

├── index.umd.js
// 未知(应该是umd格式的主入口)

├── instance
// vue实例函数


└── index.js
// 包含了vue实例的初始化,compile,data代理,methods代理,watch数据,执行渲染

├── observer
// 数据订阅发布的实现


├── array.js
// 实现array变异方法,$set $remove 实现


├── batcher.js
// watch执行队列的收集,执行


├── dep.js
// 订阅中心实现


├── index.js
// 数据劫持的实现,收集订阅者


└── watcher.js
// watch实现,订阅者

├── util
// 工具函数


├── component.js


├── debug.js


├── dom.js


├── env.js
// nexttick实现


├── index.js


├── lang.js


└── options.js

└── vdom

├── dom.js
// dom操作的封装

├── h.js
// 节点数据分析(元素节点,文本节点)

├── index.js
// vdom主入口

├── modules
// 不同属性处理函数


├── attrs.js
// 普通attr属性处理


├── class.js
// class处理


├── events.js
// event处理


├── props.js
// props处理


└── style.js
// style处理

├── patch.js
// node树的渲染,包括节点的加减更新处理,及对应attr的处理

└── vnode.js
// 返回最终的节点数据
└── webpack.config.js
// webpack配置
从template到html的过程分析
从template到html的过程分析从template到html的过程分析我们的代码是从new Vue()开始的,Vue的构造函数如下:
constructor (options) {
// options就是我们对于vue的配置
this.$options = options
this._data = options.data
// 获取元素html,即template
const el = this._el = document.querySelector(options.el)
// 编译模板 -> render函数
const render = compile(getOuterHTML(el))
this._el.innerHTML = ''
// 实例代理data数据
Object.keys(options.data).forEach(key => this._proxy(key))
// 将method的this指向实例
if (options.methods) {

Object.keys(options.methods).forEach(key => {

this[key] = options.methods[key].bind(this)

})
}
// 数据观察
this._ob = observe(options.data)
this._watchers = []
// watch数据及更新
this._watcher = new Watcher(this, render, this._update)
// 渲染函数
this._update(this._watcher.value)
}

constructor (options) {
// options就是我们对于vue的配置
this.$options = options
this._data = options.data
// 获取元素html,即template
const el = this._el = document.querySelector(options.el)
// 编译模板 -> render函数
const render = compile(getOuterHTML(el))
this._el.innerHTML = ''
// 实例代理data数据
Object.keys(options.data).forEach(key => this._proxy(key))
// 将method的this指向实例
if (options.methods) {

Object.keys(options.methods).forEach(key => {

this[key] = options.methods[key].bind(this)

})
}
// 数据观察
this._ob = observe(options.data)
this._watchers = []
// watch数据及更新
this._watcher = new Watcher(this, render, this._update)
// 渲染函数
this._update(this._watcher.value)
}
当我们初始化项目的时候,即会执行构造函数,该函数向我们展示了vue初始化的主线:编译template字符串 => 代理data数据/methods的this绑定 => 数据观察 => 建立watch及更新渲染编译template字符串 => 代理data数据/methods的this绑定 => 数据观察 => 建立watch及更新渲染1. 编译template字符串
1. 编译template字符串
const render = compile(getOuterHTML(el))

const render = compile(getOuterHTML(el))
其中compile的实现如下:
export function compile (html) {
html = html.trim()
// 对编译结果缓存
const hit = cache[html]
// parse函数在parse-html中定义,其作用是把我们获取的html字符串通过正则匹配转化为ast,输出如下 {tag: 'div', attrs: {}, children: []}
return hit || (cache[html] = generate(parse(html)))
}

export function compile (html) {
html = html.trim()
// 对编译结果缓存
const hit = cache[html]
// parse函数在parse-html中定义,其作用是把我们获取的html字符串通过正则匹配转化为ast,输出如下 {tag: 'div', attrs: {}, children: []}
return hit || (cache[html] = generate(parse(html)))
}
接下来看看generate函数,ast通过genElement的转化生成了构建节点html的函数,在genElement将对if for 等进行判断并转化( 指令的具体处理将在后面做分析,先关注主流程代码),最后都会执行genData函数
// 生成节点主函数
export function generate (ast) {
const code = genElement(ast)
// 执行code代码,并将this作为code的global对象。所以我们在template中的变量将指向为实例的属性 {{name}} -> this.name
return new Function (`with (this) { return $[code]}`)
}

// 解析单个节点 -> genData
function genElement (el, key) {
let exp
// 指令的实现,实际就是在模板编译时实现的
if (exp = getAttr(el, 'v-for')) {

return genFor(el, exp)
} else if (exp = getAttr(el, 'v-if')) {

return genIf(el, exp)
} else if (el.tag === 'template') {

return genChildren(el)
} else {

// 分别为 tag 自身属性 子节点数据

return `__h__('${ el.tag }', ${ genData(el, key) }, ${ genChildren(el) })`
}
}


// 生成节点主函数
export function generate (ast) {
const code = genElement(ast)
// 执行code代码,并将this作为code的global对象。所以我们在template中的变量将指向为实例的属性 {{name}} -> this.name
return new Function (`with (this) { return $[code]}`)
}

// 解析单个节点 -> genData
function genElement (el, key) {
let exp
// 指令的实现,实际就是在模板编译时实现的
if (exp = getAttr(el, 'v-for')) {

return genFor(el, exp)
} else if (exp = getAttr(el, 'v-if')) {

return genIf(el, exp)
} else if (el.tag === 'template') {

return genChildren(el)
} else {

// 分别为 tag 自身属性 子节点数据

return `__h__('${ el.tag }', ${ genData(el, key) }, ${ genChildren(el) })`
}
}

我们可以看看在genData中都做了什么。上面的parse函数将html字符串转化为ast,而在genData中则将节点的attrs数据进一步处理,例如class -> renderClass style class props attr 分类。在这里可以看到 bind 指令的实现,即通过正则匹配 : 和 bind,如果匹配则把相应的 value值转化为 (value)的形式,而不匹配的则通过JSON.stringify()转化为字符串('value')。最后输出attrs的(key-value),在这里得到的对象是字符串形式的,例如(value)等也仅仅是将变量名,而在generate中通过new Function进一步通过(this.value)得到变量值。
function genData (el, key) {
// 没有属性返回空对象
if (!el.attrs.length) {

return '{}'
}
// key
let data = key ? `{key:${ key },` : `{`
// class处理
if (el.attrsMap[':class'] || el.attrsMap['class']) {

data += `class: _renderClass(${ el.attrsMap[':class'] }, "${ el.attrsMap['class'] || '' }"),`
}
// attrs
let attrs = `attrs:{`
let props = `props:{`
let hasAttrs = false
let hasProps = false
for (let i = 0, l = el.attrs.length; i < l; i++) {

let attr = el.attrs[i]

let name = attr.name

// bind属性

if (bindRE.test(name)) {

name = name.replace(bindRE, '')

if (name === 'class') {

continue

// style处理

} else if (name === 'style') {

data += `style: ${ attr.value },`

// props属性处理

} else if (mustUsePropsRE.test(name)) {

hasProps = true

props += `"${ name }": (${ attr.value }),`

// 其他属性

} else {

hasAttrs = true

attrs += `"${ name }": (${ attr.value }),`

}

// on指令,未实现

} else if (onRE.test(name)) {

name = name.replace(onRE, '')

// 普通属性

} else if (name !== 'class') {

hasAttrs = true

attrs += `"${ name }": (${ JSON.stringify(attr.value) }),`

}
}
if (hasAttrs) {

data += attrs.slice(0, -1) + '},'
}
if (hasProps) {

data += props.slice(0, -1) + '},'
}
return data.replace(/,$/, '') + '}'
}

function genData (el, key) {
// 没有属性返回空对象
if (!el.attrs.length) {

return '{}'
}
// key
let data = key ? `{key:${ key },` : `{`
// class处理
if (el.attrsMap[':class'] || el.attrsMap['class']) {

data += `class: _renderClass(${ el.attrsMap[':class'] }, "${ el.attrsMap['class'] || '' }"),`
}
// attrs
let attrs = `attrs:{`
let props = `props:{`
let hasAttrs = false
let hasProps = false
for (let i = 0, l = el.attrs.length; i < l; i++) {

let attr = el.attrs[i]

let name = attr.name

// bind属性

if (bindRE.test(name)) {

name = name.replace(bindRE, '')

if (name === 'class') {

continue

// style处理

} else if (name === 'style') {

data += `style: ${ attr.value },`

// props属性处理

} else if (mustUsePropsRE.test(name)) {

hasProps = true

props += `"${ name }": (${ attr.value }),`

// 其他属性

} else {

hasAttrs = true

attrs += `"${ name }": (${ attr.value }),`

}

// on指令,未实现

} else if (onRE.test(name)) {

name = name.replace(onRE, '')

// 普通属性

} else if (name !== 'class') {

hasAttrs = true

attrs += `"${ name }": (${ JSON.stringify(attr.value) }),`

}
}
if (hasAttrs) {

data += attrs.slice(0, -1) + '},'
}
if (hasProps) {

data += props.slice(0, -1) + '},'
}
return data.replace(/,$/, '') + '}'
}
而对于genChildren,我们可以猜到就是对ast中的children进行遍历调用genElement,实际上在这里还包括了对文本节点的处理。
// 遍历子节点 -> genNode
function genChildren (el) {
if (!el.children.length) {

return 'undefined'
}
// 对children扁平化处理
return '__flatten__([' + el.children.map(genNode).join(',') + '])'
}

function genNode (node) {
if (node.tag) {

return genElement(node)
} else {

return genText(node)
}
}

// 解析{{}}
function genText (text) {
if (text === ' ') {

return '" "'
} else {

const exp = parseText(text)

if (exp) {

return 'String(' + escapeNewlines(exp) + ')'

} else {

return escapeNewlines(JSON.stringify(text))

}
}
}


// 遍历子节点 -> genNode
function genChildren (el) {
if (!el.children.length) {

return 'undefined'
}
// 对children扁平化处理
return '__flatten__([' + el.children.map(genNode).join(',') + '])'
}

function genNode (node) {
if (node.tag) {

return genElement(node)
} else {

return genText(node)
}
}

// 解析{{}}
function genText (text) {
if (text === ' ') {

return '" "'
} else {

const exp = parseText(text)

if (exp) {

return 'String(' + escapeNewlines(exp) + ')'

} else {

return escapeNewlines(JSON.stringify(text))

}
}
}

genText处理了text及换行,在parseText函数中利用正则解析{{}},输出字符串(value)形式的字符串。现在我们再看看__h__('${ el.tag }', ${ genData(el, key) }, ${ genChildren(el) })中__h__函数
// h 函数利用上面得到的节点数据得到 vNode对象 => 虚拟dom
export default function h (tag, b, c) {
var data = {}, children, text, i
if (arguments.length === 3) {

data = b

if (isArray(c)) { children = c }

else if (isPrimitive(c)) { text = c }
} else if (arguments.length === 2) {

if (isArray(b)) { children = b }

else if (isPrimitive(b)) { text = b }

else { data = b }
}
if (isArray(children)) {

// 子节点递归处理

for (i = 0; i < children.length; ++i) {

if (isPrimitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i])

}
}
// svg处理
if (tag === 'svg') {

addNS(data, children)
}
// 子节点为文本节点
return VNode(tag, data, children, text, undefined)
}

// h 函数利用上面得到的节点数据得到 vNode对象 => 虚拟dom
export default function h (tag, b, c) {
var data = {}, children, text, i
if (arguments.length === 3) {

data = b

if (isArray(c)) { children = c }

else if (isPrimitive(c)) { text = c }
} else if (arguments.length === 2) {

if (isArray(b)) { children = b }

else if (isPrimitive(b)) { text = b }

else { data = b }
}
if (isArray(children)) {

// 子节点递归处理

for (i = 0; i < children.length; ++i) {

if (isPrimitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i])

}
}
// svg处理
if (tag === 'svg') {

addNS(data, children)
}
// 子节点为文本节点
return VNode(tag, data, children, text, undefined)
}
到此为止,我们分析了const render = compile(getOuterHTML(el)),从el的html字符串到render函数都是怎么处理的。2. 代理data数据/methods的this绑定
2. 代理data数据/methods的this绑定
// 实例代理data数据
Object.keys(options.data).forEach(key => this._proxy(key))
// 将method的this指向实例
if (options.methods) {
Object.keys(options.methods).forEach(key => {

this[key] = options.methods[key].bind(this)
})
}

// 实例代理data数据
Object.keys(options.data).forEach(key => this._proxy(key))
// 将method的this指向实例
if (options.methods) {
Object.keys(options.methods).forEach(key => {

this[key] = options.methods[key].bind(this)
})
}
实例代理data数据的实现比较简单,就是利用了对象的setter和getter,读取this数据时返回data数据,在设置this数据时同步设置data数据
_proxy (key) {
if (!isReserved(key)) {

// need to store ref to self here

// because these getter/setters might

// be called by child scopes via

// prototype inheritance.

var self = this

Object.defineProperty(self, key, {

configurable: true,

enumerable: true,

get: function proxyGetter () {

return self._data[key]

},

set: function proxySetter (val) {

self._data[key] = val

}

})
}
}

_proxy (key) {
if (!isReserved(key)) {

// need to store ref to self here

// because these getter/setters might

// be called by child scopes via

// prototype inheritance.

var self = this

Object.defineProperty(self, key, {

configurable: true,

enumerable: true,

get: function proxyGetter () {

return self._data[key]

},

set: function proxySetter (val) {

self._data[key] = val

}

})
}
}
3. Obaerve的实现
3. Obaerve的实现Observe的实现原理在很多地方都有分析,主要是利用了Object.defineProperty()来建立对数据更改的订阅,在很多地方也称之为数据劫持。下面我们来学习从零开始建立这样一个数据的订阅发布体系。从简单处开始,我们希望有个函数可以帮我们监听数据的改变,每当数据改变时执行特定回调函数
function observe(data, callback) {
if (!data || typeof data !== 'object') {

return
}

// 遍历key
Object.keys(data).forEach((key) => {

let value = data[key];


// 递归遍历监听深度变化

observe(value, callback);


// 监听单个可以的变化

Object.defineProperty(data, key, {

configurable: true,

enumerable: true,

get() {

return value;

},

set(val) {

if (val === value) {

return

}


value = val;


// 监听新的数据

observe(value, callback);



// 数据改变的回调

callback();

}

});
});
}

// 使用observe函数监听data
const data = {};
observe(data, () => {
console.log('data修改');
})


function observe(data, callback) {
if (!data || typeof data !== 'object') {

return
}

// 遍历key
Object.keys(data).forEach((key) => {

let value = data[key];


// 递归遍历监听深度变化

observe(value, callback);


// 监听单个可以的变化

Object.defineProperty(data, key, {

configurable: true,

enumerable: true,

get() {

return value;

},

set(val) {

if (val === value) {

return

}


value = val;


// 监听新的数据

observe(value, callback);



// 数据改变的回调

callback();

}

});
});
}

// 使用observe函数监听data
const data = {};
observe(data, () => {
console.log('data修改');
})

上面我们实现了一个简单的observe函数,只要我们将编译函数作为callback传入,那么每次数据更改时都会触发回调函数。但是我们现在不能为单独的key设置监听及回调函数,只能监听整个对象的变化执行回调。下面我们对函数进行改进,达到为某个key设置监听及回调。同时建立调度中心,让整个订阅发布模式更加清晰。
// 首先是订阅中心
class Dep {
constructor() {

this.subs = []; // 订阅者数组
}

addSub(sub) {

// 添加订阅者

this.subs.push(sub);
}

notify() {

// 发布通知

this.subs.forEach((sub) => {

sub.update();

});
}
}

// 当前订阅者,在getter中标记
Dep.target = null;

// 订阅者
class Watch {
constructor(express, cb) {

this.cb = cb;

if (typeof express === 'function') {

this.expressFn = express;

} else {

this.expressFn = () => {

return new Function(express)();

}

}



this.get();
}

get() {

// 利用Dep.target存当前订阅者

Dep.target = this;

// 执行表达式 -> 触发getter -> 在getter中添加订阅者

this.expressFn();

// 及时置空

Dep.taget = null;
}

update() {

// 更新

this.cb();
}

addDep(dep) {

// 添加订阅

dep.addSub(this);
}
}

// 观察者 建立观察
class Observe {
constructor(data) {

if (!data || typeof data !== 'object') {

return

}


// 遍历key

Object.keys(data).forEach((key) => {

// key => dep 对应

const dep = new Dep();

let value = data[key];


// 递归遍历监听深度变化

const observe = new Observe(value);


// 监听单个可以的变化

Object.defineProperty(data, key, {

configurable: true,

enumerable: true,

get() {

if (Dep.target) {

const watch = Dep.target;

watch.addDep(dep);

}

return value;

},

set(val) {

if (val === value) {

return

}


value = val;


// 监听新的数据

new Observe(value);



// 数据改变的回调

dep.notify();

}

});

});
}
}

// 监听数据中某个key的更改
const data = {
name: 'xiaoming',
age: 26
};

const observe = new Observe(data);

const watch = new Watch('data.age', () => {
console.log('age update');
});

data.age = 22
// 首先是订阅中心
class Dep {
constructor() {

this.subs = []; // 订阅者数组
}

addSub(sub) {

// 添加订阅者

this.subs.push(sub);
}

notify() {

// 发布通知

this.subs.forEach((sub) => {

sub.update();

});
}
}

// 当前订阅者,在getter中标记
Dep.target = null;

// 订阅者
class Watch {
constructor(express, cb) {

this.cb = cb;

if (typeof express === 'function') {

this.expressFn = express;

} else {

this.expressFn = () => {

return new Function(express)();

}

}



this.get();
}

get() {

// 利用Dep.target存当前订阅者

Dep.target = this;

// 执行表达式 -> 触发getter -> 在getter中添加订阅者

this.expressFn();

// 及时置空

Dep.taget = null;
}

update() {

// 更新

this.cb();
}

addDep(dep) {

// 添加订阅

dep.addSub(this);
}
}

// 观察者 建立观察
class Observe {
constructor(data) {

if (!data || typeof data !== 'object') {

return

}


// 遍历key

Object.keys(data).forEach((key) => {

// key => dep 对应

const dep = new Dep();

let value = data[key];


// 递归遍历监听深度变化

const observe = new Observe(value);


// 监听单个可以的变化

Object.defineProperty(data, key, {

configurable: true,

enumerable: true,

get() {

if (Dep.target) {

const watch = Dep.target;

watch.addDep(dep);

}

return value;

},

set(val) {

if (val === value) {

return

}


value = val;


// 监听新的数据

new Observe(value);



// 数据改变的回调

dep.notify();

}

});

});
}
}

// 监听数据中某个key的更改
const data = {
name: 'xiaoming',
age: 26
};

const observe = new Observe(data);

const watch = new Watch('data.age', () => {
console.log('age update');
});

data.age = 22现在我们实现了订阅中心,订阅者,观察者。观察者监测数据的更新,订阅者通过订阅中心订阅数据的更新,当数据更新时,观察者会告诉订阅中心,订阅中心再逐个通知所有的订阅者执行更新函数。到现在为止,我们可以大概猜出vue的实现原理:

建立观察者观察data数据的更改 (new Observe)

在编译的时候,当某个代码片段或节点依赖data数据,为该节点建议订阅者,订阅data中某些数据的更新(new Watch)

当dada数据更新时,通过订阅中心通知数据更新,执行节点更新函数,新建或更新节点(dep.notify())
建立观察者观察data数据的更改 (new Observe)在编译的时候,当某个代码片段或节点依赖data数据,为该节点建议订阅者,订阅data中某些数据的更新(new Watch)当dada数据更新时,通过订阅中心通知数据更新,执行节点更新函数,新建或更新节点(dep.notify())上面是我们对vue实现原理订阅发布模式的基本实现,及编译到更新过程的猜想,现在我们接着分析vue源码的实现:
在实例的初始化中

// ...
// 为数据建立数据观察
this._ob = observe(options.data)
this._watchers = []
// 添加订阅者 执行render 会触发 getter 订阅者订阅更新,数据改变触发 setter 订阅中心通知订阅者执行 update
this._watcher = new Watcher(this, render, this._update)
// ...

// ...
// 为数据建立数据观察
this._ob = observe(options.data)
this._watchers = []
// 添加订阅者 执行render 会触发 getter 订阅者订阅更新,数据改变触发 setter 订阅中心通知订阅者执行 update
this._watcher = new Watcher(this, render, this._update)
// ...
vue中数据观察的实现

// observe函数
export function observe (value, vm) {
if (!value || typeof value !== 'object') {

return
}
if (

hasOwn(value, '__ob__') &&

value.__ob__ instanceof Observer
) {

ob = value.__ob__
} else if (

shouldConvert &&

(isArray(value) || isPlainObject(value)) &&

Object.isExtensible(value) &&

!value._isVue
) {

// 为数据建立观察者

ob = new Observer(value)
}
// 存储关联的vm
if (ob && vm) {

ob.addVm(vm)
}
return ob
}

// => Observe 函数
export function Observer (value) {
this.value = value
// 在数组变异方法中有用
this.dep = new Dep()
// observer实例存在__ob__中
def(value, '__ob__', this)
if (isArray(value)) {

var augment = hasProto

? protoAugment

: copyAugment

// 数组遍历,添加变异的数组方法

augment(value, arrayMethods, arrayKeys)

// 对数组的每个选项调用observe函数

this.observeArray(value)
} else {

// walk -> convert -> defineReactive -> setter/getter

this.walk(value)
}
}

// => walk
Observer.prototype.walk = function (obj) {
var keys = Object.keys(obj)
for (var i = 0, l = keys.length; i < l; i++) {

this.convert(keys[i], obj[keys[i]])
}
}

// => convert
Observer.prototype.convert = function (key, val) {
defineReactive(this.value, key, val)
}

// 重点看看defineReactive
export function defineReactive (obj, key, val) {
// key对应的的订阅中心
var dep = new Dep()

var property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {

return
}

// 兼容原有setter/getter
// cater for pre-defined getter/setters
var getter = property && property.get
var setter = property && property.set

// 实现递归监听属性 val = obj[key]
// 深度优先遍历 先为子属性设置 reactive
var childOb = observe(val)
// 设置 getter/setter
Object.defineProperty(obj, key, {

enumerable: true,

configurable: true,

get: function reactiveGetter () {

var value = getter ? getter.call(obj) : val

// Dep.target 为当前 watch 实例

if (Dep.target) {

// dep 为 obj[key] 对应的调度中心 dep.depend 将当前 wtcher 实例添加到调度中心

dep.depend()

if (childOb) {

// childOb.dep 为 obj[key] 值 val 对应的 observer 实例的 dep

// 实现array的变异方法和$set方法订阅

childOb.dep.depend()

}


// TODO: 此处作用未知?

if (isArray(value)) {

for (var e, i = 0, l = value.length; i < l; i++) {

e = value[i]

e && e.__ob__ && e.__ob__.dep.depend()

}

}

}

return value

},

set: function reactiveSetter (newVal) {

var value = getter ? getter.call(obj) : val

// 通过 getter 获取 val 判断是否改变

if (newVal === value) {

return

}

if (setter) {

setter.call(obj, newVal)

} else {

val = newVal

}

// 为新值设置 reactive

childOb = observe(newVal)

// 通知key对应的订阅中心更新

dep.notify()

}
})
}


// observe函数
export function observe (value, vm) {
if (!value || typeof value !== 'object') {

return
}
if (

hasOwn(value, '__ob__') &&

value.__ob__ instanceof Observer
) {

ob = value.__ob__
} else if (

shouldConvert &&

(isArray(value) || isPlainObject(value)) &&

Object.isExtensible(value) &&

!value._isVue
) {

// 为数据建立观察者

ob = new Observer(value)
}
// 存储关联的vm
if (ob && vm) {

ob.addVm(vm)
}
return ob
}

// => Observe 函数
export function Observer (value) {
this.value = value
// 在数组变异方法中有用
this.dep = new Dep()
// observer实例存在__ob__中
def(value, '__ob__', this)
if (isArray(value)) {

var augment = hasProto

? protoAugment

: copyAugment

// 数组遍历,添加变异的数组方法

augment(value, arrayMethods, arrayKeys)

// 对数组的每个选项调用observe函数

this.observeArray(value)
} else {

// walk -> convert -> defineReactive -> setter/getter

this.walk(value)
}
}

// => walk
Observer.prototype.walk = function (obj) {
var keys = Object.keys(obj)
for (var i = 0, l = keys.length; i < l; i++) {

this.convert(keys[i], obj[keys[i]])
}
}

// => convert
Observer.prototype.convert = function (key, val) {
defineReactive(this.value, key, val)
}

// 重点看看defineReactive
export function defineReactive (obj, key, val) {
// key对应的的订阅中心
var dep = new Dep()

var property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {

return
}

// 兼容原有setter/getter
// cater for pre-defined getter/setters
var getter = property && property.get
var setter = property && property.set

// 实现递归监听属性 val = obj[key]
// 深度优先遍历 先为子属性设置 reactive
var childOb = observe(val)
// 设置 getter/setter
Object.defineProperty(obj, key, {

enumerable: true,

configurable: true,

get: function reactiveGetter () {

var value = getter ? getter.call(obj) : val

// Dep.target 为当前 watch 实例

if (Dep.target) {

// dep 为 obj[key] 对应的调度中心 dep.depend 将当前 wtcher 实例添加到调度中心

dep.depend()

if (childOb) {

// childOb.dep 为 obj[key] 值 val 对应的 observer 实例的 dep

// 实现array的变异方法和$set方法订阅

childOb.dep.depend()

}


// TODO: 此处作用未知?

if (isArray(value)) {

for (var e, i = 0, l = value.length; i < l; i++) {

e = value[i]

e && e.__ob__ && e.__ob__.dep.depend()

}

}

}

return value

},

set: function reactiveSetter (newVal) {

var value = getter ? getter.call(obj) : val

// 通过 getter 获取 val 判断是否改变

if (newVal === value) {

return

}

if (setter) {

setter.call(obj, newVal)

} else {

val = newVal

}

// 为新值设置 reactive

childOb = observe(newVal)

// 通知key对应的订阅中心更新

dep.notify()

}
})
}

订阅中心的实现
订阅中心的实现
let uid = 0

export default function Dep () {
this.id = uid++
// 订阅调度中心的watch数组
this.subs = []
}

// 当前watch实例
Dep.target = null

// 添加订阅者
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}

// 移除订阅者
Dep.prototype.removeSub = function (sub) {
this.subs.$remove(sub)
}

// 订阅
Dep.prototype.depend = function () {
// Dep.target.addDep(this) => this.addSub(Dep.target) => this.subs.push(Dep.target)
Dep.target.addDep(this)
}

// 通知更新
Dep.prototype.notify = function () {
// stablize the subscriber list first
var subs = this.subs.slice()
for (var i = 0, l = subs.length; i < l; i++) {

// subs[i].update() => watch.update()

subs[i].update()
}
}
let uid = 0

export default function Dep () {
this.id = uid++
// 订阅调度中心的watch数组
this.subs = []
}

// 当前watch实例
Dep.target = null

// 添加订阅者
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}

// 移除订阅者
Dep.prototype.removeSub = function (sub) {
this.subs.$remove(sub)
}

// 订阅
Dep.prototype.depend = function () {
// Dep.target.addDep(this) => this.addSub(Dep.target) => this.subs.push(Dep.target)
Dep.target.addDep(this)
}

// 通知更新
Dep.prototype.notify = function () {
// stablize the subscriber list first
var subs = this.subs.slice()
for (var i = 0, l = subs.length; i < l; i++) {

// subs[i].update() => watch.update()

subs[i].update()
}
}订阅者的实现
订阅者的实现
export default function Watcher (vm, expOrFn, cb, options) {
// mix in options
if (options) {

extend(this, options)
}
var isFn = typeof expOrFn === 'function'
this.vm = vm
// vm 的 _watchers 包含了所有 watch
vm._watchers.push(this)
this.expression = expOrFn
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers
// deps 一个 watch 实例可以对应多个 dep
this.deps = []
this.newDeps = []
this.depIds = Object.create(null)
this.newDepIds = null
this.prevError = null // for async error stacks
// parse expression for getter/setter
if (isFn) {

this.getter = expOrFn

this.setter = undefined
} else {

warn('vue-lite only supports watching functions.')
}
this.value = this.lazy

? undefined

: this.get()
this.queued = this.shallow = false
}

Watcher.prototype.get = function () {
this.beforeGet()
var scope = this.scope || this.vm
var value
try {

// 执行 expOrFn,此时会触发 getter => dep.depend() 将watch实例添加到对应 obj[key] 的 dep

value = this.getter.call(scope, scope)
}
if (this.deep) {

// 深度watch

// 触发每个key的getter watch实例将对应多个dep

traverse(value)
}
// ...
this.afterGet()
return value
}

// 触发getter,实现订阅
Watcher.prototype.beforeGet = function () {
Dep.target = this
this.newDepIds = Object.create(null)
this.newDeps.length = 0
}

// 添加订阅
Watcher.prototype.addDep = function (dep) {
var id = dep.id
if (!this.newDepIds[id]) {

// 将新出现的dep添加到newDeps中

this.newDepIds[id] = true

this.newDeps.push(dep)

// 如果已在调度中心,不再重复添加

if (!this.depIds[id]) {

// 将watch添加到调度中心的数组中

dep.addSub(this)

}
}
}

Watcher.prototype.afterGet = function () {
// 切除key的getter联系
Dep.target = null
var i = this.deps.length
while (i--) {

var dep = this.deps[i]

if (!this.newDepIds[dep.id]) {

// 移除不在expOrFn表达式中关联的dep中watch的订阅

dep.removeSub(this)

}
}
this.depIds = this.newDepIds
var tmp = this.deps
this.deps = this.newDeps
// TODO: 既然newDeps最终会被置空,这边赋值的意义在于?
this.newDeps = tmp
}

// 订阅中心通知消息更新
Watcher.prototype.update = function (shallow) {
if (this.lazy) {

this.dirty = true
} else if (this.sync || !config.async) {

this.run()
} else {

// if queued, only overwrite shallow with non-shallow,

// but not the other way around.

this.shallo