Vue 2.0双向绑定原理的实现

2023-10-30

Object.defineProperty方法

vue.js是采用数据劫持结合发布-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

具体实现过程的代码如下:

  1. 定义构造函数
  function Vue(option){
    // 获取挂载节点
    this.$el = document.querySelector(option.el)
    this.$data = option.data
    this.$methods = option.methods
    // deps:所有订阅者集合
    // 目标格式 (一对多的关系):
    // {msg: [订阅者1,订阅者2,订阅者3], info: [订阅者1,订阅者2]}
    this.deps = {}
    // 调用观察者
    this.observer(this.$data)
    // 调用指令解析器
    this.compile(this.$el)
  }
  1. 定义指令解析器
Vue.prototype.compile = function (el) {
    let nodes = el.children; //获取挂载节点的子节点
    for (let i = 0; i < nodes.length; i++) {
        let node = nodes[i];
        if (node.children.length) {
            this.compile(node) //递归获取子节点
        }
        if (node.hasAttribute('l-model')) { //当子节点存在l-model指令
            let attrVal = node.getAttribute('l-model'); //获取属性值
            node.addEventListener('input', (() => {
                this.deps[attrVal].push(new Watcher(node, "value", this, attrVal)); //添加一个订阅者
                let thisNode = node;
                return () => {
                    this.$data[attrVal] = thisNode.value //更新数据层的数据
                }
            })())
        }
        if (node.hasAttribute('l-html')) {
            let attrVal = node.getAttribute('l-html'); //获取属性值
            this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者
        }
        if (node.innerHTML.match(/{{([^\{|\}]+)}}/)) {
            let attrVal = node.innerHTML.replace(/[{{|}}]/g, '');   //获取插值表达式内容
            this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者
        }
        if (node.hasAttribute('l-on:click')) {
            let attrVal = node.getAttribute('l-on:click'); //获取事件触发的方法名
            node.addEventListener('click', this.$methods[attrVal].bind(this.$data)); //将this指向this.$data
        }
    }
}
  1. 定义观察者
Vue.prototype.observer = function(data){
    for(let key in data){
        (function(that){
            let val = data[key];    //每一个数据的属性值
            that.deps[key] = [];    //初始化所有订阅者对象{msg: [订阅者], info: []}
            let watchers = that.deps[key];
            Object.defineProperty(data, key, {  //数据劫持
                get: function(){
                    return val;
                },
                set: function(newVal){  //设置值(说明有数据更新)
                    if(val !== newVal){
                        val = newVal;
                    }
                    // 通知订阅者
                    watchers.forEach(watcher=>{
                        watcher.update()
                    })
                }
            })
        })(this)
    }
}


  1. 定义订阅者
function Watcher(el, attr, vm, attrVal) {
    this.el = el;
    this.attr = attr;
    this.vm = vm;
    this.val = attrVal;
    this.update(); //更新视图
}

  1. 更新视图
Watcher.prototype.update = function () {
    this.el[this.attr] = this.vm.$data[this.val]
}

以上代码定义在一个Vue.js文件中,在需要使用双向绑定的地方引入即可。
尝试使用一下:

<!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>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <!--
        实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:
            1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
            2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
            3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
            4、mvvm入口函数,整合以上三者
    -->
    <div id="app">
        <input type="text" l-model="msg" >
        <p l-html="msg"></p>
        <input type="text" l-model="info" >
        <p l-html="info"></p>
        <button l-on:click="clickMe">点我</button>
        <p>{{msg}}</p>
    </div>

    <script>
        let vm = new Vue({
            el: "#app",
            data: {
                msg: "恭喜发财",
                info: "好好学习, 天天向上"
            },
            methods: {
                clickMe(){
                    this.msg = "我爱敲代码";
                }
            }
        })
    </script>
</body>
</html>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue 2.0双向绑定原理的实现 的相关文章

  • 用MindSpore复现VAN(Visual Attention Network)

    用MindSpore复现VAN Visual Attention Network 1 VAN 简述 2 如何复现 模型的修改 参数初始化 数据预处理 优化器 学习策略 多卡训练设置 混合精度对齐 O0 O2 O3 以及自己设置黑白名单 O1
  • vue.js:基于nodejs使用log4js输出日志文件

    一 问题 开发web项目过程中 常规的console log 只是将信息反映在了浏览器的控制台 我们经常需要输出到文本日志文件 以便开发调试和维护排查 下面介绍log4js在vue项目中的部署与使用 二 解决方案 1 安装log4j 注意

