iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >浅析vue侦测数据的变化之基本实现
  • 241
分享到

浅析vue侦测数据的变化之基本实现

2024-04-02 19:04:59 241人浏览 安东尼
摘要

目录一、Object的变化侦测二、关于 Object 的问题三、Array 的变化侦测3.1、背景3.2、实现四、关于 Array 的问题一、Object的变化侦测 下面我们就来模拟

一、Object的变化侦测

下面我们就来模拟侦测数据变化的逻辑。

强调一下我们要做的事情:数据变化,通知到外界(外界再做一些自己的逻辑处理,比如重新渲染视图)。

开始编码之前,我们首先得回答以下几个问题:

1.如何侦测对象的变化?

  • 使用 Object.defineProperty()。读数据的时候会触发 getter,修改数据会触发 setter。
  • 只有能侦测对象的变化,才能在数据发生变化的时候发出通知

2.当数据发生变化的时候,我们通知谁?

  • 通知用到数据的地方。而数据可以用在模板中,也可以用在 vm.$watch() 中,地方不同,行为也不相同,比如这里要渲染模板,那里要进行其他逻辑。所以干脆抽象出一个类。当数据变化的时候通知它,再由它去通知其他地方。
  • 这个类起名叫 Watcher。就是一个中介。

3.依赖谁?

  • 通知谁,就依赖谁,依赖 Watcher。

4.何时通知?

  • 修改数据的时候。也就是 setter 中通知

5.何时收集依赖?

  • 因为要通知用数据的地方。用数据就得读数据,我们就可以在读数据的时候收集,也就是在 getter 中收集

6.收集到哪里?

  • 可以在每个属性里面定义一个数组,与该属性有关的依赖都放里面

编码如下(可直接运行):


// 全局变量,用于存储依赖
let globalData = undefined;

// 将数据转为响应式
function defineReactive (obj,key,val) {
    // 依赖列表
    let dependList = []
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function () {
        // 收集依赖(Watcher)
        globalData && dependList.push(globalData)
        return val
      },
      set: function reactiveSetter (newVal) {
        if(val === newVal){
            return
        }
        // 通知依赖项(Watcher)
        dependList.forEach(w => {
            w.update(newVal, val)
        })
        val = newVal
      }
    });
}

// 依赖
class Watcher{
    constructor(data, key, callback){
        this.data = data;
        this.key = key;
        this.callback = callback;
        this.val = this.get();
    }
    // 这段代码可以将自己添加到依赖列表中
    get(){
        // 将依赖保存在 globalData
        globalData = this;
        // 读数据的时候收集依赖
        let value = this.data[this.key]
        globalData = undefined
        return value;
    }
    // 数据改变时收到通知,然后再通知到外界
    update(newVal, oldVal){
        this.callback(newVal, oldVal)
    }
}


let data = {};
// 将 name 属性转为响应式
defineReactive(data, 'age', '88')
// 当数据 age 改变时,会通知到 Watcher,再由 Watcher 通知到外界
new Watcher(data, 'age', (newVal, oldVal) => {
    console.log(`外界:newVal = ${newVal} ; oldVal = ${oldVal}`)
})

data.age -= 1 // 控制台输出: 外界:newVal = 87 ; oldVal = 88

在控制台下继续执行 data.age -= 1,则会输出 外界:newVal = 86 ; oldVal = 87

附上一张 Data、defineReactive、dependList、Watcher和外界的关系图。

首先通过 defineReactive() 方法将 data 转为响应式(defineReactive(data, 'age', '88'))。

外界通过 Watcher 读取数据(let value = this.data[this.key]),数据的 getter 则会被触发,于是通过 globalData 收集Watcher。

当数据被修改(data.age -= 1), 会触发 setter,会通知依赖(dependList),依赖则会通知 Watcher(w.update(newVal, val)),最后 Watcher 再通知给外界。

二、关于 Object 的问题

思考一下:上面的例子,继续执行 delete data.age 会通知到外界吗?

不会。因为不会触发 setter。请接着看:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/Vue/dist/vue.js"></script>
</head>
<body>
    <div id='app'>
        <section>
            {{ p1.name }}
            {{ p1.age }}
        </section>
    </div>
