素材巴巴 > 程序开发 >

ReactVue 系列:变量改动的监听

程序开发 2023-09-22 08:18:48

02_logo_490_230.png

 

背景:作为使用三年 react.js 的搬运工,目前正在积极学习 vue.js 语法,而在学习的过程中,总是喜欢和 react.js 进行对比,这里也不是说谁好谁坏,而是怀有他有我也有,他没我还有的思想去学习,去总结。

React18 Vue3

在上一章,介绍了React&Vue 系列: 变量的定义。那么本章就来介绍,定义变量之后,当变量被修改了,如何进行监听。

为什么需要进行监听呢?因为当变量改变之后,会存在相应的副作用操作(比如说网络请求,修改 DOM 状态等等),那么这时候就需要进行监听。

好了,直接进入正题。

老规矩,对 React 比较熟悉,React 有优先权。

React 监听变量改动

在 React 中,并没有单独的提供方法用来监听变量的改动。而且还必须清楚一点,当变量发生改变时,组件就会重新渲染(re-render)。到了这里,你也许还需要理解 React 批量更新。

Effect 在 React 中是专有定义——由渲染引起的副作用。为了指代更广泛的编程概念,也可以将其称为“副作用(side effect)”。

当组件进行重新渲染之后,就会执行一些函数(类似生命周期),在这些函数中就能做一些副作用的逻辑操作。在 React 中提供了 useEffect 和 useMemo 等 hook。

js复制代码import { useEffect, useMemo } from "react";
 

这里的 useMemo 也可以简单的看成是变量的副作用处理方式吧,当变量发生改变,会生成新的衍生值,进行渲染。

而最主要的处理副作用的函数就是 useEffect, 监听变量发生改变,处理一系列的操作(比如说网络请求,DOM 操作等等)。

js复制代码// 基本使用方式
 useEffect(setup, dependencies?)
 

执行副作用

直接看代码吧

js复制代码import { useState, useEffect } from "react";const [pagination, setPagination] = useState(1);/*** 请求表格数据* @param pagination: 页数*/
 function getTableDataSource(pagination) {// fetch...
 }useEffect(() => {getData(pagination);
 }, [pagination]);return (// jsx 的 DOM
 )
 

这里就想象成表格的分页场景吧,当点击表格分页,就会改变 pagination 变量,那么就会使当前组件重新渲染,而当前组件中的 useEffect 的依赖 pagination 发生了变动(内部通过 Object.is() 来进行比较), 就会执行副作用,重新请求表格数据。

这就是 React 监听变量改动,触发副作用的简单流程,还是比较简单的。

当然,React 内部还提供了 useLayoutEffect 和 useInsertionEffect 两个处理副作用的 hook,但是很少使用。 该两个语法跟 useEffect 一致,但是它们还是有着各自的不同。 useEffect、useLayoutEffect、useInsertionEffect 的区别和选择

取消副作用

组件的每次更新,都会触发副作用。那么如果更新过快,那么就会存在一种现象,就是上次副作用还没有执行完成,下一次的副作用执行又开始了,就又可能会形成一种竞态。所以,为了避免这种现象,在执行下一次副作用,就需要清空上一次的副作用。

在上面已经了解到, useEffect 的第一个参数为 setup函数,该函数返回另外一个函数,就是用来清理副作用的。

scss复制代码useEffect(() => {// 副作用函数体return () => {// cleanup 函数体};
 }, []);
 

cleanup 的执行时机:

案例演示:

js复制代码import { useState, useEffect } from "react";
 const [pagination, setPagination] = useState(1);const controller = new AbortController();
 const { signal } = controller;async function getTableDataSource(pagination) {const res = await fetch(xxx, {signal})
 }useEffect(() => {getData(pagination);return () => {// 取消 fetch 请求controller.abort()}
 }, [pagination]);return (// jsx 的 DOM
 )
 

这里是伪代码,理解其中的意思即可。

Vue 监听变量改动

在 Vue 中监听变量改动就非常简单,并且容易理解。因为在 Vue 中就提供了两个函数(watch,watchEffect),是专门用于变量的监听,就没有 React 组件那一套重新渲染流程。是不是心情愉悦一点啦~~

在没开始之前,先说说个人感受吧。虽然 Vue 提供了两个函数,是便于理解了;但是吧,其中的语法要点也确实太多了,给人一种学不尽的感觉。当然,仅仅只是基本使用的话,还是比较容易上手的。

在 Vue3 中提供了两个函数:watch 和 watchEffect,使用的时候导入即可。

js复制代码import { watch, watchEffect } from "vue";
 

先从 watch 函数开说吧,watchEffect 函数是为了更加的方便使用 watch,当然这里不是指的语法糖哈。

watch

watch() 监听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。

js复制代码// 这里就不粘贴官网上的 ts 定义了,比较的繁琐
 watch(source, callback, options?)
 

第一个参数 source: 监听的数据源,有四种类型:


第二个参数 callback: 副作用(一个回调函数) 当数据发生了变化,要执行的副作用函数代码。该回调函数也接受三个参数:

js复制代码function clear() {// 清除副作用逻辑
 }
 onCleanup(clear);
 

第三个参数 options: 配置项(可选参数)

  • onTrack / onTrigger:调试使用,可以忽略。

  • 返回值 stop 调用 watch 函数,也是存在一个返回值的,一个用于停止监听的函数。

    js复制代码const stop = watch(pagination, callback);// 当页数大于 10 页时,就停止监听
     if (pagination > 10) {stop();
     }
     

    到了这里,就会发现 watch 函数知识点真的比较多的。


    代码演示

    js复制代码import { watch, ref } from "vue";const pagination = ref(1);
     const controller = new AbortController();
     const { signal } = controller;// 数据请求
     async function getTableDataSource(pagination) {const res = await fetch(xxx, { signal });
     }function clear() {// 取消 fetch 请求controller.abort();
     }watch(pagination,(newValue, oldValue, onCleanup) => {getTableDataSource(newValue);// 清除副作用onCleanup(clear);},{immediate: true,flush: "post",}
     );
     

    伪代码,理解意思即可

    watchEffect

    watchEffect() 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

    为什么有了 watch() 函数,还需要 watchEffect() 函数?

    对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。 如果需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性。

    js复制代码// 这里就不粘贴官网上的 ts 定义了,比较的繁琐
     watchEffect(effect, options?)
     

    第一个参数 effect: 副作用函数

    当监听的依赖发生变化时,就会触发该副作用函数。该副作用函数接受一个参数:

    js复制代码function clear() {// 清除副作用逻辑
     }
     onCleanup(clear);
     

    跟 watch 的使用方式是一样的。


    第二个参数 options:配置项

    跟 watch 的配置项少了两个属性。

    该函数本来就是立即执行,就是为了收集依赖,所以就不需要 immediate 属性

    该函数时自动收集依赖的,特定值,就没有所谓的深度监听,所以就不需要 deep 属性。

  • onTrack / onTrigger:调试使用,可以忽略。
  • 这里还有两个语法糖的函数

    js复制代码import { watchPostEffect, watchSyncEffect } from "vue";
     

    就是根据 watchEffect 的配置对象,组合成的函数。


    返回值 stop 调用 watch 函数,也是存在一个返回值的,一个用于停止监听的函数。

    js复制代码const stop = watchEffect(callback);// 当页数大于 10 页时,就停止监听
     if (pagination > 10) {stop();
     }
     

    代码演示

    js复制代码import { watchEffect, ref } from "vue";const pagination = ref(1);
     const controller = new AbortController();
     const { signal } = controller;// 数据请求
     async function getTableDataSource(pagination) {const res = await fetch(xxx, { signal });
     }function clear() {// 取消 fetch 请求controller.abort();
     }watchEffect(() => {// 自动收集了 pagination 依赖getTableDataSource(pagination);
     });
     

    额外知识

    React 批量更新

    在 React 的函数组件中,是可以同时定义多个变量的。当一个变量发生改变的时候,组件就会重新渲染一次;当多个变量同时修改时,渲染又会存在不同的形式。

    在个人前面的博客中,写了一篇关于 React 的批量更新,React 系列:useState 和 setState 的执行机制(对比,包含 React18),可以推荐看一下,这里就简单总结一下:

    React 18 之前:

    React 18:

    useEffect、useLayoutEffect、useInsertionEffect 的区别和选择

    相同点:

    三者都是属于 React 提供的 hook,而 useInsertionEffect 是 React18 才出来的。它们的使用方式是都是一样的:

    js复制代码useEffect(setup, dependencies?)
     

    两个参数:

    1. setup 函数(也就是所谓的副作用),返回一个清理副作用函数(cleanup)
    2. dependencies 依赖,可选参数。

    不同点:

    函数的执行时机不同

    使用场景不同

    为了深入理解,读取了梳理 useEffect 和 useLayoutEffect 的原理与区别,从源码层面理解一下它们之间的区别,整理了一张流程图。

    我是一个小小小菜鸡,我看不懂源码,哈哈哈,只是根据别人的思路,理顺一下知识点。

    02_useEffect.png

     

    总结

    React 通过 useEffect,useMemo 函数来实现对变量改动的监听。

    Vue 通过 watch,watchEffect 函数来实现对变量改动的监听。

    在这里还是说一下自己的感受,Vue3 设计走向函数式编程,在 watch,watchEffect 两个函数里面充分体现出来,不停的函数嵌套,哈哈哈。watch,watchEffect 的语法确实过于偏多,学习难度较大。

    React 和 Vue 在这一块,各自的难点:

    相信你们都能掌握吧。有误,请多多指教~~~

     


    标签:

    素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。