Vue2双向数据绑定的原理

news2024/11/16 21:31:41

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

Vue双向数据绑定主要有以下几个步骤:
模板解析 事件添加 数据劫持 双向绑定

模板解析

文本解析编译

<!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>Vue双向数据绑定的原理</title>
  <script src="vue01.js"></script>
</head>

<body>
  <div id="app">
    {{str}}
    <h1>{{str}}</h1>
  </div>

</body>
<script>
  // 创建一个Vue实例
  new Vue({
    el: "#app",
    data: {
      str: '前端小白学习中!'
    }
  });
</script>

</html>

<!-- 模板解析 事件添加  数据劫持 双向绑定 -->
<!-- 当我们没有写Vue的时候,浏览器是不能帮助我们解析{{}} 里面的文字的 
目的: 让str 正常输出文字-->

<!-- 在data里面定义一些自己的文本数据,进行挂载以{{}}形式进行展示 -->
// 为了让str正常显示,我们要创建Vue这个类
class Vue {
  // options 选择 在构造函数中传入参数 这个参数就是创建的Vue 传递过来的对象 ,所以我们通过el可以获取div这个DOM
  constructor(options) {
    console.log(options.el)
    this.$el = document.querySelector(options.el)
    this.$data = options.data  //new Vue data 里面的数据
    console.log('Html页面创建的Vue实例里面的data数据', this.$el)
    // 第一步:成功获取模板
    // 第二步:解析编译模板——【我们需要使用到一个方法】并且把DOM传进去
    this.compile(this.$el)

  }
  // 将我们获取的DOM 元素传递进去,看一下里面的节点
  compile (node) {
    node.childNodes.forEach((item, index) => {
      // 怎么获取文本节点:通过nodeType  案例中节点为3的是文本节点 1元素节点
      console.log(item.nodeType)  // 这里输出的是子节点,以及对应的下标
      // 第三步:我们获取文本节点的目的是将它替换成为data中的数据
      // 使用===进行判断
      if (item.nodeType === 1) {
        // 这里考虑到标签没有内容的情况获取单标签的情况
        if (item.childNodes.length > 0) {
          this.compile(item)
        }
      }
      if (item.nodeType === 3) {
        // 是文本节点 使用正则来匹配这个格式
        let reg = /\{\{(.*?)\}\}/g
        // 获取节点的文本内容
        let text = item.textContent  //{{str}}
        item.textContent = text.replace(reg, (match, vmKey) => {
          // vnKey 这里我们获取到了一个str,通过str 我们可以获取这边的值
          // 因为对象作为参数已经进行了传递了
          // 对 {{}}里面可能存在的空格进行处理 
          vmKey=vmKey.trim()
          console.log("reg:", reg, "match:", match, "vmKey:", vmKey,)
          return this.$data[vmKey]
          // 按照正则的格式找到{{}}里面的文本,替换成为data里面的数据,也就是data中的字符串
          // 再把返回的值赋值给item的textContent,就是item文本节点里的内容已经发生改变了
          // 我们发现返回之后只有第一个节点进行了替换,因为h1是元素节点,这里只判断文本节点
          // 在上面的第21行进行递归调用 之后就获取数据成功了
        })
      }
    })
  }
}




// 问题: Vue 创建类加构造函数的意义
// 当 new 一个对象的时候会自动调用这个对象的构造函数
// compile ——是什么??
// compile——【编译】是我们自己定义的一个方法 用来将我们获取的模板进行解析
// 问题3: forEach 的使用 里面的两个参数都是什么?
// 我们上面将node里面的节点进行了遍历,item 是node里面的每一个节点 index 是节点里面对应的下标


// <h1></h1> 是元素节点 我们是不需要动的
// 我们需要获取的是文本节点
// 怎么获取文本节点:通过nodeType


// 获取文本节点之后进行匹配 使用到了正则表达式 【对于个别关键字不是很了解】转义字符的作用

事件绑定

<!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>02Vues双向数据绑定的原理——事件绑定</title>
  <script src="./vue02.js"></script>
</head>

<body>
  <div id="app">
    {{str}}
    <h1>{{str}}</h1>
    <button @click="cli">按钮</button>
  </div>

