我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue2响应式系统之分支切换怎么实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue2响应式系统之分支切换怎么实现

本篇内容介绍了“Vue2响应式系统之分支切换怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    场景

    我们考虑一下下边的代码会输出什么。

    import { observe } from "./reactive";import Watcher from "./watcher";const data = {    text: "hello, world",    ok: true,};observe(data);const updateComponent = () => {    console.log("收到", data.ok ? data.text : "not");};new Watcher(updateComponent); // updateComponent 执行一次函数,输出 hello, worlddata.ok = false; // updateComponent 执行一次函数,输出 notdata.text = "hello, liang"; // updateComponent 会执行吗?

    我们来一步一步理清:

    observer(data)

    拦截了 中 和 的 ,并且各自初始化了一个 实例,用来保存依赖它们的 对象。datatextokget、setDepWatcher

    Vue2响应式系统之分支切换怎么实现

    new Watcher(updateComponent)

    这一步会执行 函数,执行过程中用到的所有对象属性,会将 收集到相应对象属性中的 中。updateComponentWatcherDep

    Vue2响应式系统之分支切换怎么实现

    当然这里的 其实是同一个,所以用了指向的箭头。Watcher

    data.ok = false

    这一步会触发 ,从而执行 中所有的 ,此时就会执行一次 。setDepWatcherupdateComponent

    执行 就会重新读取 中的属性,触发 ,然后继续收集 。updateComponentdatagetWatcher

    Vue2响应式系统之分支切换怎么实现

    重新执行 函数 的时候:updateComponent

    const updateComponent = () => {    console.log("收到", data.ok ? data.text : "not");};

    因为 的值变为 ,所以就不会触发 的 , 的 就不会变化了。data.okfalsedata.textgettextDep

    而 会继续执行,触发 收集 ,但由于我们 中使用的是数组,此时收集到的两个 其实是同一个,这里是有问题,会导致 重复执行,一会儿我们来解决下。data.okgetWatcherDepWacherupdateComponent

    data.text = "hello, liang"

    执行这句的时候,会触发 的 ,所以会执行一次 。但从代码来看 函数中由于 为 , 对输出没有任何影响,这次执行其实是没有必要的。textsetupdateComponentupdateComponentdata.okfalsedata.text

    之所以执行了,是因为第一次执行 读取了 从而收集了 ,第二次执行 的时候, 虽然没有读到,但之前的 也没有清除掉,所以这一次改变 的时候 依旧会执行。updateComponentdata.textWatcherupdateComponentdata.textWatcherdata.textupdateComponent

    所以我们需要的就是当重新执行 的时候,如果 已经不依赖于某个 了,我们需要将当前 从该 中移除掉。updateComponentWatcherDepWatcherDep

    Vue2响应式系统之分支切换怎么实现

    问题

    总结下来我们需要做两件事情。

    • 去重, 中不要重复收集 。DepWatcher

    • 重置,如果该属性对 中的 已经没有影响了(换句话就是, 中的 已经不会读取到该属性了 ),就将该 从该属性的 中删除。DepWacherWatcherupdateComponentWatcherDep

    去重

    去重的话有两种方案:

    • Dep 中的 数组换为 。subsSet

    • 每个 对象引入 , 对象中记录所有的 的 ,下次重新收集依赖的时候,如果 的 已经存在,就不再收集该 了。DepidWatcherDepidDepidWatcher

    Vue2 源码中采用的是方案 这里我们实现下:2

    Dep 类的话只需要引入 即可。id

    let uid = 0;export default class Dep {    static target; //当前在执行的函数    subs; // 依赖的函数  id; // Dep 对象标识    constructor() {              this.id = uid++;              this.subs = []; // 保存所有需要执行的函数    }    addSub(sub) {        this.subs.push(sub);    }    depend() {        if (Dep.target) {            // 委托给 Dep.target 去调用 addSub            Dep.target.addDep(this);        }    }    notify() {        for (let i = 0, l = this.subs.length; i < l; i++) {            this.subs[i].update();        }    }}Dep.target = null; // 静态变量,全局唯一

    在 中,我们引入 来记录所有的 。Watcherthis.depIdsid

    import Dep from "./dep";export default class Watcher {  constructor(Fn) {    this.getter = Fn;        this.depIds = new Set(); // 拥有 has 函数可以判断是否存在某个 id        this.get();  }    get() {    Dep.target = this; // 保存包装了当前正在执行的函数的 Watcher    let value;    try {      value = this.getter.call();    } catch (e) {      throw e;    } finally {      this.cleanupDeps();    }    return value;  }    addDep(dep) {        const id = dep.id;    if (!this.depIds.has(id)) {      dep.addSub(this);    }      }    update() {    this.run();  }    run() {    this.get();  }}

    重置

    同样是两个方案:

    • 全量式移除,保存 所影响的所有 对象,当重新收集 的前,把当前 从记录中的所有 对象中移除。WatcherDepWatcherWatcherDep

    • 增量式移除,重新收集依赖时,用一个新的变量记录所有的 对象,之后再和旧的 对象列表比对,如果新的中没有,旧的中有,就将当前 从该 对象中移除。DepDepWatcherDep

    Vue2 中采用的是方案 ,这里也实现下。2

    首先是 类,我们需要提供一个 方法。DepremoveSub

    import { remove } from "./util";let uid = 0;export default class Dep {    static target; //当前在执行的函数    subs; // 依赖的函数    id; // Dep 对象标识    constructor() {        this.id = uid++;        this.subs = []; // 保存所有需要执行的函数    }    addSub(sub) {        this.subs.push(sub);    }      removeSub(sub) {        remove(this.subs, sub);    }      depend() {        if (Dep.target) {            // 委托给 Dep.target 去调用 addSub            Dep.target.addDep(this);        }    }    notify() {        for (let i = 0, l = this.subs.length; i < l; i++) {            this.subs[i].update();        }    }}Dep.target = null; // 静态变量,全局唯一

    然后是 类,我们引入 来保存所有的旧 对象,引入 来保存所有的新 对象。Watcherthis.depsDepthis.newDepsDep

    import Dep from "./dep";export default class Watcher {    constructor(Fn) {        this.getter = Fn;        this.depIds = new Set(); // 拥有 has 函数可以判断是否存在某个 id              this.deps = [];        this.newDeps = []; // 记录新一次的依赖        this.newDepIds = new Set();              this.get();    }        get() {        Dep.target = this; // 保存包装了当前正在执行的函数的 Watcher        let value;        try {            value = this.getter.call();        } catch (e) {            throw e;        } finally {                      this.cleanupDeps();                  }        return value;    }        addDep(dep) {        const id = dep.id;              // 新的依赖已经存在的话,同样不需要继续保存        if (!this.newDepIds.has(id)) {            this.newDepIds.add(id);            this.newDeps.push(dep);            if (!this.depIds.has(id)) {                dep.addSub(this);            }        }          }          cleanupDeps() {        let i = this.deps.length;        // 比对新旧列表,找到旧列表里有,但新列表里没有,来移除相应 Watcher        while (i--) {            const dep = this.deps[i];            if (!this.newDepIds.has(dep.id)) {                dep.removeSub(this);            }        }        // 新的列表赋值给旧的,新的列表清空        let tmp = this.depIds;        this.depIds = this.newDepIds;        this.newDepIds = tmp;        this.newDepIds.clear();        tmp = this.deps;        this.deps = this.newDeps;        this.newDeps = tmp;        this.newDeps.length = 0;    }          update() {        this.run();    }        run() {        this.get();    }}

    测试

    回到开头的代码

    import { observe } from "./reactive";import Watcher from "./watcher";const data = {    text: "hello, world",    ok: true,};observe(data);const updateComponent = () => {    console.log("收到", data.ok ? data.text : "not");};new Watcher(updateComponent); // updateComponent 执行一次函数,输出 hello, worlddata.ok = false; // updateComponent 执行一次函数,输出 notdata.text = "hello, liang"; // updateComponent 会执行吗?

    此时 修改的话就不会再执行 了,因为第二次执行的时候,我们把 中 里的 清除了。data.textupdateComponentdata.textDepWatcher

    “Vue2响应式系统之分支切换怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Vue2响应式系统之分支切换怎么实现

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

    下载Word文档

    猜你喜欢

    Vue2响应式系统之分支切换怎么实现

    本篇内容介绍了“Vue2响应式系统之分支切换怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!场景我们考虑一下下边的代码会输出什么。im
    2023-06-30

    Vue2响应式系统之深度响应怎么实现

    本文小编为大家详细介绍“Vue2响应式系统之深度响应怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue2响应式系统之深度响应怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1、场景import
    2023-06-30

    Vue2响应式系统之嵌套怎么实现

    这篇“Vue2响应式系统之嵌套怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue2响应式系统之嵌套怎么实现”文章吧
    2023-06-30

    Vue2响应式系统之异步队列怎么实现

    这篇“Vue2响应式系统之异步队列怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue2响应式系统之异步队列怎么实现
    2023-06-30

    Vue2响应式系统之set和delete怎么用

    今天小编给大家分享一下Vue2响应式系统之set和delete怎么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1、数组集
    2023-06-30

    Vue2响应式系统之怎么让数组生效

    这篇文章主要介绍了Vue2响应式系统之怎么让数组生效的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue2响应式系统之怎么让数组生效文章都会有所收获,下面我们一起来看看吧。1、场景import { observ
    2023-06-30

    Vue3响应式系统怎么实现computed

    首先,我们简单回顾一下:响应式系统的核心就是一个WeakMap---Map---Set的数据结构。WeakMap的key是原对象,value是响应式的Map。这样当对象销毁的时候,对应的Map也会销毁。Map的key就是对象的每个属性,value是依赖这个对象属性的effect函数的集合Set。然后用Proxy代理对象的get方法,收集依赖该对象属性的effect函数到对应key的Set中。还要代理对象的set方法,修改对象属性的时候调用所有该key的effect函数。上篇文章我们按照这样的思路
    2023-05-15

    使用Java怎么实现响应式系统

    使用Java怎么实现响应式系统?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。初识响应式系统ReactiveX的本质就是Observer+Iterator+函数编程+异步。是一个
    2023-06-15

    ThinkPHP中怎么实现分布式应用系统

    这篇文章主要讲解了“ThinkPHP中怎么实现分布式应用系统”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ThinkPHP中怎么实现分布式应用系统”吧!一、什么是分布式应用系统分布式应用系统
    2023-07-05

    使用CSS怎么实现一个响应式布局系统

    这篇文章将为大家详细讲解有关使用CSS怎么实现一个响应式布局系统,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。什么是csscss是一种用来表现HTML或XML等文件样式的计算机语言,主要是用
    2023-06-08

    怎么用代码实现一个迷你响应式系统vue

    这篇文章主要讲解了“怎么用代码实现一个迷你响应式系统vue”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用代码实现一个迷你响应式系统vue”吧!基本定义什么是响应式系统?学术上的定义,我
    2023-07-05

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录