<script>
const app = new Vue({
    el: '#app',
    data: {
        p1: {
            name: 'ph',
            age: 18
        }
    }
})
</script>
</body>
</html>

运行后,页面会显示 ph 18。我们知道更改数据,视图会重新渲染,于是在控制台执行 delete app.p1.name,发现页面没有变化。这与上面示例中执行 delete data.age 一样,都不会触发setter,也就不会通知到外界。

为了解决这个问题,Vue提供了两个 api(稍后将介绍它们):vm.$set 和 vm.$delete。

如果你继续执行 app.$delete(app.p1, 'age'),你会发现页面没有任何信息了(name 属性已经用 delete 删除了,只是当时没有重新渲染而已)。

:如果这里执行 app.p1.sex = 'man',用到数据 p1 的地方也不会被通知到,这个问题可以通过 vm.$set 解决。

三、Array 的变化侦测

3.1、背景

假如数据是 let data = {a:1, b:[11, 22]},通过 Object.defineProperty 将其转为响应式之后,我们修改数据 data.a = 2,会通知到外界,这个好理解;同理 data.b = [11, 22, 33] 也会通知到外界,但如果换一种方式修改数据 b,就像这样 data.b.push(33),是不会通知到外界的,因为没走 setter。请看示例:


function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function () {
        console.log(`get val = ${val}`)
        return val
      },
      set: function reactiveSetter (newVal) {
        if(val === newVal){
            return
        }
        console.log(`set val = ${newVal}; oldVal = ${val}`)
        val = newVal
      }
    });
}

// 以下是测试代码 {1}
let data = {}
defineReactive(data, 'a', [11,22])
data.a.push(33)     // get val = 11,22               (没有触发 setter)    {2}     
data.a              // get val = 11,22,33 
data.a = 1          // set val = 1; oldVal = 11,22,33(触发 setter)

通过 push() 方法改变数组的值,确实没有触发 setter(行{2}),也就不能通知外界。这里好像说明了一个问题:通过 Object.definePropery() 方法,只能将对象转为响应式,不能将数组转为响应式。

其实 Object.definePropery() 可以将数组转为响应式。请看示例:


// 继续上面的例子,将测试代码(行{1})改为:
let data = []
defineReactive(data, '0', 11)
data[0] = 22    // set val = 22; oldVal = 11
data.push(33)   // 不会触发                     {10}

虽然 Object.definePropery() 可以将数组转为响应式,但通过 data.push(33)(行{10})这种方式修改数组,仍然不会通知到外界。

所以在 Vue 中,将数据转为响应式,用了两套方式:对象使用 Object.defineProperty();数组则使用另一套。

3.2、实现

es6 中可以用 Proxy 侦测数组的变化。请看示例:


let data = [11,22]
let p = new Proxy(data, {
    set: function(target, prop, value, receiver) {
        target[prop] = value;
        console.log('property set: ' + prop + ' = ' + value);
        return true;
    }
    })
console.log(p)
p.push(33)

es6 以前就稍微麻烦点,可以使用拦截器。原理是:当我们执行 [].push() 时会调用数组原型(Array.prototype)中的方法。我们在 [].push()Array.prototype 之间增加一个拦截器,以后调用 [].push() 时先执行拦截器中的 push() 方法,拦截器中的 push() 在调用 Array.prototype 中的 push() 方法。请看示例:


// 数组原型
let arrayPrototype = Array.prototype

// 创建拦截器
let interceptor = Object.create(arrayPrototype)

// 将拦截器与原始数组的方法关联起来
;('push,pop,unshift,shift,splice,sort,reverse').split(',')
.forEach(method => {
    let origin = arrayPrototype[method];
    Object.defineProperty(interceptor, method, {
        value: function(...args){
            console.log(`拦截器: args = ${args}`)
            return origin.apply(this, args);
        },
        enumerable: false,
        writable: true,
        configurable: true
    })
});

// 测试
let arr1 = ['a']
let arr2 = [10]
arr1.push('b')
// 侦测数组 arr2 的变化
Object.setPrototypeOf(arr2, interceptor)    // {20}
arr2.push(11)       // 拦截器: args = 11
arr2.unshift(22)    // 拦截器: args = 22