</body>
<script>
  // 创建一个Vue实例
  new Vue({
    el: "#app",
    data: {
      str: '前端小白学习中_双向数据绑定的原理——事件绑定'
    },
    // 在methods进行方法的调用
    methods: {
      cli () {
        console.log('方法调用了')
      }
    }
  });
</script>

</html>

<!-- 事件绑定:我们使用了点击事件之后需要对数据进行处理 之后才可以实现点击的功能
2. 我们的点击事件是写在元素标签的里面
 2.1 我们需要找到元素节点 根据之前的判断元素类型为1就是元素节点
 2.2 找到所有的元素节点之后对节点进行判断,看一下里面有没有包含点击事件

3.拿到方法名称之后通过名称拿到里面的整个方法,当我们点击的时候再让它进行执行就可以了
-->
<!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>02Vues双向数据绑定的原理——事件绑定</title>
  <script src="./vue02.js"></script>
</head>

<body>
  <div id="app">
    {{str}}
    <h1>{{str}}</h1>
    <button @click="cli">按钮</button>
  </div>

</body>
<script>
  // 创建一个Vue实例
  new Vue({
    el: "#app",
    data: {
      str: '前端小白学习中_双向数据绑定的原理——事件绑定'
    },
    // 在methods进行方法的调用
    methods: {
      cli () {
        console.log('方法调用了')
      }
    }
  });
</script>

</html>

<!-- 事件绑定:我们使用了点击事件之后需要对数据进行处理 之后才可以实现点击的功能
2. 我们的点击事件是写在元素标签的里面
 2.1 我们需要找到元素节点 根据之前的判断元素类型为1就是元素节点
 2.2 找到所有的元素节点之后对节点进行判断,看一下里面有没有包含点击事件

3.拿到方法名称之后通过名称拿到里面的整个方法,当我们点击的时候再让它进行执行就可以了
-->

数据劫持

<!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>03Vue双向数据绑定的原理——数据劫持</title>
  <script src="./vue03.js"></script>
</head>

<body>
  <div>

    <!--  
核心:对数据进行拦截
获取设置属性的时候对属性进行拦截,再另外执行操作
-->
</body>
<script>
  let obj = {
    name: '沐舒',
    age: 19,
    // other 是一个子对象 
    other: {
      like: '健身'
    }
  }
  // 第一个参数:要操作的对象  第二个参数:哪一个属性 第三个参数:也是一个对象里面的get set 方法
  Object.defineProperty(obj, 'name', {
    // 这里defineProperty这个方法已经給name属性劫持掉了,把它控制住了,和原本的name属性没有关系了
    // 后面再要获取值的时候就要触发get方法了,后面能获取到什么值就取决于get方法里面的return 返回值
    // 企图获取属性的时候再来执行这个方法

    get () {
      return name               //这里返回的是一个空的字符串,因为我们还没有对它进行设置
    },
    // 去设置修改属性的时候触发的 
    set (val) {
      name = val  // 这里在你设置值的时候,就已经把你输入的值赋值给name
    }
  })
</script>

</html>

<!--
  这里属于编译解析——最后它对视图进行更新
  这里的重点是:definedProperty 是Vue2里面实现数据劫持的方法
get: 不是用来获取属性值的,而是在企图获取属性值的时候要触发的方法
set: 设置修改属性的时候触发d

eg: 在控制台输入obj.name='jack'  触发了修改设置属性的这个行为,触发行为就会执行set
obj.name '沐舒'
这里必须要有设置的操作,设置成为什么不重要:重要的是进行触发
set(val){
  name=val  //set 方法将name设置成为amy 这样get就可以获取name为 沐舒
}

获取到的值就是修改之后的值吗??
在set 方法中传入一个参数  ,将参数设置为属性就可以了
set(val){
  name=val  这个参数可以获取到当我们设置修改这个属性的时候输入的值
}


使用 definedProperty 将数据进行劫持之后,这个属性已经和原本设置的值没有关系了
后面需要进行值的获取的时候,
① 明确在get 方法里面给它一个返回值
get () {
      return '小红'
    },
    虽然有set 方法进行设置但是获取的时候触发的还是get,get方法中有具体的返回值的时候set方法中的值就不会返回

②
-->
// Vue03
// 为了让str正常显示,我们要创建Vue这个类
class Vue {
  constructor(options) {
    this.$el = document.querySelector(options.el)
    this.$data = options.data
    // ① 把传过来的参数的对象也存储一下 {el...data,methods}
    this.$options = options
    console.log(" this.$options", this.$options)

    this.compile(this.$el)

  }

