vue双向绑定源码解析
1、概念
- 监听器 Observer:用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
- 订阅器 Dep:用来收集订阅者,对监听器
Observer
和 订阅者Watcher
进行统一管理; - 订阅者 Watcher:可以收到属性的变化通知并执行相应的方法,从而更新视图;
- 解析器 Compile:可以解析每个节点的相关指令,对模板数据和订阅器进行初始化。
2、主流程相关函数(理解为代码注释)
- initData(proxy)
- observe
- Observer(protoAugment/copyAugment)
- walk
- observeArray
- defineReactive$$1
- Dep
- Watcher
- baseCompile
/*
*主要功能是下面2个
*1、def设置proxy代理
*2、observe观察数据
*/
function initData (vm) {
var data = vm.$options.data;
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {};
if (!isPlainObject(data)) {
data = {};
warn(
'data functions should return an object:n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
);
}
var keys = Object.keys(data);
var props = vm.$options.props;
var methods = vm.$options.methods;
var i = keys.length;
while (i--) {
var key = keys[i];
{
if (methods && hasOwn(methods, key)) {
warn(
("Method "" + key + "" has already been defined as a data property."),
vm
);
}
}
if (props && hasOwn(props, key)) {
warn(
"The data property "" + key + "" is already declared as a prop. " +
"Use prop default value instead.",
vm
);
} else if (!isReserved(key)) {
//把data中的数据绑定到跟对象上。所有this.xxx、this.data.xxx都可访问
proxy(vm, "_data", key);
}
}
// 观察数据
observe(data, true /* asRootData */);
}
/**
* 判断当前数据是否为对象
* 判断__ob__属性是否存在,存在则直接引用,不存在则创建Observer实例
* 如果是根数据则计数
*/
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
//__ob__:里面存放了该属性的观察器,也就是Observer的实例,防止重复绑定。
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
}
/**
*将Observer实例绑定到目标对象的__ob__属性
*判断当前目标对象是否为数组
*是数组则监听数组的方法,并且为数组的每个成员尝试构建一个Observer实例(即调用observe函数)
*是对象则响应式对象的属性
*/
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;//保存vm实例将当前对象作为根数据root $data的次数
def(value, '__ob__', this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);//修改数组的_proto_
} else {
copyAugment(value, arrayMethods, arrayKeys);//覆盖数组的方法
}
this.observeArray(value);//对数组类型的成员进行绑定
} else {
this.walk(value);//对对象类型的数据进行绑定
}
};
/**
* 循环遍历进行数据绑定
*/
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
/**
* 循环遍历,观察数组的每一项
*/
Observer.prototype.observeArray = function observeArray (items) {
for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]);
}
};
/**
* 设置getter、setter
* 创建Dep订阅器
*/
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
// console.log('defineReactive$$1===>',dep,Dep,Dep.target);
var childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
// 只有在有Dep.target时才说明是Vue内部依赖收集过程触发的getter
// 那么这个时候就需要执行dep.depend(),将watcher(Dep.target的实际值)添加到dep的subs数组中
// 对于其他时候,比如dom事件回调函数中访问这个变量导致触发的getter并不需要执行依赖收集,直接返回value即可
if (Dep.target) {
dep.depend();
if (childOb) {
//如果value是对象,那就让生成的Observer实例当中的dep也收集依赖
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
// 通知订阅我这个dep的watcher们:我更新了
dep.notify();
}
});
}
/*
*负责编译template
*1、返回AST语法树
*2、返回render函数
*3、返回静态render的数组
*/
function baseCompile (
template,
options
) {
var ast = parse(template.trim(), options);
if (options.optimize !== false) {
optimize(ast, options);
}
var code = generate(ast, options);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
}
参考:
Vue源码学习笔记之compilecassieran.github.io