1、数组集

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

new Watcher(updateComponent);

list[0] = 3;

list[0]会触发 的重新执行吗?updateComponent

可以先思考一下。

答案是否定的,数组我们只能通过重写的 、 等方法去触发更新,详见pushspliceVue2响应式系统之数组 。

如果我们想要替换数组某个元素的话可以转一下弯,通过 去实现。splice

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

new Watcher(updateComponent);

// list[0] = 3;
data.list.splice(0, 1, 3);

每次这样写太麻烦了,我们可以提供一个 方法供用户使用。set

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
export function set(target, key, val) {
    if (Array.isArray(target)) {
        target.length = Math.max(target.length, key);
        target.splice(key, 1, val);
        return val;
    }

    // targe 是对象的情况
    // ...
}

然后我们直接使用 方法就可以了。set

import { observe, set } from "./reactive";
import Watcher from "./watcher";
const data = {
    list: [1, 2],
};
observe(data);
const updateComponent = () => {
    console.log(data.list);
};

new Watcher(updateComponent);

// list[0] = 3;
// data.list.splice(0, 1, 3);
set(data.list, 0, 3);

2、数组 del

同数组 ,我们顺便提供一个 的方法,支持数组响应式的删除某个元素。setdel

/**
 * Delete a property and trigger change if necessary.
 */
export function del(target, key) {
    if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.splice(key, 1);
        return;
    }
    // targe 是对象的情况
    // ...
}

3、对象 set

import { observe, set, del } from "./reactive";
import Watcher from "./watcher";
const data = {
    obj: {
        a: 1,
        b: 2,
    },
};
observe(data);
const updateComponent = () => {
    const c = data.obj.c ? data.obj.c : 0;
    console.log(data.obj.a   data.obj.b   c);
};

new Watcher(updateComponent);

data.obj.c = 3;

updateComponent 方法中虽然使用了 的 属性,但是在调用 之前, 中并没有 属性,所以 属性不是响应式的。objcobservedata.objcc

当我们修改 的值的时候,并不会触发 的执行。data.obj.cupdateComponent

如果想要变成响应式的话,一种方法就是在最开始就定义 属性。c

const data = {
    obj: {
        a: 1,
        b: 2,
        c: null,
    },
};
observe(data);
const updateComponent = () => {
    const c = data.obj.c ? data.obj.c : 0;
    console.log(data.obj.a   data.obj.b   c);
};

new Watcher(updateComponent);

data.obj.c = 3;

另一种方法就是通过 去设置新的属性了,在 中我们可以将新添加的属性设置为响应式的。setset

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
export function set(target, key, val) {
    if (Array.isArray(target)) {
        target.length = Math.max(target.length, key);
        target.splice(key, 1, val);
        return val;
    }

    // targe 是对象的情况
    // key 在 target 中已经存在
    if (key in target && !(key in Object.prototype)) {
        target[key] = val;
        return val;
    }

    const ob = target.__ob__;
    // target 不是响应式数据
    if (!ob) {
        target[key] = val;
        return val;
    }
  	// 将当前 key 变为响应式的
    defineReactive(target, key, val);
    return val;
}

回到我们之前的程序:

import { observe, set, del } from "./reactive";
import Watcher from "./watcher";
const data = {
    obj: {
        a: 1,
        b: 2,
    },
};
observe(data);
const updateComponent = () => {
    const c = data.obj.c ? data.obj.c : 0;
    console.log(data.obj.a   data.obj.b   c);
};

const ob = new Watcher(updateComponent);

set(data.obj, "c", 3);

虽然通过 增加了属性,但是此时 并不会重新触发,原因的话我们看下依赖图。setWatcher

image-20220409102100995

虽然属性 拥有了 对象,但由于没有调用过依赖属性 的 ,所以它并没有收集到依赖。cDepcWatcher

当然我们可以 完手动调用一次相应的 。setWatcher

const data = {
    obj: {
        a: 1,
        b: 2,
    },
};
observe(data);
const updateComponent = () => {
    const c = data.obj.c ? data.obj.c : 0;
    console.log(data.obj.a   data.obj.b   c);
};

const ob = new Watcher(updateComponent);

set(data.obj, "c", 3);
ob.update(); // 手动调用 Watcher

data.obj.c = 4;

这样的话,当执行 的时候就会触发 的执行了。data.obj.c = 4Watcher

那么我们能将触发相应的 的逻辑放到 函数中吗?Watcherset

image-20220409102100995

可以看到 里也有个 ,这个其实当时是为数组准备的,参考 objDepVue2响应式系统之数组,但 的 什么都没收集。objdep

我们修改一下代码让它也收集一下:

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) {
                  	/******新位置 *************************/
                		childOb.dep.depend();
                  	/**********************************/
                    if (Array.isArray(value)) {
                       // childOb.dep.depend(); //原来的位置
                        dependArray(value);
                    }
                }
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            childOb = !shallow && observe(newVal);
            dep.notify();
        },
    });
}

function dependArray(value) {
    for (let e, i = 0, l = value.length; i < l; i  ) {
        e = value[i];
      	/******新位置 *************************/
        e && e.__ob__ && e.__ob__.dep.depend();
      	/**********************************/
        if (Array.isArray(e)) {
           //  e && e.__ob__ && e.__ob__.dep.depend(); // 原位置
            dependArray(e);
        }
    }
}

因为读取 属性,一定先会读取 属性,即 。 也同理。aobjdata.obj.ab

所以通过上边的修改, 的 会收集到它的所有属性的依赖,也就是这里的 、 的依赖,但因为 和 的依赖是相同的,所以收集到一个依赖。objdepabab

image-20220409104705075

但其实我们并不知道 被哪些 依赖,我们只知道和 同属于一个对象的 和 被哪些 依赖,但大概率 也会被其中的 依赖。cWatchercabWatchercWatcher