  compile (node) {
    node.childNodes.forEach((item, index) => {
      if (item.nodeType === 1) {
        //         2. 我们的点击事件是写在元素标签的里面
        //  2.1 我们需要找到元素节点 根据之前的判断元素类型为1就是元素节点
        //  2.2 找到所有的元素节点之后对节点进行判断,看一下里面有没有包含点击事件
        if (item.hasAttribute("@click")) {
          // 通过getAttribute这个方法看一下里面的属性值是什么 
          let vmKey = item.getAttribute("@click")
          console.log("vmKey", vmKey)  // cli 可以拿到事件绑定的方法名称
          vmKey = vmKey.trim()          // 对vmKey 进行空格处理
          // 3.拿到方法名称之后通过名称拿到里面的整个方法,当我们点击的时候再让它进行执行就可以了

          item.addEventListener('click', () => {
            console.log(this.$options.methods[vmKey])
            // 4. 让方法执行 获取了里面的数据 
            this.$options.methods[vmKey]()
          })
        }
        if (item.childNodes.length > 0) {
          this.compile(item)
        }
      }
      if (item.nodeType === 3) {
        let reg = /\{\{(.*?)\}\}/g
        let text = item.textContent  //{{str}}
        item.textContent = text.replace(reg, (match, vmKey) => {
          vmKey = vmKey.trim()
          return this.$data[vmKey]
        })
      }
    })
  }
}

请添加图片描述请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1116060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为何说只有 1 种实现线程的方法?

Java全能学习面试指南&#xff1a;https://javaxiaobear.cn 今天我们来学习为什么说本质上只有一种实现线程的方式&#xff1f;实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里&#xff1f; 实现线程是并发编程中基础中的基础&#xff0c;因为我们必须要先实现多线程…

虹科活动 | 探索全新AR应用时代,虹科AR VIP研讨会广州场回顾!

文章来源&#xff1a;虹科数字化AR 阅读原文&#xff1a;https://mp.weixin.qq.com/s/7tmYR42Tw5XLn70fm8Nnew 主题演讲 本次研讨会&#xff0c;虹科特邀 “工业AR鼻祖” 美国Vuzix公司的首席应用工程师郑慎方先生进行主题演讲&#xff0c;并邀请到了各界的专业人士和企业代表参…

ESRI ArcGIS Pro 3.0-3.0.2图文安装教程及下载

ArcGIS 是由美国著名的地理信息系统公司 Esri 开发的一款地理信息系统软件。ArcGIS Pro是一款功能强大的单桌面 GIS 应用程序&#xff0c;是在桌面上创建和处理空间数据的基本应用程序。ArcGIS Pro支持数据可视化和数据高级分析&#xff0c;可以创建 2D 地图和3D 场景。它支持跨…

无需公网IP,通过内网穿透轻松搭建微信公众号开发本地调试环境!

文章目录 前言1. 配置本地服务器2. 内网穿透2.1 下载安装cpolar内网穿透2.2 创建隧道 3. 测试公网访问4. 固定域名4.1 保留一个二级子域名4.2 配置二级子域名 5. 使用固定二级子域名进行微信开发 前言 在微信公众号开发中&#xff0c;微信要求开发者需要拥有自己的服务器资源来…

如何建立风险、内控与合规三位一体的管控体系?

为指导企业开展风险管理工作&#xff0c;进一步提高管理水平&#xff0c;增强竞争力&#xff0c;促进稳步发展&#xff0c;自2006年开始&#xff0c;我国国资委、财政部等相关部委借鉴COSO理论及国际上影响较大的风险管理和内部控制标准&#xff0c;结合国内的实际情况&#xf…

构建跨平台应用程序:Apollo在移动开发中的应用

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

软件工程与计算(二十二)软件开发过程模型

&#xff08;自顶向下&#xff0c;逐层细化&#xff09; 目录 一.软件开发的典型阶段 1.需求工程 2.软件设计 3.软件构造 4.软件测试 5.软件交付 6.软件维护 二.软件生命周期模型 三.软件过程模型 四.构建-修复模型 五.瀑布模型 六.增量迭代模型 七.演化模型 八…

优维产品最佳实践第12期:IT资源管理首页丰富

