首先我们先大概讲一下思路
我们需要5个类 可以理解成无人类 不同人拥有不同的技能
1. mvvm 用来整合 可以理解成boss
2. compiler 用来把数据编译到指定的元素
例如我们给一个input添加了v-model='person.name'属性 那么compiler要做的就是找到person.name对应的值
并把这个值赋给input的value
3. watcher 用来观察data中每一个属性 如果这个属性对应的属性值发生变化 就会重新渲染视图
4. dep 用来存储每个属性对应的所有watcher 如果属性对应的属性值发生变化 就通知watcher属性值发生了变化
5. observer 用来劫持data中所有的属性 让每一个属性都拥有一个dep 如果属性对应的属性值发生变化 就会让
dep通知这个属性对应的所有watcher watcher就会更新所有用到这个属性的视图
总结:
=> 数据发生变化
=> observer监听到了
=> 告诉这个属性对应的dep
=> dep通知里面所有的watcher
=> watcher完成视图更新
----------------------------------------------------------------------------
class Observer {
constructor(data) {
if (this.isObject(data)) {
this.observer(data);
}
}
isObject(obj) {
return ({}).toString.call(obj) === '[object Object]';
}
observer(data) {
Object.keys(data).forEach(key => {
if (this.isObject(data[key])) {
this.observer(data[key]);
}
this.definedRective(data, key, data[key]);
})
}
definedRective(obj, key, value) {
let dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
Dep.target && dep.addSubs(Dep.target);
return value;
},
set(newValue) {
if (value !== newValue) {
value = newValue;
dep.notify();
}
}
})
}
}
--------------------------------------------------------------------------
class Dep {
constructor() {
this.subs = [];
}
addSubs(watcher) {
this.subs.push(watcher);
}
notify() {
this.subs.forEach(watcher => watcher.update());
}
}
-----------------------------------------------------------------------------------
class Watcher {
constructor(data, expr, cb) {
this.data = data;
this.expr = expr;
this.cb = cb;
this.getValue();
}
getValue() {
Dep.target = this;
const value = this.expr.split('.').reduce((a, current) => a[current], this.data);
Dep.target = null;
return value;
}
update() {
this.cb()
}
}
------------------------------------------------------------------------------------------------------
class Compiler {
constructor(el, data) {
this.el = this.isElement(el) ? el : document.querySelector(el);
this.data = data;
if (this.el) {
//如果确实存在这个元素 我们在进行编译
//把所有元素插入文档碎片中
this.fragment = this.nodeToFragment(this.el);
//把数据编译到对应的元素上
this.compiler(this.fragment);
//把编译好的文档碎片插入到el上
this.el.appendChild(this.fragment);
}
}
//辅助方法
isElement(node) {
return node.nodeType === 1;
}
isDirective(name) {
return name.includes('v-');
}
//核心方法
nodeToFragment(node) {
const fragment = document.createDocumentFragment();
while(node.firstChild) {
fragment.appendChild(node.firstChild);
}
return fragment;
}
compiler(fragment) {
const childNodes = fragment.childNodes;
childNodes.forEach(node => {
if (this.isElement(node)) {
//是元素节点
this.compilerElement(node);
this.compiler(node);
}else {
//是文本节点
this.compilerText(node);
}
})
}
compilerElement(node) {
const attrs = node.attributes;
Array.from(attrs).forEach(attr => {
if (this.isDirective(attr.name)) {
const expr = attr.value;
const directiveName = attr.name.split('-')[1];
compilerUtil[directiveName](node, this.data, expr);
}
})
}
compilerText(node) {
const text = node.textContent;
const reg = /\{\{([^}]+)\}\}/g;
if (reg.test(text)) {
compilerUtil.text(node, this.data, text);
}
}
}
compilerUtil = {
model(node, data, expr) {
const value = this.getVal(data, expr);
this.updater.modelUpdater(node, value);
new Watcher(data, expr, () => {
this.updater.modelUpdater(node, this.getVal(data, expr));
})
//双向绑定
node.oninput = e => {
const newValue = e.target.value;
this.setValue(data, expr, newValue);
}
},
text(node, data, text) {
const value = this.getText(data, text)
this.updater.textUpdater(node, value);
text.replace(/\{\{([^}]+)\}\}/g, (...arg) => {
new Watcher(data, arg[1].trim(), () => {
this.updater.textUpdater(node, this.getText(data, text));
})
})
},
updater: {
modelUpdater(node, value) {
node.value = value;
},
textUpdater(node, value) {
node.textContent = value;
}
},
getVal(data, expr) {
return expr.split('.').reduce((a, current) => a[current], data);
},
getText(data, text) {
return text.replace(/\{\{([^}]+)\}\}/g, (...arg) => {
return this.getVal(data, arg[1].trim())
})
},
setValue(data, expr, value) {
const keyArr = expr.split('.');
keyArr.reduce((a, current, index) => {
if (index === keyArr.length - 1) {
a[current] = value;
}
return a[current];
}, data)
}
}
class MVVM {
constructor({ el, data }) {
this.$el = el;
this.$data = data;
if (this.$el && this.$data) {
//如果用户传了el和data 我们才进行编译
//数据劫持
new Observer(this.$data);
//数据编译到元素上
new Compiler(this.$el, this.$data);
}
}
}
简单实现vue的响应式和双向绑定
最新推荐文章于 2023-03-27 15:09:48 发布