Vue3源码学习之路

编程入门 行业动态 更新时间:2024-10-18 10:35:31

Vue3源码学习<a href=https://www.elefans.com/category/jswz/34/1770107.html style=之路"/>

Vue3源码学习之路

  • watch监控的是对象时,无法区分新值和老值,因为是对象引用的形式
  • 当监控一个函数时,函数的返回值就是老值
  • watch其实也是effect,会对用户填写的数据进行依赖收集

watch基础例子

packages/reativity/dist/index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><div id="app"></div><script src="./reactivity.global.js"></script><!-- <script src="../../../node_modules/vue/dist/vue.global.js"></script> --><script>const { watch, reactive } = VueReactivity;const state = reactive({ name: 'bowen', age: 18 });watch(() => state.age,(newValue, oldValue) => {console.log(`newValue: ${newValue}`, `oldValue: ${oldValue}`);});setTimeout(() => {state.age = 24;}, 1000);</script></body>
</html>

packages/reactivity/src/reactive.ts > reactive
reactive函数中二次代理判断优化一下

export function isReactive(value) {return !!(value && value[ReactiveFlags.IS_REACTIVE]);
}
// 如果传入的是已经代理过的Proxy无需再次代理
if (isReactive(target)) {return target;
}

新建:packages/reactivity/src/watch.ts

import { isFunction, isObject } from '@vue/shared';
import { ReactiveEffect } from './effect';
import { isReactive } from './reactive';/*** 遍历对象* @param value 对象* @param set 处理循环引用*/
function traversal(value, set = new Set()) {if (!isObject(value)) {return value;}if (set.has(value)) {return value;}set.add(value);for (const key in value) {traversal(value[key], set);}return value;
}/*** watch* @param source 传入的响应式对象* @param cb 回调*/
export function watch(source, cb) {let getter;if (isReactive(source)) {// 需要对传入的对象进行递归循环,循环时访问属性,就会执行effect依赖收集getter = () => traversal(source);} else if (isFunction(source)) {getter = source;} else {return;}let oldValue;let effect;// 值变化后执行const job = () => {const newValue = effect.run();cb(newValue, oldValue);oldValue = newValue;};effect = new ReactiveEffect(getter, job);oldValue = effect.run();
}

watch中并发异步请求

当我们监听的对象并发出多次修改,并且watch中执行异步请求时,需要按顺序执行并且只渲染最后一次更改

异步例子

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><div id="app"></div><script src="./reactivity.global.js"></script><!-- <script src="../../../node_modules/vue/dist/vue.global.js"></script> --><script>const app = document.getElementById('app');const { watch, reactive } = VueReactivity;const state = reactive({ name: 'bowen', age: 18 });let i = 3000;function getData(timer) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(timer);}, timer);});}watch(() => state.age,async (newValue, oldValue, onCleanup) => {let update = true;// onCleanup中传入的函数将被下一次watch调用的时候执行// 就是永远保持最新的更改排在最后一位进行渲染onCleanup(() => {update = false;});i -= 1000;let r = await getData(i);if (update) {app.innerHTML = r;}});state.age = 24;state.age = 25;state.age = 26;</script></body>
</html>

watch函数中cb增加onCleanup参数

export function watch(source, cb) {let getter;if (isReactive(source)) {// 需要对传入的对象进行递归循环,循环时访问属性,就会执行effect依赖收集getter = () => traversal(source);} else if (isFunction(source)) {getter = source;} else {return;}let oldValue;let effect;let cleanup;// 用户执行onCleanup时传入的函数const onCleanup = (fn) => {cleanup = fn;};// 值变化后执行const job = () => {// 下一次执行时触发上一次watch的清理if (cleanup) {cleanup();}const newValue = effect.run();cb(newValue, oldValue, onCleanup);oldValue = newValue;};effect = new ReactiveEffect(getter, job);oldValue = effect.run();
}

更多推荐

Vue3源码学习之路

本文发布于:2024-02-07 04:00:37,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1752845.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:之路   源码

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!