vue模板解析(双大括号的解析)以及双向绑定原理
1.首先根据正则获取组件中所有的{{}},将{{}}里的值取出在data中取出对应的值,以文本节点的方式放上去。4.然后根据 render function 方法最终返回string。5.最后通过vue的_update方法转化成对应的DOM。3.AST会根据generate得到render函数。2.vue有内置的编译器,会将组件转译成AST。...
1.首先根据正则获取组件中所有的{{}},将{{}}里的值取出在data中取出对应的值,以文本节点的方式放上去。
2.vue有内置的编译器,会将组件转译成AST
3.AST会根据generate得到render函数
4.然后根据 render function 方法最终返回string
5.最后通过vue的_update方法转化成对应的DOM
这是网上看到的,结合自己的理解
1.在dom中遍历所有的包含{{}}的元素
2.遍历这些元素,用正则表达式替换{{}}里面的属性,以文本节点的方式替换原来的属性。
3.vue的生命周期update()钩子函数更新dom的渲染效果。
双向绑定原理
1.将data中的数据指向Vue对象的实例(数据代理)。 有没有想过为什么vue中 this.可以访问data中的变量?
通过this能直接获取到data,是因为data里的属性最终被存储到new Vue的实例(vm)上的_data对象中,我们访问this.xxx,实际上是访问通过Object.defineProperty代理后的this._data.xxx。
简易实现:
function Person(options) {
let vm = this;
vm.$options = options;
if (options.data) {
initData(vm);
}
if (options.methods) {
initMethods(vm, options.methods);
}
}
function initData(vm) {
let data = vm._data = vm.$options.data;
let keys = Object.keys(data);
let len = keys.length;
while(len--) {
let key = keys[len];
proxy(vm, "_data", key);
}
}
function proxy(vm, sourceKeys, key) {
Object.defineProperties(vm, sourceKeys, {
enumerable: true,
configurable: true,
get: function() {
return vm[sourceKeys][key];
},
set: function(val) {
return vm[sourceKeys][key] = val;
}
})
}
function initMethods(vm, methods) {
for (let key in methods) {
vm[key] = typeof methods[key] === "function" ? methods[key].bind(vm) : noop;
}
}
function noop(a, b, c) { }
let p1 = new Person({
data: {
name: "123",
age: 28
},
methods: {
sayName() {
console.log("I am" + this.name);
}
}
})
console.log(p1.name); // 123
p1.sayName(); // 'I am 123'
观察者模式(数据劫持)
Vue实现观察者模式主要通过三个类:Observer类、Watcher类、Dep类
- Observer:通过
defineProperty
给数据添加get
和set
方法并在get
中收集依赖,在set
中通知更新 - Watcher:观察数据变化(接收
Dep
的notify
发出的通知),执行回调 - Dep:一个可观察对象,收集订阅(在
Observer
的get
时收集)、发送通知(在Observer
的set
时发布)
var app = new Vue({
el: '#J_app',
data: {
message: 'hello world'
},
mounted: function(){
console.log(this.message)
}
})
app.message = 'hellp vue'
console.log(`'app.message:' ${app.message}`)
我们给data
添加了message
属性,然后,在数据初始化过程中:Observer
通过defineProperty
给message
设置get/set
属性,并在get
中调用Dep
的depend
方法,将message
添加为可观察对象,之后在Watcher
中,对message
进行求值,调用其get
方法,同时将Watcher
实例添加到可观察对象message
的观察者数组里;同时,在set
时,通过Dep
的notify
发布message
更新的事件,通知调用观察者数组中的update
方法。
在Vue中,上面流程中Observer
负责的部分是通过defineReactive$$1
实现的,可以简写如下:
/*
*参数含义:
*obj: 需要添加属性的对象
*key: 要添加的属性名
*value: 要添加的属性名对应的值
*customSetter: 在非生产环境,一些自定义的错误警告
*shallow: 属性值是否添加到代理到vm上
*/
function defineReactive(obj, key, value, customSetter, shallow) {
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
var getter = property && property.get;
var setter = property && property.set;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function () {
var value = getter ? getter.call(obj) : val;
//收集依赖 交由Dep类负责
return value
},
set: function () {
var value = getter ? getter.call(obj) : val;
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
//开发环境属性检查并警告
if ("development" !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
observe(newVal);
//发布更新消息
dep.notify();
}
})
}
这里的代码相对好理解,get
方法里的收集依赖其实也是一个曲折的过程。在watcher中,每添加一个新的watcher实例时,都会对相应的对象进行求值,也就是会主动触发一次defineReactive
的get
方法,然后就在get
方法里进行依赖收集。同时,数据有变更时,也会触发defineReactive
的set
方法,然后在set
方法里发布数据更新的消息dep.notify()
,然后dep.notify
里会对调相应的watcher进行update
。
Dep
类负责依赖收集和观察者存储,来看下Dep
类的代码:
/**
* 每个Dep的实例都是一个可观察对象,可以被多个观察者订阅
*/
var uid = 0;
var Dep = function Dep () {
this.id = uid++;
this.subs = [];
};
Dep.prototype.addSub = function addSub (sub) {
this.subs.push(sub);
};
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub);
};
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
Dep.prototype.notify = function notify () {
// stabilize the subscriber list first
var subs = this.subs.slice();
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
// the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.
Dep.target = null;
var targetStack = [];
function pushTarget (_target) {
if (Dep.target) { targetStack.push(Dep.target); }
Dep.target = _target;
}
function popTarget () {
Dep.target = targetStack.pop();
}
初始每个可观察对象dep
都有一个id和观察者数组(subs),可以对当前id的可观察对象:添加观察者(addSub
)、移除观察者(removeSub
)、设置为可观察对象(depend
)、通知变更到观察者们(notify
),添加和移除观察者相对简单,往数组push
和splice
即可;设置为可观察对象调用的是Dep.target.addDep(this)
,Dep.target
其实是Watcher
的实例,下节再看;通知变更到观察者们只是执行了是观察者们的update
方法。
总结一下:
- new Vue,执行初始化
- 挂载
$mount
方法,通过自定义Render方法、template、el等生成Render函数 - 通过Watcher监听数据的变化
- 当数据发生变化时,Render函数执行生成VNode对象
- 通过patch方法,对比新旧VNode对象,通过DOM Diff算法,添加、修改、删除真正的DOM元素
更多推荐
所有评论(0)