三、Vue响应式原理-核心内容精讲

本文深入探讨Vue的响应式原理,从数据驱动、响应式核心到发布订阅模式和观察者模式,逐步揭示Vue如何实现数据变化驱动视图更新。通过模拟Vue响应式,解析`defineReactive`方法,理解响应式数据的处理,包括对象属性的响应式转换和双向数据绑定。文章总结了给属性重新赋值和实例新增成员是否为响应式的答案,为理解Vue响应式提供了全面的指导。
摘要由CSDN通过智能技术生成

Vue响应式原理

1、课程目标

模拟一个最小版本的Vue

响应式原理在面试的常问问题

实际项目中出现问题的原理层面的解决

​给Vue实例新增一个成员是否是响应式的?

​给属性重新赋值成对象,是否是响应式的?

为学习Vue源码做铺垫。

2、数据驱动

在实现整个Vue响应式代码之前,我们先来了解几个概念。

第一个:数据驱动

第二个:响应式的核心原理

第三个:发布订阅模式和观察者模式

我们先来看一下数据驱动的内容:

数据响应式,双向绑定,数据驱动(我们经常看到这几个词)

数据响应式:数据模型仅仅是普通的JavaScript对象,而当我们修改数据时,视图会进行更新,避免了频繁的DOM操作,提高开发效率,这与Jquery不一样,Jquery是频繁的操作Dom

双向绑定:

数据改变,视图改变,视图改变,数据也随之改变( 通过这句话,我们可以看到在双向绑定中是包含了数据响应式的内容)

​ 我们可以使用v-model 在表单元素上创建双向数据绑定

数据驱动是Vue最独特的特性之一

​ 开发过程中仅仅需要关注数据本身,不需要关心数据是如何渲染到视图中的。主流的MVVM框架都已经实现了数据响应式与双向绑定,所以可以将数据绑定到DOM上。

3、响应式的核心原理

3.1 Vue2.x响应式原理

关于Vue2.x的响应式原理在官方文档中也有介绍。

https://cn.vuejs.org/v2/guide/reactivity.html

在该文档中,我们注意如下一段内容:

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,
Vue 将遍历此对象所有的 property,
并使用 Object.defineProperty 
把这些 property 全部转为 getter/setter。
Object.defineProperty 是 ES5 中一个无法 shim 的特性,
这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

通过以上的文字,我们可以看到,在Vue2.x中响应式的实现是通过Object.defineProperty来完成的,注意该属性无法降级(shim)处理,所以Vue不支持IE8以及更低版本的浏览器的原因。

下面我们来看一下Object.defineProperty基本使用

修改data对象中的msg属性的值,实现视图的更新.(这也就是我们所说的响应式)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>defineProperty</title>
  </head>
  <body>
    <div id="app">hello</div>
    <script>
      //模拟Vue中的data选项(当)
      let data = {
     
        msg: "hello",
      };
      //模拟Vue的实例
      let vm = {
     };
      //数据劫持,当访问或者设置vm中的成员的时候,做一些干预操作
      Object.defineProperty(vm, "msg", {
     
        //可枚举(可遍历)
        enumerable: true,
        //可配置(可以使用delete删除,可以通过defineProperty重新定义)
        configurable: true,
        //当获取值的时候执行
        get() {
     
          console.log("get:", data.msg);
          return data.msg;
        },
        // 当设置值的时候执行
        set(newValue) {
     
          console.log("set:", newValue);
          //设置的值与原有的值相同,则没有更改,所以不做任何操作
          if (newValue === data.msg) {
     
            return;
          }
          data.msg = newValue;
          //数据更改,更新DOM的值
          document.querySelector("#app").textContent = data.msg;
        },
      });
      //测试
      //执行set操作
      vm.msg = "abc";
      //执行get操作
      console.log(vm.msg);
    </script>
  </body>
</html>

在进行测试的时候,可以在浏览器的控制台中,输入vm.msg进行测试。

在上面的代码中,我们是将一个对象中的属性转换成了getter/setter的形式,那么这里我们还有一个问题:

如果有一个对象中多个属性需要转换getter/setter,那么应该如何处理?