​ 背 景 当我们进入平台后&#xff0c;默认跳转至IT资源管理首页&#xff0c;因此该页面的优化与丰富将极大的提高平台使用者的体验和效率。优化后的首页可以更好地展示常用模型、小产品、外部系统、以及保存的所有关系查询和快速查询条件&#xff0c;使用户能够更快捷、方便…

抖音同城热搜榜上榜技巧有哪些

抖音同城热搜榜上的话题通常是具有一定热度和社会关注度的。因此&#xff0c;在制作视频时&#xff0c;可以关注一些热门话题&#xff0c;如社会热点、明星八卦、节日庆典等。以社会热点为例&#xff0c;可以关注一些突发事件、政策变革等&#xff0c;这样可以在短时间内吸引大…

SystemVerilog Assertions应用指南 Chapter1.20“ $past”构造

1.20“ $past”构造 SVA提供了一个内嵌的系统任务“$past”,它可以得到信号在几个时钟周期之前的值。在默认情况下,它提供信号在前一个时钟周期的值。结构的基本语法如下 $past (signal_name ,number of clock cycles) 这个任务能够有效地验证设计到达当前时钟周期的状态所采用…

数据分析案例-基于snownlp模型的MatePad11产品用户评论情感分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【配置本地仓库yum源的两种方式】

一、使用下载的rpm包搭建本地仓库yum源 ​  使用自己下载的rpm包搭建本地仓库&#xff0c;需要用createrepo去创建仓库元数据。 1、下载需要的软件包 在一台可以联网的服务器上下载。或者通过其他方式下载自己需要的软件包。 # 下载所需工具依赖包到 /mypath [roottest ~]…

深入理解TDD(测试驱动开发):提升代码质量的利器!

在日常的软件开发工作中&#xff0c;我们常常会遇到这样的问题&#xff1a;如何在繁忙的项目进度中&#xff0c;保证我们的代码质量&#xff1f;如何在不断的迭代更新中&#xff0c;避免引入新的错误&#xff1f;对此&#xff0c;有一种有效的开发方式能帮助我们解决这些问题&a…

redis哨兵机制

为什么要有哨兵机制&#xff1f; 在 Redis 的主从架构中&#xff0c;由于主从模式是读写分离的&#xff0c;如果主节点&#xff08;master&#xff09;挂了&#xff0c;那么将没有主节点来服务客户端的写操作请求&#xff0c;也没有主节点给从节点&#xff08;slave&#xff0…

使用Matplotlib画多y轴图

使用Matplotlib画多y轴图 代码成品图 代码 import matplotlib.pyplot as plt import mpl_toolkits.axisartist as AA from mpl_toolkits.axes_grid1 import host_subplot%matplotlib inline config {"font.family": "serif","font.size": 14,&…

文件批量下载

网页可能暂时无法连接&#xff0c;或者它已永久性地移动到了新网址。 把uploads前面的 / 去掉 public function downFile(){// 网页可能暂时无法连接&#xff0c;或者它已永久性地移动到了新网址。 把uploads前面的/去掉$files ["uploads/20231018/868ac3fe78e40c67…

【React】高频面试题

1. 简述下 React 的事件代理机制&#xff1f; React使用了一种称为“事件代理”&#xff08;Event Delegation&#xff09;的机制来处理事件。事件代理是指将事件处理程序绑定到组件的父级元素上&#xff0c;然后在需要处理事件的子元素上触发事件时&#xff0c;事件将被委托给…

通过Django Admin+HttpRunner1.5.6实现简易接口测试平台

这篇文章主要介绍了通过Django AdminHttpRunner1.5.6实现简易接口测试平台,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 这是一个使用HttpRunner开发接口平台的简单Demo。 新建Django项目 这是一个使…

lesson1-C++类和对象(上)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 5.类的作用域 6.类的实例化 7.类的对象大小的计算 8.类成员函数的this指针 1.面向过程和面向对象初步认识 在C语言中&#xff0…

Python 框架学习 Django篇 (五) Session与Token认证

我们前面经过数据库的学习已经基本了解了怎么接受前端发过来的请求&#xff0c;并处理后返回数据实现了一个基本的登录登出效果&#xff0c;但是存在一个问题&#xff0c;我们是将所有的请求都直接处理了&#xff0c;并没有去检查是否为已经登录的管理员发送的&#xff0c;如果…