首页 > 编程语言 >vue3源码-二、响应式原理effect的实现

vue3源码-二、响应式原理effect的实现

时间:2023-04-30 20:13:23浏览次数:42  
标签:function const target depsMap effect 源码 key vue3

effect实现

  1. 定义effect方法:

    export function effect(fn, options: any = {}) {
      // 创建响应式的effect
      const effect = createReactiveEffect(fn, options);
    
      // 默认会让effect先执行一次
      if (!options.lazy) {
        effect();
      }
      return effect;
    }
    
    let uid = 0;
    let activeEffect; // 存储当前的effect
    const effectStack = []; // 保存effect 保证依赖关系
    function createReactiveEffect(fn, options) {
      // 返回响应式的effect
      const effect = function reactiveEffect() {
        // 保证effect没有加入到effectStack中
        if (!effectStack.includes(effect)) {
          try {
            effectStack.push(effect);
            activeEffect = effect;
            return fn();
          } finally {
            effectStack.pop();
            activeEffect = effectStack[effectStack.length - 1];
          }
        }
      };
      effect.id = uid++; // 制作一个effect表示,用于区分effect
      effect._isEffect = true; // 用于表示这个是响应式effect
      effect.raw = fn; // 保留effect对应的原函数
      effect.options = options; // 在effect上保存用户属性
      return effect;
    }
    
  2. effect函数执行时,进行取值操作,让属性记住对应的effect函数。

    function createGetter(isReadonly = false, shallow = false) {
        return function get(target, key, receiver) {
          	// ...
            if (!isReadonly) { // effect函数执行时,进行取值操作,让属性记住对应的effect函数
                track(target, TrackOpTypes.GET, key);
            }
        }
    }
    

    需要构建track方法进行依赖收集

    // 可以拿到当前的effect
    const targetMap = new WeakMap();
    export function track(target, type, key) {
      // 此属性不需要收集,因为没有在effect使用
      if (activeEffect === undefined) {
        return;
      }
      let depsMap = targetMap.get(target);
      if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()));
      }
      let dep = depsMap.get(key);
      if (!dep) {
        depsMap.set(key, (dep = new Set()));
      }
      if (!dep.has(activeEffect)) {
        dep.add(activeEffect);
       	activeEffect.deps.push(dep);
      }
    }
    
  3. trigger触发更新

    对新增属性和修改属性做分类,在setter里面

    function createSetter(shallow = false) {
        return function set(target, key, value, receiver) {
            const oldValue = target[key];
            const hadKey =
                isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key);
            const result = Reflect.set(target, key, value, receiver);
            if (!hadKey) {
                // 新增属性
                trigger(target, TriggerOpTypes.ADD, key, value)
            } else if (hasChanged(value, oldValue)) {
                // 修改属性
                trigger(target, TriggerOpTypes.SET, key, value, oldValue)
            }
            return result;
        }
    }
    

    触发effect找到依次执行,trigger方法。

    // 找属性对应的effect让其执行(数组,对象)
    export function trigger(target, type, key?, newValue?, oldValue?) {
      // 如果这个属性没有,收集过依赖,那不需要做任何操作
      const depsMap = targetMap.get(target);
      if (!depsMap) return;
      // 所有的要执行的effect全部存放到一个新的集合中,最终一起执行
      const effects = new Set(); // 去重
      const add = (effectsToAdd) => {
        if (effectsToAdd) {
          effectsToAdd.forEach((effect) => effects.add(effect));
        }
      };
    
      // 1.看修改的是不是数组的长度,因为修改长度影响比较大
      if (key === "length" && isAarray(target)) {
        //   如果对应的长度有依赖收集需要更新
        depsMap.forEach((dep, key) => {
          // 如果更改的长度小于收集的索引,那么这个索引也需要触发effect重新执行
          if (key === "length" || key > newValue) {
            add(dep);
          }
        });
      } else {
        // 可能是对象
        // 这里是修改,不能是新增
        if (key !== undefined) {
          add(depsMap.get(key)); // 如果实现新增
        }
        //   如果修改数组中的某一个索引
        switch (type) {
          case TriggerOrTypes.ADD:
            if (isAarray(target) && isIntegerKey(key)) {
              add(depsMap.get("length"));
            }
        }
      }
      effects.forEach((effect: any) => effect());
    }
    