所以我们可以在 中手动执行一下 的 ,依赖 的 大概率会被执行,相应的 也会成功收集到依赖。setobjDepcWatcherc

export function set(target, key, val) {
    if (Array.isArray(target)) {
        target.length = Math.max(target.length, key);
        target.splice(key, 1, val);
        return val;
    }

    // targe 是对象的情况
    // key 在 target 中已经存在
    if (key in target && !(key in Object.prototype)) {
        target[key] = val;
        return val;
    }

    const ob = target.__ob__;
    // target 不是响应式数据
    if (!ob) {
        target[key] = val;
        return val;
    }
    defineReactive(target, key, val);
   /******新增 *************************/
    ob.dep.notify() 
    /************************************/
    return val;
}

回到最开始的代码:

const data = {
    obj: {
        a: 1,
        b: 2,
    },
};
observe(data);
const updateComponent = () => {
    const c = data.obj.c ? data.obj.c : 0;
    console.log(data.obj.a   data.obj.b   c);
};

const ob = new Watcher(updateComponent);

set(data.obj, "c", 3);

执行完后 除了变为响应式的,也成功触发了 执行,并且收集到了 。cWatcherWatcher

image-20220409105217629

此时如果修改 的值,也会成功触发 的执行了。cWatcher

4、对象 del

有了上边的了解,删除就很好解决了。

image-20220409105217629

如果要删除 属性,删除后执行下它相应的 就可以。但 的 是存在闭包中的,我们并不能拿到。aDepaDep

退而求其次,我们可以去执行 属性所在的对象 的 就可以了。aobjDep

/**
 * Delete a property and trigger change if necessary.
 */
export function del(target, key) {
    if (Array.isArray(target)) {
        target.splice(key, 1);
        return;
    }
    // targe 是对象的情况
    const ob = target.__ob__;
    if (!hasOwn(target, key)) {
        return;
    }
    delete target[key];
    if (!ob) {
        return;
    }
    ob.dep.notify();
}

5、总结

通过为对象收集依赖,将对象、数组的修改、删除也变成响应式的了,同时为用户提供了 和 方法。

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

Vue2响应式系统之set和delete的更多相关文章

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. 缺少ios开发签名身份(null)

    当我尝试生成ipa文件时,我收到此错误.无法解决.请帮我解决此错误:我有自己的帐户,在我的钥匙链中访问它的鞋子像这样:我没有使用新的Mac,我已经创建了ipa.Day.但今天无法做到.我也有.cer个人资料.它有效解决方法这是Apple发表的声明.Thanksforbringingthistotheattentionofthecommunityandapologiesfortheissuesyou

  3. swift tableview中添加侧滑删除功能 类似qq删除

    在UITableViewDelegate中继承三个方法functableView(tableView:UITableView,canEditRowAtIndexPathindexPath:NSIndexPath)->BoolfunctableView(tableView:UITableView,editingStyleForRowAtIndexPathindexPath:NSIndexPath)-

  4. swift http post/get/delete/put

    Swift中的HTTP请求iOS开发中大部分App的网络数据交换是基于HTTP协议的。这个库通过封装NSURLSession,提供了GET/POST/PUT/DELETE以及上传和下载的支持,比较全面:4.使用Objective-C中的AFNetworking网络库AFNetworking库是在iOS开发领域享有盛名、功能强大的网络请求库。

  5. 使用swift开发OSX应用

    选择Swift作为开发语言,保持所有选项框反选,documentextension栏保留为空白。确保“AlsocreateXIBfileforuserinterface”为勾选,然后点Next。需要注意的是,在MacApp中,有大量的类和iOS中都类似,只不过是以NS前缀命名。在applicationDidFinishLaunching方法内,加入以下语句:masterViewController=MasterViewControllerwindow.contentView.addSubviewmaste

  6. 制作一个可以滑动操作的 Table View Cell

    本教程将会向你展示如何制作一个这样的TableViewCell,而不用因嵌套的ScrollView陷入困境。如果你还不知道一个可滑动的TableViewCell意味着什么,那么看看Apple的邮件应用:可能你会想,既然Apple展示了这种方案,那它应该已将其开放给开发者使用了。这会要求你深入研究iOS7UITableViewCell的结构,以便复制出我们需要的行为。打开MasterViewController.m并找到viewDidLoad。这个循环添加了一些字符串到_objects数组,应用运行时,这些

  7. 从Coredata Swift中删除数据

    而我正在尝试删除一个项目。更新我的编码问题,执行swift和coredata中的数据删除。这个代码我结束了,工作。编辑上面的Swift2.2和Xcode7.3.1还需要这两行代码来纠正。

  8. 从特定的Realm对象Swift中删除所有数据

    在我对我的问题太深入之前我的目标,可能会影响你的答案,是删除对象数据,如果它不再在云中.所以如果我有一个数组[“一”,“二”,“三”]然后在我的服务器我删除“两个”我想要我的境界来更新这个变化.我认为最好的方法是删除特定对象中的所有数据,然后调用我的RESTAPI来下载新的数据.如果有更好的方法,请让我知道.好的,这是我的问题.我有一个对象通知()每次调用我的RESTAPI之前,下载我运行的任何东

  9. android Volley删除方法,为什么会发送空参数

    我有一些不明白从服务器发送请求与json和DELETE方法的问题.请求成功连接到服务器但服务器将收到的已发送参数为空.但标题请求工作正常!

  10. android – 如何建议使用contentResolver的delete方法注入安全?

    您可以使用URI通过内容解析器进行删除,也可以通过将某些参数传递给where参数.如何使参数成为sql注入安全?是否可以使用Prepared语句与ContentResolver?解决方法使用位置参数.例如

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部