随机推荐

  • IntelliJ IDEA2022(破译版)安装教程

    首先先下载一个压缩包 里面安装包破译插件都有 通过百度网盘分享的文件 JetBrain zip 链接 https pan baidu com s 1mBQUacDIf39AJtR wGZ bw 提取码 P95C 复制这段内容打开 百度网盘A
  • 全局负载均衡(GSLB)的实现方案

    What is GSLB Global Server Load Balancing 中文 全局负载均衡 SLB Server load balancing 是对集群内物理主机的负载均衡 而GSLB是对物理集群的负载均衡 这里的负载均衡可能不
  • python驱动DDT框架应用--实战举例

    步骤 创建一个python file ddttest 引入ddt 更新ddt File Settings 双击pip 搜索框输入 ddt 实例讲解 传入一个参数 import unittest from ddt import ddt dat
  • 2022全国职业技能大赛-网络安全赛题解析总结⑨(超详细)

    2022全国职业技能大赛 网络安全赛题解析总结 自己得思路 模块A 基础设施设置与安全加固 20分 模块B 网络安全事件响应 数字取证调查和应用安全 40分 模块C CTF夺旗 攻击 20分 模块D CTF夺旗 防御 20分 有什么不懂得可
  • openstack调优

    cinder限速 为了减缓来自实例的数据访问速度的减慢 openstack block storage支持对卷数据复制带宽的速率限制 请修改cinder后端配置文件将文件卷复制带宽限制为最高100MB s The upper limit o
  • arm 关于m a r系列区别

    arm系列contex的a r m的区别 从cortex开始 分为三个系列 a系列 r系列 m系列 m系列与arm7相似 不能跑操作系统 只能跑ucos2 偏向于控制方面 说白了就是一个高级的单片机 a系列主要应用在人机互动要求较高的场合
  • Github Copilot连接不上服务器

    现象 报错 ERROR default 2023 09 08T15 47 01 542Z GitHub Copilot could not connect to server Extension activation failed conn
  • Python 2.7下下载并安装nltk (自然语言处理工具包)

    1 在cmd窗口中 进入到python的文件夹内的 Scripts内 我的目录地址是 D using Python Scripts 命令行输入 easy install pip 运行结束后 安装PyYAML and NLTK 命令行输入 p
  • c++ 中bool 的默认值

    比如在Test h中定义变量 isFirst Test h头文件 ifndef TEST H define TEST H class Test private bool isFirst endif 然后访问它 include Test h
  • Java面试题整理——网络

    Java面试题整理 网络 网络 1 常用的 http 响应码及含义 200 OK 请求成功 这是对HTTP请求成功的标准应答 201 Created 请求创建被完成 同时新的资源被创建 202 Acceptd 供处理的请求已被接受 但是处理
  • 【VxWorks】Vxworks、QNX、Xenomai、Intime、Sylixos、Ucos等实时操作系统的性能特点

    目录 1 VxWorks操作系统 2 QNX操作系统 3 Xenomai操作系统 4 INtime操作系统 5 SylixOS操作系统 5 1 SylixOS官网
  • uniapp 跳转传参 [‘object‘] 问题解决, 遇坑解决

    普通 号拼接传参不知道什么原因 接收参数时转换数据失败 换成模板传参试试 解决 传 ckgd productList 传参时先转为json 一定要用模板字符串传参 如下 uni navigateTo url pagesA service d
  • MySQL执行器与存储引擎是怎么交互的

    体系结构 1 连接器 2 查询缓存 3 分析器 4 优化器 多个索引 选择哪个索引 join的顺序 5 执行器 调用存储引擎接口获取满足条件的第一行记录 调用存储引擎接口获取满足条件的下一行记录 6 存储引擎 索引下推 假设现子表T有字段
  • c++泛型算法扩展和迭代器、反向迭代器

    cout lt lt 插入迭代器 lt lt endl back inserter 创建一个使用push back的迭代器 front inserter 创建一个使用push front的迭代器 inserter 创建一个使用insert的
  • bee-box

    这篇博客就是为了记下bee box做题过程 随便记记 免得忘了 安装 先去官网下载了 然后分个新的盘单独放进去 打开虚拟机 双击bee box vmx就能安装了 打开里面的火狐会自动跳去一个登录界面 默认账号和密码是bee bug 登录然后
  • GitHub 源代码被泄露了...

    阅读本文大概需要 4 分钟 来自量子位 GitHub 忽然 开源 了自己代码的一部分 还将它放在了 GitHub 上 事件起因是这样的 TypeScript 的开发者 Resynth 忽然 Po 了篇文章 表示代码托管服务 GitHub 的
  • 【牛客网OJ题】不要二

    题目描述 二货小易有一个WH的网格盒子 网格的行编号为 0 H 1 网格的列编号为0 W 1 每个格子至多可以放一块蛋糕 任意两块蛋糕的欧几里得距离不能等于2 对于两个格子坐标 x1 y1 x2 y2 的欧几里得距离为 x1 x2 x1 x
  • spring注解:@Autowired、@Qualifier、@Primary

    Autowired 默认情况下 Autowired 按类型装配 Spring Bean 如果容器中有多个相同类型的 bean 则框架将抛出 NoUniqueBeanDefinitionException 以提示有多个满足条件的 bean 进
  • Raki的读paper小记:SELF-INSTRUCT: Aligning Language Models with Self-Generated Instructions

    Abstract Introduction Related Work 研究任务 改进大模型遵循指令的能力 SELF INSTRUCT提供了一种几乎无需注释的方法来使预训练语言模型与指令对齐 已有方法和相关工作 许多研究提出使用语言模型进行数
  • Vue 2.0双向绑定原理的实现

    Object defineProperty方法 vue js是采用数据劫持结合发布 订阅者模式的方式 通过Object defineProperty 来劫持各个属性的setter getter 在数据变动时发布消息给订阅者 触发相应的监听回