标签:function,const,target,depsMap,effect,源码,key,vue3
From: https://www.cnblogs.com/dgqp/p/17365708.html

相关文章

  • JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"
    JSP的本质原理解析:"编写的时候是JSP,心里想解读的是java源码"@目录JSP的本质原理解析:"编写的时候是JSP,心里想解读的是java源码"每博一文案1.JSP概述2.第一个JSP程序3.JSP的本质就是Servlet4.JSP的基础语法4.1在JSP文件中直接编写文字4.2在JSP中编写Java程序......
  • vue3 vueRouter4 :No match found for location with path ***
    0.采用vue+router4做路由导航.首次载入控制台很干净.F5刷新后,控制台爆出警告,但点击路由正常工作.1.经过排查发现,是menu中使用了<router-link>这玩意,后来改造成  @click="router.push(ele.path)"即可消除警告 2.网络上各种方式我均尝试过,都是无效方案,比如:......
  • koa-compose 源码解析
    Koa-Compose函数解析1'usestrict'23/**4*Exposecompositor.5*/67module.exports=compose89/**10*Compose`middleware`returning11*afullyvalidmiddlewarecomprised12*ofallthosewhicharepassed.13*14......
  • koa-cors 源码及基本原理解析
    cors: 跨域资源共享(Cross-OriginResourceSharing)是一种机制,用来允许不同源服务器上的指定资源可以被特定的Web应用访问。在koa项目中使用cors中间件:eg:1varkoa=require('koa');2varroute=require('koa-route');3varcors=require('koa-cors');4varapp......
  • 从源码编译并安装LXQT
    平台:ubuntu-22.04-server-amd64.对象:LXQT.文件:lxqt.LXQt是由LXDE-Qt和RazorQt合并的项目,它的目标是创建一个轻量级、模块化、运行快并且简单易用的桌面环境。本例中会介绍多种发行版下的编译方法,本例中使用Ubuntu22.04,你也可以使用其它发行版。1.安装编译环境CMake版本≥3.......
  • Django的message组件(源码分析)
    Django的Message组件(源码分析)1.配置#MESSAGE_STORAGE='django.contrib.messages.storage.fallback.FallbackStorage'#MESSAGE_STORAGE='django.contrib.messages.storage.cookie.CookieStorage'MESSAGE_STORAGE='django.contrib.messages.stor......
  • SpringBoot读取.yml配置文件最常见的两种方式-源码及其在nacos的应用
    三、第二种方式(推荐)这种方式是小编比较推荐的,虽然看似比​​@Value​​麻烦不少,但是更加的规范,在配合nacos的时候也可以动态的修改,会立即生效,一会小编带大家试一下哈!!为什么推荐这种方式呢,是因为spring他们都是使用这种方式进行配置的,所以跟着官方走不会有错的!!1.修改yml文件我们......
  • vue3源码-一、响应式原理reactive的实现
    reactive的实现使用:使用reactive()函数创建一个响应式对象。import{reactive}from'vue'exportdefault{//`setup`是一个专门用于组合式API的特殊钩子函数setup(){conststate=reactive({count:0})//暴露state到模板return{......
  • 使用 ChatGPT 生成 Vue3 响应式导航栏组件
    下面是ChartGPT生成的Vue3响应式导航栏组件。经过简单的调试。基本可实现描述的要求。Nav.vue<template><navclass="nav-container"><divclass="logo-container"><imgsrc="your-logo-here.svg"alt="logo"/></......
  • 第四篇:白话tornado源码之褪去模板外衣的前戏
    原笔记博客链接:https://www.cnblogs.com/wupeiqi/p/4592637.html 执行字符串表示的函数,并为该函数提供全局变量本篇的内容从题目中就可以看出来,就是为之后剖析tornado模板做准备,也是由于该知识点使用的巧妙,所有就单独用一篇来介绍了。废话不多说,直接上代码:#!u......