发布订阅模式

我把发布订阅的实现类单独提出来,这样代码看起来简洁

/*
* 发布订阅
**/
class Pubsub {
static instance = null; // 单例
static getInstance() {
if (Pubsub.instance == null) {
Pubsub.instance = new Pubsub;
}
return Pubsub.instance;
} // 注册的事件和处理器关联集合
eventAndHandel = {}; // 触发
emit(eventName, params) {
if (this.eventAndHandel.hasOwnProperty(eventName)) {
this.eventAndHandel[eventName].forEach(hander => {
hander(params)
});
}
} // 注册or订阅
on(eventName, cbc) {
if (!this.eventAndHandel.hasOwnProperty(eventName)) {
this.eventAndHandel[eventName] = [cbc]
} else {
this.eventAndHandel[eventName].push(cbc)
}
}
}

第一版本 es5

最普通的语法

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head> <body>
<div>
<h1 d-bind="name"></h1>
<span d-bind="name"></span>:<span d-bind="age"></span>
</div>
<div>
姓名:<input d-model="name" type="text"></br>
性别:<input d-model="age" type="text"></br>
</div>
<!-- <script src="./lodash.js"></script> -->
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
<script src="./helper.js"></script>
<script>
// ---------------------一个vue实例开始---------------------------- const data = {
name: '丁少华',
age: 20
}; // v-model的实现
const models = document.querySelectorAll(`[d-model]`);
models.forEach(item => {
const v = item.getAttribute('d-model');
item.oninput = function ({ target: { value } }) {
data[v] = value;
} }) // 双向绑定的实现
const pubsub = Pubsub.getInstance();
const data_ = _.cloneDeep(data); for (const key in data) {
Object.defineProperty(data, key, {
set(newValue) {
data_[key] = newValue;
pubsub.emit('vm', {
id: key,
value: newValue
})
},
get() {
return data_[key];
}
})
}
pubsub.on('vm', ({ id, value }) => {
// 给普通节点复制
const binds = document.querySelectorAll(`[d-bind=${id}]`);
binds.forEach(item => {
item.innerText = value;
}) // 给表单控件赋值
const models = document.querySelectorAll(`[d-model=${id}]`);
models.forEach(item => {
item.value = value;
})
}); // 初始化赋值 for (const key in data) {
data[key] = data[key];
} </script>
</body>
</html>

第二版 es6

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head> <body>
<div>
<h1 d-bind="name"></h1>
<span d-bind="name"></span>:<span d-bind="age"></span>
</div>
<div>
姓名:<input d-model="name" type="text"></br>
性别:<input d-model="age" type="text"></br>
</div>
<script>
// ---------------------一个vue实例开始----------------------------
const data = {
name: '丁少华',
age: 20
}; // ------------双向绑定的实现---------------
const pubsub = Pubsub.getInstance();
const data_ = new Proxy(data,{
set(target, property, value, receiver){
Reflect.set(...arguments);
pubsub.emit('vm', {
id: property,
value: value
})
},
get(){
return Reflect.get;
}
})
pubsub.on('vm', ({ id, value }) => {
// 给普通节点复制
const binds = document.querySelectorAll(`[d-bind=${id}]`);
binds.forEach(item => {
item.innerText = value;
}) // 给表单控件赋值
const models = document.querySelectorAll(`[d-model=${id}]`);
models.forEach(item => {
item.value = value;
})
console.log(data_);
}); // 初始化赋值
for (const key in data) {
data_[key] = data[key];
} // ---------v-model的实现-----------------
const models = document.querySelectorAll(`[d-model]`);
models.forEach(item => {
const v = item.getAttribute('d-model');
item.oninput = function ({ target: { value } }) {
data_[v] = value;
} }) </script>
</body> </html>

可以看到proxy很方便,我也不用使用lodash的深拷贝来进行隔离元数据了

vue编译模板