我们可以通过循环遍历的方式,将对象中的多个属性转换成getter/setter

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>defineProperty多个属性</title>
  </head>
  <body>
    <div id="app">hello</div>
    <script>
      //模拟Vue中的data选项
      let data = {
     
        msg: "hello",
        count: 10,
      };
      //模拟Vue实例
      let vm = {
     };
      proxyData(data);
      function proxyData(data) {
     
        //遍历data对象中的所有属性
        Object.keys(data).forEach((key) => {
     
          // 把data中的属性,转换成vm的setter/getter
          Object.defineProperty(vm, key, {
     
            enumerable: true,
            configurable: true,
            get() {
     
              console.log("get", key, data[key]);
              return data[key];
            },
            set(newValue) {
     
              console.log("set:", key, newValue);
              if (newValue === data[key]) {
     
                return;
              }
              data[key] = newValue;
              document.querySelector("#app").textContent = data[key];
            },
          });
        });
      }
      vm.msg = "hello world";
      console.log(vm.msg);
    </script>
  </body>
</html>

在上面的代码中,我们通过循环的方式给data对象中的每个属性添加了getter/setter.

这里我们只是在视图中展示了msg属性的值,如果想展示count属性的值,可以在浏览器的控制台中,通过vm.count=20这种形式来展示,当然,在后期的课程中我们会分别展示出msgcount属性的值,

3.2 Vue3响应式原理

Vue3的响应式原理是通过Proxy来完成的。

Proxy直接监听对象,而非属性,所以将多个属性转换成getter/setter的时候,不需要使用循环。

ProxyES6课程中新增的,IE不支持

Proxy实现响应式的基本代码如下(该代码的功能与上面所讲解的是一样的):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Proxy</title>
  </head>
  <body>
    <div id="app">hello</div>
    <script>
      //模拟Vue中的data选项
      let data = {
     
        msg: "hello",
        count: 0,
      };
      //模拟Vue实例
      //为data创建一个代理对象vm,这样就可以通过vm.msg来获取data中的msg属性的值,而这时候会执行get方法
      let vm = new Proxy(data, {
     
        // 当访问vm的成员时会执行
        //target表示代理的对象(这里为data对象),key表示所代理的对象中的属性
        get(target, key) {
     
          console.log("get key:", key, target[key]);
          return target[key];
        },
        //当设置vm的成员时会执行
        set(target, key, newValue) {
     
          console.log("set key:", key, newValue);
          if (target[key] === newValue) {
     
            return;
          }
          target[key] = newValue;
          document.querySelector("#app").textContent = target[key];
        },
      });
      //测试
      vm.msg = "aaaa";
      console.log(vm.msg);
    </script>
  </body>
</html>

通过以上的代码我们发现使用Proxy的代码是给对象中所有属性添加getter/setter,而不需要通过循环的方式来实现,所以代码更加的简洁。

4、发布订阅模式

发布订阅模式:订阅者,发布者,信号中心

我们假定,存在一个“信号中心”,某个任务执行完成,
就向信号中心"发布"(publish)一个信号,
其它任务可以向信号中心“订阅”(subscribe)这个信号,
从而知道什么时候自己可以开始执行。
这就叫做"发布/订阅模式"(publish-subscribe pattern)

家长向学生所在的班级订阅了获取学生考试成绩的事件,当老师公布学生的成绩后,就会自动通知学生的家长。

在整个案例中,学生所在的班级为信号中心,老师为发布者,家长为订阅者

Vue 的自定义事件就是基于发布订阅模式来实现的。

下面通过Vue中兄弟组件通信过程,来理解发布订阅模式

// eventBus.js
// 事件中心
let eventHub=new Vue()
//ComponentA.vue
addTodo:function(){
   
    //发布消息(事件)
    eventHub.$emit('add-todo',{
   text:this.newTodoText})
    this.newTodoText=''
}
//ComponentB.vue
//订阅者
created:function(){
   
    //订阅消息(事件)
    eventHub.$on('add-todo',this.addTodo)
}

通过以上代码,我们可以理解发布订阅模式中的核心概念。

下面我们模拟Vue中的自定义事件的实现

下面我们先来做一个基本的分析:

先来看如下代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue 自定义事件</title>
  </head>
  <body>
    <script 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值