iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue2 响应式系统之数组
  • 921
分享到

Vue2 响应式系统之数组

2024-04-02 19:04:59 921人浏览 独家记忆
摘要

目录1、场景2、场景 23、方案3、收集依赖代码实现4、通知依赖代码实现5、测试6、总结本文接Vue2响应式系统 、Vue2 响应式系统之分支切换  ,

本文接Vue2响应式系统 、Vue2 响应式系统之分支切换  ,响应式系统之嵌套、响应式系统之深度响应 还没有看过的小伙伴需要看一下。

1、场景

import { observe } from "./Reactive";
import Watcher from "./watcher";
const data = {
    list: ["hello"],
};
observe(data);
const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};

new Watcher(updateComponent);
data.list = ["hello", "liang"];

先可以一分钟思考下会输出什么。

虽然 的值是数组,但我们是对 进行整体赋值,所以依旧会触发 的 ,触发 进行重新执行,输出如下:listdata.listdata.listsetWatcher

image-20220405184002118

2、场景 2

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    list: ["hello"],
};
observe(data);
const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};
new Watcher(updateComponent);

data.list.push("liang");

先可以一分钟思考下会输出什么。

这次是调用 方法,但我们对 方法什么都没做,因此就不会触发 了。pushpushWatcher

3、方案

为了让 还有数组的其他方法也生效,我们需要去重写它们,通过push代理模式 我们可以将数组的原方法先保存起来,然后执行,并且加上自己额外的操作。




import { def } from "./util";

const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);

const methodsToPatch = [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse",
];


methodsToPatch.forEach(function (method) {
    // cache original method
    const original = arrayProto[method];
    def(arrayMethods, method, function mutator(...args) {
        const result = original.apply(this, args);
        
        // 待补充
        
        return result;
    });
});

当调用了数组的 或者其他方法,就相当于我们之前重写属性的 ,上边待补充的地方需要做的就是通知 中的 。pushsetdepWatcher

export function defineReactive(obj, key, val, shallow) {
    const property = Object.getOwnPropertyDescriptor(obj, key);
    // 读取用户可能自己定义了的 get、set
    const getter = property && property.get;
    const setter = property && property.set;
    // val 没有传进来话进行手动赋值
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
    }
    const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher

    let childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            if (Dep.target) {
                dep.depend();
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            dep.notify();
        },
    });
}

如上边的代码,之前的 是通过闭包,每一个属性都有一个各自的 ,负责收集 和通知 。depdepWatcherWatcher

那么对于数组的话,我们的 放到哪里比较简单呢?dep

回忆一下现在的结构。

const data = {
    list: ["hello"],
};
observe(data);

const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};
new Watcher(updateComponent);

上边的代码执行过后会是下图的结构。

image-20220405200509660

list 属性在闭包中拥有了 属性,通过 ,收集到了包含 函数的 。Depnew WatcherupdateCompnentWatcher

同时因为 的 是数组,也就是对象,通过上篇 listvalue["hello"]响应式系统之深度响应 (opens new window)我们知道,它也会去调用 函数。Observer

那么,我是不是在 中也加一个 就可以了。ObserverDep

image-20220405201451179

这样当我们调用数组方法去修改 的值的时候,去通知 中的 就可以了。['hello']ObserverDep

3、收集依赖代码实现

按照上边的思路,完善一下 类。Observer

export class Observer {
    constructor(value) {
        
        this.dep = new Dep();
        
      	this.walk(value);
    }

    
    walk(obj) {
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]);
        }
    }
}

然后在 中,当前 中的 也去收集依赖。getOberverdep

export function defineReactive(obj, key, val, shallow) {
    const property = Object.getOwnPropertyDescriptor(obj, key);
    // 读取用户可能自己定义了的 get、set
    const getter = property && property.get;
    const setter = property && property.set;
    // val 没有传进来话进行手动赋值
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
    }
    const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher

    let childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            if (Dep.target) {
                dep.depend();
                
                if (childOb) {
                    // 当前 value 是数组,去收集依赖
                    if (Array.isArray(value)) {
                        childOb.dep.depend();
                    }
                }
                
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            dep.notify();
        },
    });
}