const compileHandel = (el, data) => {
const childNodes = el.childNodes;
const reg = /\{\{(.*)\}\}/; // 表达式文本
childNodes.forEach(node => {
const text = node.textContent;
const isElementNode = node.nodeType == 1; // 按元素节点方式编译
if (isElementNode) {
const nodeAttrs = node.attributes;
for (const iterator of nodeAttrs) { //argument都实现了iterator接口,所以可以for of
const { name: attrName, value: exp } = iterator;
if (attrName.indexOf('v-') == 0) {
const dir = attrName.substring(2);
if (dir.indexOf('on') === 0) { // 事件指令
compileHandelHelper.eventDirective(data,node, exp, dir);
} else { // 普通指令
if(dir === 'model'){
compileHandelHelper.modelDirective(data,node, exp);
}
}
}
}
} else {
reg.test(text) && compileHandelHelper.mastache(data, node, RegExp.$1.trim());
}
// 遍历编译子节点
if (node.childNodes && node.childNodes.length) {
compileHandel(node, data);
}
})
} const compileHandelHelper = {
mastache(data, node, txt) { // 解析双大括号
node.textContent = '123';
},
eventDirective() { // 解析事件指令 },
modelDirective(data, node, exp) { // 解析v-model指令
node.oninput = function ({ target: { value } }) { }
}
} export default (el, data) => {
compileHandel(el, data);
}

实现一个双向绑定和v-model的更多相关文章

  1. 用ES6的class模仿Vue写一个双向绑定

    原文地址:用ES6的class模仿Vue写一个双向绑定 点击在线尝试一下 最终效果如下: 构造器(constructor) 构造一个TinyVue对象,包含基本的el,data,methods cla ...

  2. 自己实现一个双向绑定的Vue

    我们知道双向绑定是Vue的核心之一,接下来我们自己仿照Vue实现一个基本的功能. 项目代码在GitHub上: https://github.com/zhangKunUserGit/zk-vue

  3. Vue - 如何实现一个双向绑定

    JS - 如何实现一个类似 vue 的双向绑定 Github JS 实现代码 先来看一张图: 这张图我做个简要的描述: 首先创建一个实例对象,分别触发了 compile  解析指令 和 observe ...

  4. Vue双向绑定原理,教你一步一步实现双向绑定

    当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...

  5. 前端MVVM框架avalon揭秘 - 双向绑定原理

    avalon大家可能不熟悉,但是Knockout估计或多或少听过用过,那么说说KO的几个概念 监控属性(Observables)和依赖跟踪(Dependency tracking) 声明式绑定(Dec ...

  6. Angular系列----AngularJS入门教程05:双向绑定(转载)

    在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f ...

  7. Angular数据双向绑定

    Angular数据双向绑定 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.Angul ...

  8. AngularJS入门讲解2:过滤器和双向绑定

    我们在上一课做了很多基础性的训练,接下来,我们讲一些难点的知识点,首先,讲一下如何实现一个全文检索功能: <html ng-app> <head> ... <script ...

  9. 剖析Vue原理&实现双向绑定MVVM-1

    本文能帮你做什么?1.了解vue的双向数据绑定原理以及核心代码模块2.缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考 ...

  10. 很easy的js双向绑定框架(二):控制器继承

    初衷 上一篇已经实现了数据的双向绑定,但model的控制范围是整个文档.在实际project中必需要有作用范围,以便做ui模块的拆分. 这一篇,我们希望实现像angularjs一样的控制器继承: 1. ...

随机推荐

  1. 阿里云域名+Github配置Pages

    最近用Flutter开发了一个新的应用,准备上架AppStore的时候遇到一个问题,就是通过什么途经能把自己的隐私html和官网html通过链接都显示出来呢? 经过一番的查找和研究后就准备用Githu ...

  2. Mybatis的*Dao.XML中的配置与其对应的接口、resultMap的运用

    例子. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC &quo ...

  3. 使用CAMEL创建第一个Agent Society

    CAMEL介绍 CAMEL 是一个开源社区,致力于探索代理的扩展规律.相信,在大规模研究这些代理可以提供对其行为.能力和潜在风险的宝贵见解.为了促进这一领域的研究,实现了并支持各种类型的代理.任务.提 ...

  4. MySQL 中的数据排序是怎么实现的?

    MySQL 中的数据排序实现 在 MySQL 中,数据排序是通过 ORDER BY 子句实现的,主要涉及 文件排序 和 索引排序 两种方式. 1. 排序的基本原理 MySQL 的排序实现分为以下两种情 ...

  5. Java 中的字符串常量池和运行时常量池

    Java 中的字符串常量池和运行时常量池 1. 字符串常量池(String Constant Pool) 定义 字符串常量池是 JVM 内存中专门用于存储字符串字面量和通过 intern() 方法加入 ...

  6. 题解:P10862 [HBCPC2024] Spicy or Grilled?

    题目翻译: 有 nnn 个人,我们为他们买了香辣鸡肉汉堡套餐,每份是 aaa 元,可是里面有 xxx 人不能吃辣,于是还准备了 xxx 份烤鸡汉堡套餐,每份是 bbb 元,求总共要多少元. 解题思路 ...

  7. Kreuzberg:本地OCR+多格式解析!Kreuzberg如何用Python暴力提取30+文档格式?程序员看完直呼内行!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 我们经常需要从各种不同类型的文档中提取文本内容,无论是办公文档.图像还是PDF文件.而Kre ...

  8. JavaScript编程实践:打造优雅健壮的代码

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  9. C#之使用任务并行库

    .NET Framework4.0引入了一个新的关于异步操作的API,它叫做任务并行库(Task Parallel Library,简称TPL).TPL的核心是任务,一个任务代表一个异步操作,该操作可 ...

  10. 安装Scikit-learn 0.24.2版本

    安装Scikit-learn 0.24.2版本 Anaconda 安装scikit-learn是0.19版本,目前最新版本是0.24.2,之前的版本与现在的版本之间差距还是比较大,因此有必要对scik ...