发布订阅模式

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

/*
* 发布订阅
**/
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. Web前端入门第 32 问:CSS background 元素渐变背景用法全解

    渐变背景在 CSS 里面就是一个颜色到另一个颜色渐渐变化的样子. 本文示例中,盒子基础样式: .box { margin: 20px; padding: 20px; border: 10px dash ...

  2. Python科学计算系列7—微分方程

    1.可分离变量方程 例1:求下列微分方程法通解 先化简此方程如下: 代码如下: from sympy import * x = symbols('x') f = symbols('f', cls=Fu ...

  3. 🎀EXCEL-时间函数

    简介 在Excel中,时间函数用于处理和操作日期和时间数据; 以下是Excel中常用的时间函数及其常见应用场景的总结. 函数 时间函数基础 TIME 语法:TIME(hour, minute, sec ...

  4. eolinker请求参数:提交参数JSON转换格式不正确的解决方法

    当某个接口的提交参数类型为"array"时,该接口被自动化测试调用会转换成text类型. 导致执行测试的时候,整个参数转化json格式不正确 解决方法是在  格式不正确的项后面 配 ...

  5. .NET周刊【4月第1期 2025-04-06】

    国内文章 35岁程序员的中年求职记:四次碰壁后的深度反思 https://www.cnblogs.com/minily/p/18803259 文章探讨程序员的35岁危机,指出这一问题确实存在,但也有其 ...

  6. <HarmonyOS第一课10>ArkUI进阶#鸿蒙课程##鸿蒙生态#

    课程介绍 <HarmonyOS第一课:ArkUI进阶>是专为HarmonyOS开发者设计的课程,旨在提升开发者在ArkUI框架中的布局技能.课程将重点讲解如何进行布局性能优化,包括精简节点 ...

  7. ubuntu下搭建Gitea代码托管平台

    在 Ubuntu 上部署 Gitea 是设置本地代码托管平台的简洁和高效选择.以下是详细步骤: 步骤 1:更新系统 sudo apt update && sudo apt upgrad ...

  8. Linux系列:聊一聊 SystemV 下的进程间共享内存

    一:背景 1. 讲故事 昨天在分析一个 linux 的 dump 时,看到了这么一话警告,参考如下: 0:000> !eeheap -gc *** WARNING: Unable to veri ...

  9. VMware NSX Manager SSL证书更新

    安装 NSX 后,管理器节点和集群具有自签名证书.证书有效期为825天,到期后需要进行证书重新更新.如图所示,本环境中此次将有三个类型的证书即将到期需要替换:1.NSX 联合身份验证 PI(Local ...

  10. MySQL SQL语句书写顺序和执行顺序

    目录 SQL语句书写顺序和执行顺序 MySql执行顺序理解 实例 知识扩展 on和where的区别 limit 分页 结束语 Reference SQL语句书写顺序和执行顺序 (7) SELECT ( ...