4、通知依赖代码实现

我们已经重写了 方法,但直接覆盖全局的 方法肯定是不好的,我们可以在 类中去操作,如果当前 是数组,就去拦截它的 方法。arrayarrrayObservervaluearray

这里就回到 的原型链上了,我们可以通过浏览器自带的 ,将当前对象的原型指向我们重写过的方法即可。js__proto__

考虑兼容性的问题,如果 不存在,我们直接将重写过的方法复制给当前对象即可。__proto__

import { arrayMethods } from './array' // 上边重写的所有数组方法

export class Observer {
    constructor(value) {
        this.dep = new Dep();
      	
        if (Array.isArray(value)) {
            if (hasProto) {
                protoAugment(value, arrayMethods);
            } else {
                copyAugment(value, arrayMethods, arrayKeys);
            }
        
        } else {
            this.walk(value);
        }
    }

    
    walk(obj) {
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]);
        }
    }
}

function protoAugment(target, src) {
    
    target.__proto__ = src;
    
}



function copyAugment(target, src, keys) {
    for (let i = 0, l = keys.length; i < l; i++) {
        const key = keys[i];
        def(target, key, src[key]);
    }
}

还需要考虑一点,数组方法中我们只能拿到 值,那么怎么拿到 对应的 呢。valuevalueObserver

我们只需要在 类中,增加一个属性来指向自身即可。Observe

export class Observer {
    constructor(value) {
        this.dep = new Dep();
        
        def(value, '__ob__', this)
        
        if (Array.isArray(value)) {
            if (hasProto) {
                protoAugment(value, arrayMethods);
            } else {
                copyAugment(value, arrayMethods, arrayKeys);
            }
        } else {
            this.walk(value);
        }
    }
  	...
}

回到最开始重写的 方法中,只需要从 中拿到 去通知 即可。array__ob__DepWatcher



import { def } from "./util";

const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);

const methodsToPatch = [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse",
];


methodsToPatch.forEach(function (method) {
    // cache original method
    const original = arrayProto[method];
    def(arrayMethods, method, function mutator(...args) {
        const result = original.apply(this, args);
        
        const ob = this.__ob__;
        // notify change
        ob.dep.notify();
        
        return result;
    });
});

5、测试

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    list: ["hello"],
};
observe(data);
const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};

new Watcher(updateComponent);
data.list.push("liang");

这样当调用 方法的时候,就会触发相应的 来执行 函数了。pushWatcherupdateComponent

当前的依赖就变成了下边的样子:

image-20220405205545348

6、总结

对于数组的响应式我们解决了三个问题,依赖放在哪里、收集依赖和通知依赖。

我们来和普通对象属性进行一下对比。

image-20220405210847016

 到此这篇关于Vue2 响应式系统之数组的文章就介绍到这了,更多相关Vue2 数组内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Vue2 响应式系统之数组

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

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

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

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

