Compile
vue 内置了一个编译器,主要的作用是将template 中的字符串转换为AST,将各标签中的元素以及属性等用特定的数据结构进行了描述,其内部通过pase对字符串进行解析。在vue中compiler并不是固定执行,而是通过createCompilerCreator 生成 createCompiler 再由 其生成
{compile,compileToFunctions:createCompileToFunctionFn(compile)}
而主要对template 进行解析的 parse就在compile 中执行。顺带提一句这个编译器之所以要通过这么麻烦的创建流程主要是因为它需要通过多个compileOption对编译器进行配置,修改编译器内部的规则。
模版解析: 事件指令解析
1. 从元素节点中取出指令
2. 从指令名中取出事件名
3. 根据指令的值(表达式)从methods中得到对应的回调函数
4. 给当前元素节点绑定指定的事件名和回调函数(指定this指向为vm)
5. 移除元素的指令属性
watcher
-
1.数据绑定
-
1.数据绑定(效果)
-
一旦更新了Data中的某个属性数据,所有界面上直接或者间接使用了此属性的节点都会发生更新
-
2.数据劫持(技术)
-
1.数据劫持是vue中用来实现数据绑定的一种技术
-
2.基本思想: 通过Object.defineProperty()来监视DATA中的所有的(所有层次)属性,一旦数据发生变化就去更新页面
-
observe
function Observer(data) {
//在Observer实例上暂存data
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
var me = this;
//对data里所有的属性名进行遍历
Object.keys(data).forEach(function(key) {
me.convert(key, data[key]);
});
},
convert: function(key, val) {
//为每个属性增加响应式
this.defineReactive(this.data, key, val);
},
defineReactive: function(data, key, val) {
//为data中所有层次的属性都创建一个dep实例
var dep = new Dep();
//递归遍历data中所有层次的属性
var childObj = observe(val);
//为原有属性新增get和set方法(数据劫持)
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 不能再define
get: function() {
if (Dep.target) {
dep.depend();
}
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 新的值是object的话,进行监听
childObj = observe(newVal);
// 通知订阅者
dep.notify();
}
});
}
};
function observe(value, vm) {
//判断value是否存在或者value的数据类型是否为object(递归的终止条件)
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
var uid = 0;
function Dep() {
//没创建一个dep都会给这个dep增加一个独立的标识
this.id = uid++;
this.subs = []; //watcher
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
depend: function() {
Dep.target.addDep(this);
},
removeSub: function(sub) {
var index = this.subs.indexOf(sub);
if (index != -1) {
this.subs.splice(index, 1);
}
},
//通知所有的watcher
notify: function() {
//遍历subs中所有的watcher的实例
this.subs.forEach(function(sub) {
// 每一个watcher的实例调用update方法
sub.update();
});
}
};
Dep.target = null;
mvvm
function MVVM(options) {
//给实例新增一个$options属性,.并且把传递过来的配置进行暂存
this.$options = options;
//在实例上新增一个_data 保存传递过来的data数据
var data = this._data = this.$options.data;
//保存this 为了之后使用this的时候保证this指向的正确性
var me = this;
//通过Object.keys取出data中每一项数据的属性名,然后遍历调用_proxy方法
Object.keys(data).forEach(function(key) {
// 数据代理
me._proxy(key);
});
//为data所有数据进行劫持 结合订阅发布模式
observe(data, this);
//增加模版解析
this.$compile = new Compile(options.el || document.body, this)
}
MVVM.prototype = {
$watch: function(key, cb, options) {
new Watcher(this, key, cb);
},
_proxy: function(key) {//实现数据代理
var me = this;//暂存this 保证this的指向正确 这里的this还是实例vm
//通过defineProperty方法在实例(vm)上新增所有与data中属性所对应属性,并且为该属性添加get和set方法
Object.defineProperty(me, key, {//vm.name
configurable: false,
enumerable: true,
get: function proxyGetter() {
//实现了vm代理data中数据的读操作
return me._data[key];
},
set: function proxySetter(newVal) {//vm.name = "bb"
//实现了vm代理data中数据的写操作
me._data[key] = newVal;
}
});
}
};