10分钟搞定简易MVVM
实现一个简易的 MVVM 分为这么几步来
1.类 Vue:这个类接收的是一个 options。
el属性:根元素的id
data属性:双向绑定的数据。
2.Dep 类:
subNode数组:存放所依赖这个属性 的依赖,
addSub方法:添加依赖的
removeSub方法:删除,
updata方法:遍历更新它subs中的所有依赖
target静态属性:用来表示 '当前的观察者' ,依赖收集的时候可以将它添加到dep. subNode中
静态的是指向类自身,而不是指向实例对象,主要是归属不同,这是静态属性的核心
3.observe 方法:监听数据变化(参数:data,也就是 options.data)
遍历data:使用Object.defineProperty()来重写它的get和set,
使用new Dep():实例化一个dep对象,
get时:addSub 添加 '当前的观察者Dep.target' 完成依赖收集,
set时:dep.updata 通知每一个依赖它的观察者进行更新
4.compile 方法:来将 HTML 模版和数据结合起来(参数:node 节点)。
遍历它的所有子级,判断是否有firstElmentChild
有:进行递归调用compile方法,
没有:child.innderHTML用判断是否有(/{{(.)}}/)需要双向绑定的数据,
有:new Reg('\{\{\s' + key + '\s*\}\}', 'gm')替换msg变量。
5.将 Dep.target 指向当前的这个 child,
调用this.opt.data[key]:触发这个数据的get来对当前的child进行依赖收集,
目的:下次数据变化通知child进行视图更新,
将Dep.target指为null(其实在Vue中是有一个targetStack栈用来存放target的指向的)
6.监听 document 的 DOMContentLoaded
回调函数中实例化这个Vue对象就可以了
需要注意的点:
childNodes 会获取到所有的子节点以及文本节点(包括元素标签中的空白节点)
firstElementChild 表示获取元素的第一个字元素节点,以此来区分是不是元素节点,如果是的话则调用 compile 进行递归调用,否则用正则匹配
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>MVVM</title>
</head>
<body>
<div id="app">
<h3>姓名</h3>
<p>{{name}}</p>
<h3>年龄</h3>
<p>{{age}}</p>
</div>
</body>
</html>
<script>
document.addEventListener(
"DOMContentLoaded",
function () {
let opt = { el: "#app", data: { name: "等待修改...", age: 20 } };
let vm = new Vue(opt);
setTimeout(() => {
opt.data.name = "jing";
}, 2000);
},
false
);
class Vue {
constructor(opt) {
this.opt = opt;
this.observer(opt.data);
let root = document.querySelector(opt.el);
this.compile(root);
}
observer(data) {
Object.keys(data).forEach((key) => {
let obv = new Dep();
data["_" + key] = data[key];
Object.defineProperty(data, key, {
get() {
Dep.target && obv.addSubNode(Dep.target);
return data["_" + key];
},
set(newVal) {
obv.update(newVal);
data["_" + key] = newVal;
},
});
});
}
compile(node) {
[].forEach.call(node.childNodes, (child) => {
if (!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)) {
let key = RegExp.$1.trim();
child.innerHTML = child.innerHTML.replace(
new RegExp("\\{\\{\\s*" + key + "\\s*\\}\\}", "gm"),
this.opt.data[key]
);
Dep.target = child;
this.opt.data[key];
Dep.target = null;
} else if (child.firstElementChild) this.compile(child);
});
}
}
class Dep {
constructor() {
this.subNode = [];
}
addSubNode(node) {
this.subNode.push(node);
}
update(newVal) {
this.subNode.forEach((node) => {
node.innerHTML = newVal;
});
}
}
</script>
最新文章
- 算法是什么我记不住,But i do it my way. 解一道滴滴出行秋招编程题。
- html5手机常见问题与工具分享
- Hibernate配置文件
- hdu----(5047)Sawtooth(大数相乘+数学推导)
- ulink 固件更新问题
- java 多线程 CountDownLatch用法
- duilib中控件拖拽功能的实现方法(附源码)
- Windows xp下IDT Hook和GDT的学习
- C# 中的 lock的陷阱
- Selenium如何实现dropbar移动
- 在Linux上怎么安装和配置DenyHosts工具
- 面试之get和post(转)
- Zoj 3842 Beauty of Array
- set echo on/off,set term on/off,set feedback off,set heading off命令(转)
- 一、Python3.6+PyQt5 安装
- LeetCode算法题-Keyboard Row(Java实现)
- lwip TCP client &; FreeRTOS 打开TCP 的 保活机制 LWIP_TCP_KEEPALIVE==1
- Python全栈开发之路 【第十五篇】:jQuery的介绍和选择器
- jquery easyui datagrid 空白条处理 自适应宽高 格式化函数formmater 初始化时会报错 cannot read property &#39;width&#39;||&#39;length&#39; of null|undefined
- Vue学习二:v-model指令使用方法
热门文章
- Go语言核心36讲18
- Django 接收到body后 json.loads() 报编码错误 且在报错之前打印body为空
- 【云原生 · Kubernetes】apiserver高可用
- 认证服务(keystone)
- 螺旋矩阵II-LeetCode59 考验代码能力
- 【数据库】PostgreSQL/PgSql-根据模式名和字段名查询有该字段的所有表信息【通过表元数据信息和函数实现】
- Django批量插入(自定义分页器)
- 重学c#系列——linq(2) [二十八]
- PyTorch复现GoogleNet学习笔记
- Django 连接各数据库配置汇总(sqlite3,MySql,Oracle)