这个例子将能改变数组自身内容的 7 个方法都加入到了拦截器。如果需要侦测哪个数组的变化,就将该数组的原型指向拦截器(行{20})。当我们通过 push 等 7 个方法修改该数组时,则会在拦截器中触发,从而可以通知外界。

到这里,我们只完成了侦测数组变化的任务。

数据变化,通知到外界。上文编码的实现只是针对 Object 数据,而这里需要针对 Array 数据。

我们也来思考一下同样的问题:

1.如何侦测数组的变化?

  • 拦截器

2.当数据发生变化的时候,我们通知谁?

  • Watcher

3.依赖谁?

  • Watcher

4.何时通知?

  • 修改数据的时候。拦截器中通知。

5.何时收集依赖?

  • 因为要通知用数据的地方。用数据就得读数据。在读数据的时候收集。这和对象收集依赖是一样的。
  • {a: [11,22]} 比如我们要使用 a 数组,肯定得访问对象的属性 a。

6.收集到哪里?

  • 对象是在每个属性中收集依赖,但这里得考虑数组在拦截器中能触发依赖,位置可能得调整

就到这里,不在继续展开了。接下来的文章中,我会将 vue 中与数据侦测相关的源码摘出来,配合本文,简单分析一下。

四、关于 Array 的问题


// 需要自己引入 vue.js。后续也尽可能只罗列核心代码
<div id='app'>
        <section>
            {{ p1[0] }}
            {{ p1[1] }}
        </section>
</div>
<script>
const app = new Vue({
    el: '#app',
    data: {
        p1: ['ph', '18']
    }
})
</script>

运行后在页面显示 ph 18,控制台执行 app.p1[0] = 'lj' 页面没反应,因为数组只有调用指定的 7 个方法才能通过拦截器通知外界。如果执行 app.$set(app.p1, 0, 'pm') 页面内容会变成 pm 18

以上就是浅析vue侦测数据的变化之基本实现的详细内容,更多关于vue侦测数据的变化的资料请关注编程网其它相关文章!

--结束END--

本文标题: 浅析vue侦测数据的变化之基本实现

