实现简易版本的MVVM框架(Vue)

整体流程图

image.png

在10~15分钟完成的自定义Vue

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>EasyFrame</title>
<script src="./easy-frame.js"></script>
</head>
<body>
<div id="app">
<p>{{count}}</p>
<button @click="add">+1</button>
</div>
<script>
const app = new EasyFrame({
el: "#app",
data: {
count: 0,
},
methods: {
add() {
console.log("add");
this.count++;
},
},
});
</script>
</body>
</html>

EasyFrame

function EasyFrame(options) {
this.$options = options;
this.$el = this.$options.el;
this.$data = this.$options.data;
this.$methods = this.$options.methods;
observe(this.$data);
proxy(this);
compile(this.$el, this);
}

observe

function observe(obj) {
function defineReactive(obj, key, val) {
observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
// 绑定后清空target,下个对象响应化时,对应绑定
Dep.target && dep.addDep(Dep.target);
console.log("getter");
return val;
},
set(newVal) {
if (newVal != val) {
console.log("setter");
observe(val);
val = newVal;
dep.notify();
}
},
});
}
Object.keys(obj).forEach((key) => defineReactive(obj, key, obj[key]));
}

compile

function compile(el, vm) {
function traverse(nodes) {
nodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
Array.from(node.attributes).forEach((attr) => {
if (attr.name.startsWith("@")) {
const attrName = attr.name.substring(1);
const attrValue = attr.value;
node.addEventListener(attrName, vm.$methods[attrValue].bind(vm));
}
});
}
if (
node.nodeType === Node.TEXT_NODE &&
/\{\{(.*)\}\}/.test(node.textContent)
) {
node.textContent = vm[RegExp.$1];
new Watcher(vm, RegExp.$1, function (val) {
node.textContent = val;
});
}
if (node.childNodes && node.childNodes.length > 0) {
traverse(node.childNodes);
}
});
}
const childNodes = document.querySelector(el).childNodes;
traverse(childNodes);
}

Watcher&Dep

function Dep() {
this.watchers = [];
this.addDep = function (watcher) {
this.watchers.push(watcher);
};
this.notify = function () {
this.watchers.forEach((watcher) => watcher.update());
};
}
function Watcher(vm, key, fn) {
// 触发依赖收集
Dep.target = this;
vm[key];
Dep.target = null;
// 回调最新val
this.update = function () {
fn && fn(vm[key]);
};
}

proxy

function proxy(vm) {
Object.keys(vm.$data).forEach((key) => {
Object.defineProperty(vm, key, {
get() {
return vm.$data[key];
},
set(val) {
vm.$data[key] = val;
},
});
});
}
posted @   前端小鑫同学  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示