Vue2 响应式系统之深度响应

 更新时间:2022年4月12日 23:48  点击:639 作者:windliang

1、场景

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: {
        innerText: {
            childText: "hello",
        },
    },
};
observe(data);
const updateComponent = () => {
    console.log(data.text.innerText.childText);
};

new Watcher(updateComponent);
data.text.innerText.childText = "liang";

我们的响应式系统到现在还没有支持属性是对象时候的响应,因此我们改变 的时候不会有任何输出。childText

我们只收集了 的依赖,所以如果想要响应的话必须给 整个赋值为一个新对象。data.textdata.text

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: {
        innerText: {
            childText: "hello",
        },
    },
};
observe(data);
const updateComponent = () => {
    console.log(data.text.innerText.childText);
};

new Watcher(updateComponent);
data.text = {
    innerText: {
        childText: "liang",
    },
};

image-20220405115503890

我们当然不希望每次都赋值整个对象,我们需要做一些修改,把嵌套的对象也变成响应式的。

2、方案

我们只需要在给某个 重写 和 之前,把它的 就像上边给 调用 函数一样,也调用一次 函数即可。keygetsetvaluedataobserveobserve

同时提供 参数,留下扩展,让外界决定是否需要深度响应。shallow

/*******************新增 shallow*******************/
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
    /*******************新增****************************/
    !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();
        },
    });
}

同时,在 函数中,传进来的 不是对象的话我们直接 。observevaluereturn

/*
util.js
export function isObject(obj) {
    return obj !== null && typeof obj === "object";
}
*/
export function observe(value) {
    if (!isObject(value)) {
        return;
    }
    let ob = new Observer(value);
    return ob;
}

3、场景2

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: {
        innerText: {
            childText: "hello",
        },
    },
};
observe(data);
const updateComponent = () => {
    console.log(data.text.innerText.childText);
};

new Watcher(updateComponent);

data.text.innerText.childText = "liang";
data.text = {
    innerText: {
        childText: "liang2",
    },
};

data.text.innerText.childText = "liang3";

可以一分钟想一下上边会输出什么。

new Watcher(updateComponent); ,执行一次 输出 。updateComponenthello

data.text.innerText.childText = "liang"; ,我们已经解决了属性是对象的情况,因此这里也会输出 。liang

data.text = {
    innerText: {
        childText: "liang2",
    },
};

上边代码就是文章最开头的方法,因此也会触发函数执行,输出 。liang2

data.text.innerText.childText = "liang3"; 最后这句会执行吗?

答案是否定的了,因为我们的 赋值为了一个新对象,但这个新对象我们并没有将其设置为响应式的。data.text

因此我们需要在 的时候把对象也设置为响应式的。set

/**
 * Define a reactive property on an Object.
 */
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;
            }
          /******新增 *************************/
            childOb = !shallow && observe(newVal);
           /************************************/
            dep.notify();
        },
    });
}

4、总结

通过递归解决了属性是对象的依赖,可以为未来数组的依赖留下基础。

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

原文出处:https://vue.windliang.wang/

[!--infotagslink--]