本文链接: https://www.lsjlt.com/news/128202.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅析vue侦测数据的变化之基本实现
    目录一、Object的变化侦测二、关于 Object 的问题三、Array 的变化侦测3.1、背景3.2、实现四、关于 Array 的问题一、Object的变化侦测 下面我们就来模拟...
    99+
    2024-04-02
  • C#实现数据库数据变化监测(sqlserver&mysql)
    监测数据库表数据变化,可实现数据库同步(一主一从(双机备份),一主多从(总部数据库,工厂1,工厂2,工厂数据合并到总部数据)) sqlserver 启用数据库监听服务 ALTER DATABASE test SET NEW_BROKER W...
    99+
    2023-09-22
    数据库 sqlserver mysql
  • Vue中如何实现对Array的数据侦听
    这篇文章给大家分享的是有关Vue中如何实现对Array的数据侦听的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。核心思想通过创建一个拦截器来覆盖数组本身的原型对象Array.pro...
    99+
    2024-04-02
  • vue中watch如何自动检测数据变化实时渲染
    小编给大家分享一下vue中watch如何自动检测数据变化实时渲染,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!具体如下:首先确认...
    99+
    2024-04-02
  • SQLServer2019 数据库的基本使用之图形化界面操作的实现
    目录一、数据库、表的创建与删除1. 创建数据库2. 修改数据库3. 删除数据库4. 创建数据表5. 删除数据表二、数据表常见操作1. 在表结构中添加新字段2. 在表结构中删除字段3....
    99+
    2024-04-02
  • vue前端测试开发watch监听data的数据变化
    目录watch监听data的数据变化新问题解决1. 先把姓名的值,也加到options里2. 在监听里增加for循环和判断watch监听data的数据变化 上一篇里提到了用eleme...
    99+
    2024-04-02
  • Python自动化测试之登录脚本的实现
    目录环境准备1、安装selenium模块2、安装浏览器驱动器代码1、登录代码2、xpath定位元素标签环境准备 前提已经安装好python、pycharm,配置了对应的环境变量。 1...
    99+
    2023-02-23
    Python自动化登录 Python自动化测试登录
  • ES6变量赋值和基本数据类型实例分析
    本篇内容介绍了“ES6变量赋值和基本数据类型实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!let和constlet和const不存在...
    99+
    2023-07-02
  • 实例JavaScript之实现数值的动态变化
    以上就是实例JavaScript之实现数值的动态变化的详细内容,更多请关注编程网其它相关文章!...
    99+
    2022-11-22
    javascript
  • 基于Python实现股票数据分析的可视化
    目录一、简介二、代码1、主文件2、数据库使用文件3、ui设计模块4、数据处理模块三、数据样例的展示四、效果展示一、简介 我们知道在购买股票的时候,可以使用历史数据来对当前的股票的走势...
    99+
    2024-04-02
  • C++浅析序列数据封装与优化实现方法
    目录一、说明二、示范和代码一、说明 用于优化的包装函数:本节介绍包装函数以优化序列化过程。这些函数标记对象以允许 Boost.Serialization 应用某些优化技术。 二、示范...
    99+
    2022-12-08
    C++序列数据封装与优化 C++序列数据封装 C++序列数据优化
  • vue前端测试开发watch如何监听data的数据变化
    本篇内容主要讲解“vue前端测试开发watch如何监听data的数据变化”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue前端测试开发watch如何监听data的数据变化”吧!watch监听d...
    99+
    2023-06-30
  • Python Selenium 之数据驱动测试的实现!
    数据驱动模式的测试好处相比普通模式的测试就显而易见了吧!使用数据驱动的模式,可以根据业务分解测试数据,只需定义变量,使用外部或者自定义的数据使其参数化,从而避免了使用之前测试脚本中固定的数据。可以将测试脚本与测试数据分离,使得测试脚本在不...
    99+
    2023-10-27
    python selenium 测试工具 postman 自动化测试 软件测试 测试工程师
  • Python数据可视化之matplotlib.pyplot绘图的基本参数详解
    目录1.matplotlib简介2.图形组成元素的函数用法2.1. figure():背景颜色2.2 xlim()和 ylim():设置 x,y 轴的数值显示范围2.3 xlabel...
    99+
    2024-04-02
  • 剖析 Python 变量与数据类型:深入浅出的学习之旅
    变量是计算机程序中存储数据的容器,其类型决定了存储数据的格式和操作。在 Python 中,变量和数据类型密不可分,深入理解两者对于有效利用语言至关重要。 Python 变量 标识符:变量名称,由字母、数字或下划线组成,不能以数字开头。 ...
    99+
    2024-04-02
  • vue实现监听数值的变化,并捕捉到
    目录上传头像的例子是完整的,登录没全部展示1. 封装全局监听方法2. 触发,并将监听的值存入Storage3. 监听完整例子(代码看上面 )第一步第二步第二步第三步上传头像的例子是完...
    99+
    2022-11-13
    vue监听 监听数值变化 vue捕捉数值
  • Python中的基本数据类型实例分析
    这篇“Python中的基本数据类型实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python中的基本数据类型实例分析...
    99+
    2023-07-02
  • MySQL数据库的基本操作实例分析
    本文小编为大家详细介绍“MySQL数据库的基本操作实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“MySQL数据库的基本操作实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、MySQL简介1、数据...
    99+
    2023-06-30
  • Vue移动端下拉加载更多数据onload实现方法浅析
    思想:van-list组件负责UI层监测触底, 执行onload函数, page++, 请求下页数据, 和现在数据合并显示更多, 设置loading为false, 确保下次触底还能执...
    99+
    2023-02-15
    Vue移动端下拉加载 Vue下拉加载onload
  • vue如何实现监听数值的变化并捕捉
    这篇文章主要介绍了vue如何实现监听数值的变化并捕捉的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue如何实现监听数值的变化并捕捉文章都会有所收获,下面我们一起来看看吧。1. 封装全局监听方法在main.js...
    99+
    2023-07-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作