Observer 监听数据变化
数据劫持 发布-订阅
class Observer{
constructor(value) {
this.value = value;
this.walk(value)
}
walk(data){
//遍历所有key 监听
Object.keys(data).forEach(key => {
this.defineReactive(key,data[key])
})
}
defineReactive(key, value) {
let dep = new Dep();
let childObj = this.observe(value)
//监听属性 递归
Object.defineProperty(this.value, key, {
enumerable: true,
configurable: false,
get() {
//添加依赖 订阅
Dep.target&&dep.depend()
return value;
},
set(newVal) {
if (value == newVal) return;
value = newVal;
// 新的值是object的话,进行监听
childObj = observe(newVal);
//通知所有的依赖 发布
dep.notify();
console.log("监听到变化了。。。")
}
})
}
observe(val) {
if (typeof val != "object") return;
//继续监听
return new Observer(val);
}
}
walk 遍历obj的key 调用defineReactive 监听属性 数据已经监听了 , 需要通知订阅者 ,需要一个消息订阅器 发布 - 订阅 变化
Dep(发布-订阅)
//Dep 消息订阅器 订阅Watcher的数据依赖
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.target 添加的依赖???
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
Dep.prototype.notify = function notify () {
var subs = this.subs.slice();
//发布订阅 通知 每个订阅者,调用update()方法
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
//全局的 订阅后重置
Dep.target = null;
watch
在initMixin中 初始化完成后 如果传入el 则调用$mount
$mount =》 mountComponent =》
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
//挂载组件
function mountComponent (
vm,
el,
hydrating
) {
vm.$el = el;
//是否有render函数
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
}
callHook(vm, 'beforeMount');
var updateComponent;
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
//
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true /* isRenderWatcher */);
hydrating = false;
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm
}
//mountComponent在构造新的Watcher对象传了当前vue实例、updateComponent函数、空函数
// 订阅者初始化的时候 添加到订阅器中,调用get方法,添加
//只有初始化时才添加 添加成功后重置Dep.target
class Watcher{
constructor(vm,exp,cb) {
this.vm = vm;
this.cb = cb;
this.exp = exp;
this.depIds = {}
//触发Observe 的get 方法 在dep上添加自己
if (typeof exp === 'function') {
//updateComponent函数
this.getter = exp;
} else {
this.getter = this.parseGetter(exp.trim());
}
this.value = this.get();
}
addDep(attr) {
//每次变化都会调用run,触发getter ==》dep.depend()=》addDep 判断当前dep.id是否是新的,如果是 添加依赖 不是则不需要添加依赖
if (!this.depIds.hasOwnProperty(dep.id)) {
dep.addSub(this);
this.depIds[dep.id] = dep;
}
}
//属性变化收到通知
update() {
this.run();
}
run() {
//取最新值
let value = this.get();
//旧值
let oldVal = this.value;
if (value != oldVal) {
//更新value
this.value = value;
//执行回调 比如更新视图 执行回调
this.cb.call(vm,value,oldVal)
}
}
parseGetter(attr) {
if (/[^\w.$]/.test(attr)) return;
var exps = attr.split('.');
return (obj)=> {
for (var i = 0, len = exps.length; i < len; i++) {
if (!obj) return;
obj = obj[exps[i]];
}
return obj;
}
}
get() {
Dep.target = this; //当前订阅指向自己
let value = this.vm[exp];//触发getter 添加到自己的订阅器
//把当前的 `watcher` 订阅到这个数据持有的 `dep` 的 `watchers` 中 方便后续数据变化时能通知到watch
Dep.target = null;//重置
return value;
}
}
Compiler
class Compile{
constructor(el,vm) {
this.$el = this.isElementNode(el) ? el : document.querySelector(el);
this.$vm = vm;
this.$fragment = "";
this.init()
}
init() {
if (this.$el) {
//文档碎片
this.$fragment = this.node2Fragment(this.$el);
this.initFragment();
this.$el.appendChild(this.$fragment)
}
}
node2Fragment(el) {
let fragment = document.createDocumentFragment(),child;
while (child = el.firstChild) {
fragment.appendChild(child);
}
return fragment;
}
//解析文档碎片
initFragment() {
this.compileElement(this.$fragment)
}
//遍历所有的子节点和节点
compileElement(el) {
let childNodes = el.childNodes;
[...childNodes].forEach(node => {
var text = node.textContent;
var reg = /\{\{(.*)\}\}/;
if (this.isElementNode(node)) {
//元素节点
this.compile(node)
}else if (this.isTextNode(text)&®.test(text)) {
//文本节点 执行文本指令
this.compileText(node, RegExp.$1.trim());
}
//遍历子节点
if (node.childNodes && node.childNodes.length) {
this.compileElement(node.childNodes)
}
})
}
compileText(node, str) {
compileUtil.text(node, this.$vm, str);
}
compile(node) {
let attrs = node.attributes;
[...attrs].forEach(attr => {
let name = attr.name;
if (this.isDirective(name)) {
let exp = attr.value;
//v-on v-text
let str = name.substring(2);
if (this.isEventDirective(str)) {
//v-on:click 绑定的事件
compileUtil.eventHandler(node, this.$vm, exp, str);
} else {
compileUtil[str]&& compileUtil[str](node,this.$vm,exp,str)
}
// node.removeAttribute(attrName);
}
})
}
//是否是指令
isDirective(attr) {
return attr.indexOf('v-')==0
}
//是否方法
isEventDirective(attr) {
return attr.indexOf(on)==0
}
isElementNode(node) {
return node.type ===1
}
}
let compileUtil = {
eventHandler(node, vm, exp, str) {
//on:click
var eventType = str.split(':')[1],
fn = vm.$options.methods && vm.$options.methods[str];
if (eventType && fn) {
node.addEventListener(eventType, fn.bind(vm), false);
}
},
text(node, vm, exp, str) {
this.bind(node, vm, exp, 'text');
},
bind(node, vm, exp, attr) {
//更新函数
let updateFn = updater[attr + 'Updater'];
updateFn && updateFn(node, this._getVMVal(vm, exp));
//视图数据发生变化 通知watch 将这个指令初始化为一个订阅者,后续 exp 改变时,就会触发这个更新回调,从而更新视图
new Watcher(vm, exp, (value, oldVal) => {
updaterFn && updaterFn(node, value, oldValue);
})
},
_getVMVal(vm, exp) {
let val = vm;
//obj.name
exp = exp.split('.');
exp.forEach(v => {
val = val[v]
})
return v;
}
}
let updater = {
textUpdater(node, val) {
node.textContent = val ? val : '';
}
}
双向绑定,view-->Model Model-》view viewModel 数据变了通知视图 视图变了通知model viewModel 职责: 监听: Observer 解析: Compiler
数据监听 通过数据劫持 + 发布订阅
Observer 监听数据 如果数据变化在set中触发notify调用消息订阅器 消息订阅器遍历所有消息,通知Watcher 调用Watcher的update方法,来执行更新回调
Watcher 初始化时,添加Watcher到消息队列 便于触发时拿到数据
Compiler:
-
Compile 编译模板指令,替换模板数据, 初始化视图
-
将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器;