下载Word文档
猜你喜欢
  • Vue2 响应式系统之数组
    目录1、场景2、场景 23、方案3、收集依赖代码实现4、通知依赖代码实现5、测试6、总结本文接Vue2响应式系统 、Vue2 响应式系统之分支切换  ,...
    99+
    2024-04-02
  • Vue2 响应式系统之深度响应
    目录1、场景2、方案3、场景24、总结1、场景 import { observe } from "./reactive"; import Watcher from "./watche...
    99+
    2024-04-02
  • Vue2响应式系统之怎么让数组生效
    这篇文章主要介绍了Vue2响应式系统之怎么让数组生效的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue2响应式系统之怎么让数组生效文章都会有所收获,下面我们一起来看看吧。1、场景import {&n...
    99+
    2023-06-30
  • Vue2响应式系统之嵌套
    目录1、场景2、执行过程3、修复4、测试5、总结1、场景 在 开发中肯定存在组件嵌套组件的情况,类似于下边的样子。Vue <!-- parent-component ...
    99+
    2024-04-02
  • Vue2响应式系统之set和delete
    目录1、数组集2、数组 del3、对象 set4、对象 del5、总结1、数组集 import { observe } from "./reactive"; import Watch...
    99+
    2024-04-02
  • Vue2 响应式系统之异步队列
    目录场景解决方案代码实现执行结果总结试想一下如果这里的 console.log 是渲染页面,那改变一次值就刷新一下页面,会造成严重的性能问题,页面也会不停的改变。 场景 import...
    99+
    2024-04-02
  • Vue2 响应式系统之分支切换
    目录场景observer(data)new Watcher(updateComponent)data.ok = falsedata.text = "hello, ...
    99+
    2024-04-02
  • Vue2响应式系统之深度响应怎么实现
    本文小编为大家详细介绍“Vue2响应式系统之深度响应怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue2响应式系统之深度响应怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1、场景import&...
    99+
    2023-06-30
  • Vue2响应式系统介绍
    目录一、响应式系统要干什么二、响应式数据三、保存当前正在执行的函数四、响应式数据五、Observer 对象六、测试七、总结前言: 目前工作中大概有 的需求是在用 ...
    99+
    2024-04-02
  • Vue2响应式系统之set和delete怎么用
    今天小编给大家分享一下Vue2响应式系统之set和delete怎么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。...
    99+
    2023-06-30
  • Vue2响应式系统之嵌套怎么实现
    这篇“Vue2响应式系统之嵌套怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue2响应式系统之嵌套怎么实现”文章吧...
    99+
    2023-06-30
  • vue2基本响应式实现方式之让数组也变成响应式
    目录vue2让数组也变成响应式vue2中的响应式原理有三点 解决缺点的方法总结vue2让数组也变成响应式 之前的代码有个问题,就是操作数组数据的时候,不会触发set let...
    99+
    2023-05-17
    vue2基本响应式 vue2数组 vue2数组变响应式
  • Vue2响应式系统有什么用
    这篇文章主要讲解了“Vue2响应式系统有什么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Vue2响应式系统有什么用”吧!一、响应式系统要干什么回到最简单的代码:data =&n...
    99+
    2023-06-30
  • Vue2响应式系统之分支切换怎么实现
    本篇内容介绍了“Vue2响应式系统之分支切换怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!场景我们考虑一下下边的代码会输出什么。im...
    99+
    2023-06-30
  • Vue2响应式系统之异步队列怎么实现
    这篇“Vue2响应式系统之异步队列怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue2响应式系统之异步队列怎么实现...
    99+
    2023-06-30
  • vue2响应式的缺点影响
    前言: 响应式:数据改变-->视图跟着变 对象新增的属性没有响应式 数组的部分操作没有响应式    push(),pop(),shift(),unshift...
    99+
    2024-04-02
  • vue2之响应式双向绑定,在对象或数组新增属性页面无响应的情况
    目录vue2响应式双向绑定,在对象或数组新增属性页面无响应问题描述解决方法vue2实现响应式数据JS中的对象属性利用Object.defineProperty()进行数据劫持与标签联...
    99+
    2023-05-17
    vue2响应式双向绑定 vue2双向绑定 vue2新增属性
  • 详解vue数据响应式原理之数组
    目录src/core/observer/index.jssrc/core/observer/array.js arrayMethods总结src/core/observer/inde...
    99+
    2024-04-02
  • vue2实现provide inject传递响应式
    1. vue2 中的常规写法 // 父级组件提供 'foo' var Provider = { data(){ return { foo: 'bar' ...
    99+
    2024-04-02
  • 浅谈Java响应式系统
    目录初识响应式系统什么是响应式系统响应式系统的四大特点及时响应性(Responsive)恢复性(Resilient)有弹性(Elastic)消息驱动(Message Driven)总...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作