Vue简单实现
Vue
类图
- class
- 属性
- 方法
- compile()
- compileElement()
- compileText()
- isDirective()
- isTextNode()
- isElementNode()
- …编译指令对应的 方法
实现
Vue
class Vue {
$el;
$options;
$data;
constructor(options) {
this.$options = options || {};
this.$data = options.data || {};
this.$el = typeof options.el === "string" ? document.querySelector(options.el) : options.el;
this._proxyData(this.$data);
new Observer(this.$data)
new Compiler(this);
}
_proxyData(data) {
Object.keys(data).forEach(key=>{
Object.defineProperty(this, key, {
enumerable:true,
configurable: true,
get() {
return data[key];
},
set(newValue) {
if (newValue === data[key]) return;
data[key] = newValue;
}
})
})
}
}
Observer
class Observer {
constructor(data){
this.walk(data)
}
walk(data){
if (typeof data !== "object" || !data) return;
Object.keys(data).forEach(key=>{
this.defineReactive(data, key, data[key])
})
}
defineReactive(data, key, value) {
let that = this;
let dep = new Dep();
this.walk(value);
Object.defineProperty(data, key, {
enumerable:true,
configurable:true,
get() {
Dep.target && dep.addSubs(Dep.target);
return value
},
set(newValue) {
if (newValue === value) return;
value = newValue;
that.walk(newValue);
dep.notify();
}
})
}
}
Compiler
class Compiler {
constructor(vm) {
this.el = vm.$el;
this.vm = vm;
this.compile(this.el);
}
compile (el) {
let childNodes = el.childNodes;
Array.from(childNodes).forEach(node => {
if (this.isTextNode(node)) {
this.compileText(node);
} else if (this.isElementNode(node)) {
this.compileElement(node);
}
if (node.childNodes && node.childNodes.length) this.compile(node);
})
}
compileElement (node) {
Array.from(node.attributes).forEach(attr=>{
if (!this.isDirective(attr.name)) return;
const directive = attr.name.replace('v-','');
const key = attr.value;
this.update(node, key, directive)
})
}
update(node, key, directive) {
const confirmFn = this[directive+'Updater'];
confirmFn && confirmFn.call(this, node, this.vm[key], key);
}
textUpdater(node, value, key) {
node.textContent = value;
new Watcher(this.vm, key, newValue=>{
node.textContent = newValue;
})
}
modelUpdater(node, value, key) {
node.value = value;
new Watcher(this.vm, key, newValue=>{
node.value = newValue;
})
node.addEventListener('input', ()=>{
this.vm[key] = node.value;
})
}
compileText (node) {
const value = node.textContent;
const reg = /\{\{(.+?)\}\}/;
if (reg.test(value)) {
const key = RegExp.$1.trim();
node.textContent = value.replace(reg, this.vm[key]);
new Watcher(this.vm, key, (newValue)=>{
node.textContent = value.replace(reg, newValue);
})
}
}
isDirective (attrName) {
return attrName.startsWith('v-')
}
isTextNode (node) {
return node.nodeType === 3;
}
isElementNode (node) {
return node.nodeType === 1;
}
}
Dep
class Dep {
constructor() {
this.subs = []
}
addSubs(sub) {
if (!(sub.update && typeof sub.update === 'function')) return;
this.subs.push(sub)
}
notify() {
this.subs.forEach(sub => {
sub.update();
});
}
}
Watcher
class Watcher {
constructor(vm, key, cb) {
this.vm = vm;
this.key = key;
this.cb = cb;
Dep.target = this;
this.oldValue = vm[key];
Dep.target = null;
}
update() {
let newValue = this.vm[this.key];
if (newValue === this.oldValue) return;
this.cb(newValue);
}
}
流程图
![大概流程](https://i-blog.csdnimg.cn/blog_migrate/6ab54d136935124b5af12f8a8cbb0d4f.png)