这个实现会包含以下核心部分:
- Observer: 用于监听对象属性变化
- Dep: 依赖收集器
- Watcher: 订阅者,用于更新视图
- Compile: 编译模板,解析指令
- MVVM: 主要入口类
下面是一个基本的实现:
// Observer: 监听对象属性变化
class Observer {
constructor(data) {
this.observe(data);
}
observe(data) {
if (!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key]);
this.observe(data[key]); // 递归观察子属性
});
}
defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
Dep.target && dep.addDep(Dep.target);
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 通知所有订阅者
}
});
}
}
// Dep: 依赖收集器
class Dep {
constructor() {
this.subs = [];
}
addDep(sub) {
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;
this.value = this.get();
}
get() {
Dep.target = this;
const value = this.vm.$data[this.key];
Dep.target = null;
return value;
}
update() {
const value = this.vm.$data[this.key];
if (value !== this.value) {
this.value = value;
this.cb.call(this.vm, value);
}
}
}
// Compile: 编译模板
class Compile {
constructor(el, vm) {
this.vm = vm;
this.el = document.querySelector(el);
this.fragment = null;
this.init();
}
init() {
if (this.el) {
this.fragment = this.nodeToFragment(this.el);
this.compileElement(this.fragment);
this.el.appendChild(this.fragment);
}
}
nodeToFragment(el) {
const fragment = document.createDocumentFragment();
let child = el.firstChild;
while (child) {
fragment.appendChild(child);
child = el.firstChild;
}
return fragment;
}
compileElement(el) {
const childNodes = el.childNodes;
Array.from(childNodes).forEach(node => {
if (node.nodeType === 1) {
// 元素节点
this.compile(node);
} else if (node.nodeType === 3) {
// 文本节点
const text = node.textContent;
const reg = /\{\{(.*)\}\}/;
if (reg.test(text)) {
const key = RegExp.$1.trim();
new Watcher(this.vm, key, function(value) {
node.textContent = text.replace(reg, value);
});
}
}
});
}
compile(node) {
const nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach(attr => {
const attrName = attr.name;
if (attrName.startsWith('v-')) {
const exp = attr.value;
const dir = attrName.substring(2);
if (dir === 'model') {
new Watcher(this.vm, exp, function(value) {
node.value = value;
});
node.addEventListener('input', e => {
this.vm.$data[exp] = e.target.value;
});
}
}
});
}
}
// MVVM
class MVVM {
constructor(options) {
this.$el = options.el;
this.$data = options.data;
new Observer(this.$data);
new Compile(this.$el, this);
this.proxy(this.$data);
}
proxy(data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
return this.$data[key];
},
set(newVal) {
this.$data[key] = newVal;
}
});
});
}
}
// 使用示例
const vm = new MVVM({
el: '#app',
data: {
message: 'Hello MVVM!'
}
});
这个简易的MVVM实现包含了基本的数据绑定和视图更新功能。你可以这样使用它:
<div id="app">
<input v-model="message">
<p>{{message}}</p>
</div>
<script>
const vm = new MVVM({
el: '#app',
data: {
message: 'Hello MVVM!'
}
});
</script>
关注微信公众号温暖前端,不定期分享前端知识点和前端资料↓↓↓