相关文章

  • python实现学生通讯录管理系统

    这篇文章主要为大家详细介绍了python实现学生通讯录管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-25
  • 详解为什么现代系统需要一个新的编程模型

    如今高要求的分布式系统的建造者遇到了不能完全由传统的面向对象编程(OOP)模型解决的挑战,但这可以从Actor模型中获益。...2021-05-20
  • 护卫神 主机管理系统使用说明(MSSQL管理)

    护卫神·主机管理系统该版本支持在Windows Server 200320082012,含32位和64位,直接开设配置WEB站、FTP站,以及SQL Server和MySQL,是您开设和管理虚拟主机的绝好帮手。但是对于新用户可能在使用上有一些困难,因此请仔细阅读如下说明文档...2016-01-27
  • BootStrap栅格系统、表单样式与按钮样式源码解析

    这篇文章主要为大家详细解析了BootStrap栅格系统、表单样式与按钮样式源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-01-23
  • 利用C#修改Windows操作系统时间

    这篇文章主要介绍了利用C#修改Windows操作系统时间,帮助大家更好的利用c#操作系统,感兴趣的朋友可以了解下...2020-12-08
  • vivo OriginOS新系统如何更新 originos系统更新方法

    vivo新系统更新的步骤是什么?如何更新到vivo的最新系统?vivo的最新系统太亮眼了,不少vivo的用户都在跃跃欲试想体验一下最新的系统。vivo新系统虽然做出来了不过我们想体验的话还是要等待一段时间。到时大家通过下面的方法就可以使用到新系统了...2020-12-08
  • C#实现影院售票系统

    这篇文章主要为大家详细介绍了C#实现影院售票系统,解析了售票系统的难点,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • ColorOS7.2好不好用 ColorOS7.2系统升级体验

    ColorOS7.2系统怎么样?好不好用?值不值得升级?下面小编带来ColorOS7.2系统升级体验...2020-06-29
  • C#多线程编程中的锁系统(四):自旋锁

    这篇文章主要介绍了C#多线程编程中的锁系统(四):自旋锁,本文讲解了基础知识、自旋锁示例、SpinLock等内容,需要的朋友可以参考下...2020-06-25
  • Unity实现换装系统

    这篇文章主要为大家详细介绍了Unity实现换装系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-04-11
  • php需登录的文件上传管理系统

    本文给大家介绍一个不错的需要登录的php 文件上传管理系统,功能简单有需要了解的同学可参考。 代码如下<&#63;php$admin_pw="admin";//管理密码$uploaddir="upload";//上传目录session_start();if($_GET['action']=="g...2015-10-30
  • C++学生信息管理系统

    这篇文章主要为大家想详细介绍了C++学生信息管理系统的实现代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • 源码揭秘为什么 Vue2 this 能够直接获取到 data 和 methods

    本篇文章主要介绍的是Vue2 this 能够直接获取到 data 和 methods,阅读本文将能学到如何学习调试 vue2 源码、data 中的数据为什么可以用 this 直接获取到、methods 中的方法为什么可以用 this 直接获取到,需要的朋友可以参考一下...2021-09-23
  • C语言学生成绩管理系统小设计

    这篇文章主要为大家详细介绍了C语言学生成绩管理系统小设计,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • C#毕业设计之Winform零压健身房管理系统

    本文介绍了个人的《零压健身房管理系统(扁平化)》的基本流程和功能点的介绍,虚心接受各位的意见,欢迎在提出宝贵的意见,大家一起探讨学习...2021-09-26
  • .NET Core如何获取操作系统中的各种信息

    .net core是最近讨论频率很高的话题,下面这篇文章主要给大家介绍了关于利用.NET Core如何获取操作系统中各种信息的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧...2021-09-22
  • 基于jQuery和CSS3制作响应式水平时间轴附源码下载

    我们经常看到有很多的垂直时间轴用来记录事件进展,而有朋友问我要求分享水平的时间轴,其实水平时间轴的难点在于自适应屏幕尺寸。那么 今天我要给大家分享的是一款支持响应式、支持触屏手势滑动的水平时间轴...2015-12-21
  • 20分钟成功编写bootstrap响应式页面 就这么简单

    这篇文章主要教大家如何在20分钟内成功编写bootstrap响应式页面,其实很简单,培养大家分分钟开发出一个高大上的页面能力,感兴趣的小伙伴们可以参考一下...2016-05-14
  • C++顺序表实现图书管理系统

    这篇文章主要为大家详细介绍了C++顺序表实现图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-13
  • MySQL深度分页(千万级数据量如何快速分页)

    后端开发中经常需要分页展示,个时候就需要用到MySQL的LIMIT关键字。LIMIT在数据量大的时候极可能造成的一个问题就是深度分页。本文就介绍一下解决方法,感兴趣的可以了解一下...2021-07-25