这几天看了一些vue 的相关源码,小结一下。
vue工作机制
vue 响应式的原理defineProperty
class KVue {
constructor(options) {
this._data = options.data;
this.observer(this._data);
}
observer(value) {
if (!value || typeof value !== "object") {
return; }
Object.keys(value).forEach(key => {
this.defineReactive(value, key, value[key]);
}); }
defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true /* 属性可枚举 */, configurable: true /* 属性可被修改或删除 */, get() {
return val;
},
set(newVal) {
if (newVal === val) return;
this.cb(newVal);
} });
}
cb(val) { console.log("更新数据了", val);
} }
let o = new KVue({
data: {
test: "I am test."
}
});
o._data.test = "hello,kaikeba";
依赖收集与追踪
new Vue({
template:
`<div>
<span>{{text1}}</span>
<span>{{text2}}</span>
</div>`
data: {
text1: 'name1'
},
created () {
this.text1 = 'come on'
}
})
text1 被修改,所以视图更新,但是text2视图没有用到,这里就用到了依赖收集
// 依赖收集小朋友 class Dep {
constructor () {
// 存数所有的依赖
this.deps = []
}
// 在deps中添加一个监听器对象 addDep (dep) {
this.deps.push(dep)
}
// 通知所有监听器去更新视图 notify () {
this.deps.forEach((dep) => {
dep.update()
}) }
}
class Watcher {
constructor () {
// 在new一个监听器对象时将该对象赋值给Dep.target,在get中会用到
Dep.target = this
}
// 更新视图的方法 update () {
console.log("视图更新啦~") }
}
我们在增加了一个Dep类的对象,用来收集Watcher对象。读数据的时候,会触发reactiveGetter函数把当前的Watcher对象存放在Dep.target中,收集到Dep类中去。
写数据的时候,则会触发reactiveGetter方法,通知Dep类调用notify来触发所有watcher对象的update方法更新对应视图
constructor(options) {
this._data = options.data
this.observer(this._data)
// 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 new Watcher();
// 在这里模拟render的过程,为了触发test属性的get函数 console.log('模拟render,触发test的getter', this._data.test);
}
defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 将Dep.target(即当前的Watcher对象存入Dep的deps中) dep.addDep(Dep.target)
return val
},
set: function reactiveSetter(newVal) {
if (newVal === val) return
// 在set的时候触发dep的notify来通知所有的Watcher对象更新视图
dep.notify()
}
}) }
编译complie
核心逻辑 获取dom,遍历dom,获取{{}}, h- 和@开头的,设置响应式
// 编译过程
compile(el) {
const childNodes = el.childNodes;
Array.from(childNodes).forEach(node => {
// 类型判断
if (this.isElement(node)) {
// 元素
// console.log('编译元素'+node.nodeName);
// 查找k-,@,:
const nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach(attr => {
const attrName = attr.name; //属性名
const exp = attr.value; // 属性值
if (this.isDirective(attrName)) {
// k-text
const dir = attrName.substring(2);
// 执行指令
this[dir] && this[dir](node, this.$vm, exp);
}
if (this.isEvent(attrName)) {
const dir = attrName.substring(1); // @click
this.eventHandler(node, this.$vm, exp, dir);
}
});
} else if (this.isInterpolation(node)) {
// 文本
// console.log('编译文本'+node.textContent);
this.compileText(node);
}
// 递归子节点
if (node.childNodes && node.childNodes.length > 0) {
this.compile(node);
}
});
}