您现在的位置是:首页 > Javascript
当前栏目
实现一个mvvm
2023-04-18 14:53:18 时间
最近在团队内做了一次vue原理分享,现场手写了一个乞丐版mvvm,这里记录一下这个mvvm实现的过程。
源码:https://github.com/keller35/mvvm
这个mvvm是基于发布订阅模式实现(也是vue本身的实现原理),最终达到的效果如下:
使用方式也跟vue一样:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>mvvm</title>
</head>
<body>
<div id="app">
<input type="text" v-model="text">
{{ text }}
<button @click="reset" style="display:block;">重置</button>
</div>
<script src="./index.js"></script>
<script>
var vm = new Mvvm({
el: 'app',
data: {
text: 'hello world'
},
methods: {
reset() {
this.text = '';
},
},
});
</script>
</body>
</html>
实现很简单:
class Mvvm {
constructor(options) {
const { el, data, methods } = options;
this.methods = methods;
this.target = null;
// 初始化dispatcher
this.observe(this, data);
// 初始化watcher
this.compile(document.getElementById(el));
}
observe(root, data) {
for (const key in data) {
this.defineReactive(root, key, data[key]);
}
}
defineReactive(root, key, value) {
if (typeof value == 'object') {
return this.observe(value, value);
}
const dep = new Dispatcher();
Object.defineProperty(root, key, {
set(newValue) {
if (value == newValue) return;
value = newValue;
// 发布
dep.notify(newValue);
},
get() {
// 订阅
dep.add(this.target);
return value;
}
});
}
compile(dom) {
const nodes = dom.childNodes;
for (const node of nodes) {
// 元素节点
if (node.nodeType == 1) {
const attrs = node.attributes;
for (const attr of attrs) {
if (attr.name == 'v-model') {
const name = attr.value;
node.addEventListener('input', e => {
this[name] = e.target.value;
});
this.target = new Watcher(node, 'input');
this[name];
}
if (attr.name == '@click') {
const name = attr.value;
node.addEventListener('click', this.methods[name].bind(this));
}
}
}
// text节点
if (node.nodeType == 3) {
const reg = /{{(.*)}}/;
const match = node.nodeValue.match(reg);
if (match) {
const name = match[1].trim();
this.target = new Watcher(node, 'text');
this[name];
}
}
}
}
}
class Dispatcher {
constructor() {
this.watchers = [];
}
add(watcher) {
this.watchers.push(watcher);
}
notify(value) {
this.watchers.forEach(watcher => watcher.update(value));
}
}
class Watcher {
constructor(node, type) {
this.node = node;
this.type = type;
}
update(value) {
if (this.type == 'input') {
this.node.value = value;
}
if (this.type == 'text') {
this.node.nodeValue = value;
}
}
}
原理:
- 最根本的原理很简单,无非是基于发布订阅的消息通知模式,消息发出方来自mvvm中modal层的变法,而订阅方来自view层。
- modal层的变化,是通过对data设置setter来实现响应式,只要数据发生变化,通知所有订阅者。
- view层的订阅,则是在compile阶段,compile会对所有数据依赖进行收集,然后在getter中注册监听。
相关文章
- SQL Server数据挖掘之如何实现Web路径流挖掘
- iOS数据优化之处理HTML字符串
- 2017-2018年Hybrid APP混合跨平台开发的一些经验和总结
- SQL Server 2008的BI组件SSAS使用详解
- iOS屏幕适配实践浅谈
- 滴滴开源Web移动端组件库cube-ui 独特技术大幅优化性能
- 如何启用Master Data Services的Web服务
- 【直播】DCloud CTO崔红保:如何玩转H5网站快速转换成App?
- 打造丝般顺滑的H5翻页库
- 前端:常见的6种HTML5错误用法
- 聊聊全站HTTPS带来的技术挑战
- 如何做到兼顾安全与性能?电商网站HTTPS优化探索与实践
- WePhone创始人自杀 称遭前妻勒索千万
- 为何你会被强插广告?谈HTTPS连接的那些事
- 移动H5首屏秒开优化方案探讨
- 51CTO开发者社群管理员招募第二期圆满结束
- 挨踢部落故事汇(24):扎根技术圈归属感爆棚
- 基于JavaScript语言的快速物联网开发架构
- 干货分享:让你分分钟学会JS闭包
- Web开发中的响应式图片处理