当前位置: 首页 > news >正文

effect的参数和返回值

系列文章目录


第一章 vue 简单的effect实现

第二章 链表运用到响应式中

第三章 effect问题处理


前言

上一章我们处理了effect的嵌套问题,那么这一章我们处理一下effect的参数和返回值的问题

一、effect的参数

scheduler函数

首先我们要先知道effect的参数是什么,其实就是一个对象,对象里面有一个scheduler函数,当你传入了这个函数的时候,effect的之后等到响应式数据更新的时候就会执行这个函数

我们直接上代码看一下

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><script type="module">import {effect,ref,} from "./node_modules/vue/dist/vue.esm-browser.prod.js";const name = ref("vue");// 第二部分 自定义调度器schedulereffect(() => {console.log(name.value);},{scheduler() {console.log("自定义");},});setTimeout(() => {name.value = "react";}, 1000);</script></body>
</html>

执行结果应该是先打印vue,然后在打印自定义

根据上面官方的effect能看出,当name更新之后,下次执行的是scheduler这个函数,那我们处理一下这个问题

实现scheduler

scheduler的实现比较简单,还记得我们上一章effect函数是如何实现的吗? 

是用ReactiveEffect这个实例去实现的,那我们也需要去这里修改来实现

class ReactiveEffect {constructor(fn) {this.fn = fn;}run() {const prevSub = activeSub; //保存当前的effect,处理嵌套逻辑activeSub = this;console.log(prevSub?.fn);try {this.fn();} finally {// 执行完一次恢复到之前的activeSub = prevSub;}}// 通知更新调用这个函数notify() {this.scheduler();}scheduler() {this.run();}
}
function effect(fn, options) {const e = new ReactiveEffect(fn);// schedulerObject.assign(e, options);e.run();}

我们在effect函数中,合并用户传进来的对象,这里其实就是下面这个对象

  {scheduler() {console.log("自定义");},}

然后我们在ReactiveEffect这个类中再新增两个方法。分别是schedulernotify这两个方法,这个我相信大家都能看得懂吧。其实就是嵌套调用

最后我们需要修改propagate方法,为什么呢

不知道你们还记得这个方法当时我们是如何执行回调函数的?

答案是

function propagate(subs) {//  依次执行let link = subs; //保存头节点let queuedEffects = []; //保存需要执行的effectwhile (link) {// 注意:这里不能直接执行effect,因为effect可能会再次触发set,导致死循环queuedEffects.push(link.sub); //将effect保存到数组中link = link.nextSub; //指向下一个节点}// 依次执行queuedEffects.forEach((effect) => {effect.run();});
}

遍历执行effect.run(),但我们修改之后就不能执行run函数了,而是变成了effect.notify()

那最后代码就是如下显示

function propagate(subs) {//  依次执行let link = subs; //保存头节点let queuedEffects = []; //保存需要执行的effectwhile (link) {// 注意:这里不能直接执行effect,因为effect可能会再次触发set,导致死循环queuedEffects.push(link.sub); //将effect保存到数组中link = link.nextSub; //指向下一个节点}// 依次执行queuedEffects.forEach((effect) => {effect.notify(); //执行effect 2});
}

我们看一下结果

结果也是没有问题的

总结

以上是effect的参数处理方式,比较简单,大家跟着写一下基本就知道了

二、effect的返回值

返回值

老样子,我们先看下官方的返回值是什么

发现返回值其实就是run函数;

那就简单了啊,把run函数返回不就行了嘛,那好我们开始实现这块代码

function effect(fn, options) {const e = new ReactiveEffect(fn);// schedulerObject.assign(e, options);e.run();const run = e.run.bind(e);run.effect = e;return run;
}

就这么几行,但需要注意的是this的指向问题

代码

我们将目前所有的代码都贴出来,防止大家中间有什么错误遗漏的代码

effect.js代码

import { propagate, link } from "../common/sys.js";
class RefImpl {_v_isRef = true;/*** 订阅者链表头节点*/subs;/***  订阅者链表尾节点*/subsTail;constructor(value) {this.value = value;}get value() {if (activeSub) {trackRef(this);}return this._value;}set value(newVal) {// 触发更新this._value = newVal;// this.subs?.();triggerRef(this);}
}
function ref(val) {return new RefImpl(val);
}
let activeSub = null;// interface Link {
//   // effect(订阅者)
//   sub: Function;
//   nextSub: Link;
//   prevSub: Link;
//   dep: Dep; //依赖项
// }function trackRef(dep) {// 收集依赖/**TODO: 嵌套会导致外部的无法被收集 对应的index2.html* class ReactiveEffect {* constructor(fn) {* this.fn = fn;* }* run() {* activeSub = this;* try {* return this.fn();* } finally {* activeSub = undefined;*    }*  }* }*/if (activeSub) {link(dep, activeSub);}
}function triggerRef(dep) {if (dep.subs) {propagate(dep.subs); //触发更新}
}class ReactiveEffect {constructor(fn) {this.fn = fn;}run() {const prevSub = activeSub; //保存当前的effect,处理嵌套逻辑activeSub = this;console.log(prevSub?.fn);try {return this.fn();} finally {// 执行完一次恢复到之前的activeSub = prevSub;}}// 通知更新调用这个函数notify() {this.scheduler();}scheduler() {this.run();}
}
function effect(fn, options) {const e = new ReactiveEffect(fn);// schedulerObject.assign(e, options);e.run();const run = e.run.bind(e);run.effect = e;return run;}export { effect, ref };

sys.js

function propagate(subs) {//  依次执行let link = subs; //保存头节点let queuedEffects = []; //保存需要执行的effectwhile (link) {// 注意:这里不能直接执行effect,因为effect可能会再次触发set,导致死循环queuedEffects.push(link.sub); //将effect保存到数组中link = link.nextSub; //指向下一个节点}// 依次执行queuedEffects.forEach((effect) => {effect.notify(); //执行effect 2});
}
const link = function (dep, sub) {const newLink = {sub,nextSub: null,prevSub: null,};// 尾插法if (dep.subsTail) {dep.subsTail.nextSub = newLink;newLink.prevSub = dep.subsTail;dep.subsTail = newLink;} else {// 没有的话头尾一样dep.subs = newLink;dep.subsTail = newLink;}
};
export { propagate, link };

总结

以上就是effect参数和返回值的实现,这一章算是比较简单的,大家可以跟着手敲一下加深理解

http://www.dtcms.com/a/392461.html

相关文章:

  • GAMIT 10.71 问题记录
  • 【愚公系列】《人工智能70年》032-机器翻译拆除语言樊篱(自然语言处理阔步前进)
  • 隐私与合规内建:Python医疗AI编程中的SBOM、依赖监测与威胁建模实践分析(下)
  • 基于C++11手撸前端Promise
  • C++学习笔记——内存管理
  • AI热点周报(09.14~09.20):Gemini集成到Chrome、Claude 强化记忆、Qwen3-Next快速落地,AI走向集成化,工程化?
  • 网络服务阶段——作业
  • OpenLayers地图交互 -- 章节三:选择交互详解
  • RocksDB:C++中的RAII锁应用解析
  • Linux920 RHEL 8 YUM配置;root密码;文件夹 磁盘分区 磁盘
  • yarn命令介绍(替代npm命令的JavaScript包管理工具)
  • MFC中开源布局库---ResizableLib
  • Scade 6 编译原理的参考实现 - LustreC
  • MFC List 控件详解:高效数据展示与管理
  • 从根到叶的二进制数之和(霍纳法则)
  • 隐私与合规内建:Python医疗AI编程中的SBOM、依赖监测与威胁建模实践分析(上)
  • 基于实战:如何高效调用陌讯AIGC检测RESTful API进行批量内容审核
  • 如何用kimi写一个最小excel软件
  • Ansible-script模块
  • ansible批量给网络设备下发配置
  • 使用 Bright Data Web Scraper API Python 高效抓取 Glassd
  • uni-app 用scroll-view实现横向滚动
  • Kafka 图形界面客户端工具
  • 【开题答辩全过程】以 Php产品报价系统的设计与实现为例,包含答辩的问题和答案
  • 软件测试基础知识(网络协议)
  • 手机中的轻量化 AI 算法:智能生活的幕后英雄
  • wo店模式兴起旧模式式微:本地生活服务市场的深度变革
  • 服务器磁盘空间满了怎么办?阿里云ECS清理与云盘扩容教程
  • OpenAI推出更擅长AI代理编码的GPT-5-Codex,与Claude code有何区别?国内怎么使用到Codex呢?
  • GPT-5 深度测试报告:前端编程能力专项评估