[TOC]

纯函数 pure function

  • 相同的输入,永远会得到相同的输出
  • 没有副作用

    副作用 side effect

    函数外部环境发生的交互

  • 网络请求

  • DOM 操作
  • 订阅数据来源
  • 写入文件系统
  • 获取用户输入

    react副作用

    需要把依赖项传进去,不然的话,每次更新都会出发副作用。 ```typescript import React, { useEffect } from “react”; function Hello(props) { // 通过useEffect监听dom的更新,触发副作用 useEffect(() => { console.log(props.msg); // 只要有更新就会触发 }); return

    {props.msg}

    ; }

export default Hello;

<a name="NTjyV"></a>
### Vue 副作用处理
`watchEffect`中使用到的值才会触发

- 组件第一次初始化时,会触发` watchEffect`
- `watchEffect `回调里面的值,没有发生变化,就不会触发副总用
```typescript
import { defineComponent, watchEffect } from 'vue'
export default defineComponent {
  props: {
    msg: string
  }
  setup(props) {
    watchEffect(() => {
      console.log('props effect', props.msg); // watchEffect中使用到的值才会触发
    })
  }
}

深入 watchEffect

  • 自动收集依赖且触发
  • 自动销毁 effect
    • 在 setup 或生命周期钩子函数中使用 watchEffect ,在组件销毁时,副作用也会一起销毁
    • 可以手动清除
      const stop = watchEffect(() => {
      console.log('props effect', props.msg);
      });
      stop(); // 销毁
      

使副作用失效

  • watchEffect 中发送请求时,多次变化,势必多次请求
  • watchEffect向回调中提供参数,以停止未完成的副作用

    watchEffect((onInvalidate) => {
    console.log('props effect', props.msg);
    console.log('inner effect', count.value);
    const source = axios.CancelToken.source()
    axios.get(`https://jsonplaceholder.typicode.com/todos/${count.value}`, {
      cancelToken: source.token
    }).catch(err => {
      console.log(err.message);
    });
    onInvalidate(() => {
      source.cancel('trigger');
    });
    });
    


    副作用执行顺序
    watchEffect都是异步执行的
    watchEffect先执行
    DOM updated 再执行

    <template>
      <h1 ref="node">{{count}}</h1>
      <a herf="javascript:;" @click="count++">change</a>
    </template>
    <script>
    import { defineComponent, ref } from 'vue'
    export default defineComponent ({
    props: {
      msg: string
    }
    setup() {
        const count = ref(1)
      const node = ref<null | HTMLElement>(null);
      watchEffect(() => {
        const currentText = node.value ? node.value.innerText : '';
        console.log(currentText);
      }, {
        flush: 'post' // 默认是 pre:之前,
      })
      return {
        count,
        node
      };
    }
    });
    </script>
    
  • watchEffect 提供参数 flush 改变执行顺序。post 表示,在 DOM updated 之后再执行 watchEffect

  • React 的执行顺序不可以调整,都是在组件 updated 之后触发

watch 精准控制 effect

<template>
    <h1 ref="node">{{msg}}</h1>
    <h1>{{count}}</h1>
    <a herf="javascript:;" @click="count++">change</a>
</template>
<script>
import { ref, watch, toRefs } from 'vue'
export default defineComponent ({
  props: {
    msg: string
  }
  setup(props) {
    const node = ref<null | HTMLElement>(null);
    // 基本使用
    watch(count, (newV, oldV) => {
      console.log(count.value)
    });
    // 响应式对象的值
    // watch(props.msg, (newV, oldV) => {});// props 是只读的,拿出其中的值,这个值将不再是响应式对象
    // 方法 1
    const { msg } = toRefs(props.msg);
    watch(msg, (newV, oldV) => {});
    // 方法 2
    watch(() => props.msg, (newV, oldV) => {});

    // watch 多个值
    watch([() => props.msg, count] => props.msg, (newV, oldV) => {
      console.log(newV.value) // [第一个值,第二个值]
    });
    return {
      node
    };
  }
});

watch 的基本用法

watch(count, (newV, oldV) => {
  console.log(count.value)
});

watch 响应式对象的单个值

  • 使用 toRefs
  • 使用 getter 函数

    // watch(props.msg, (newV, oldV) => {});// props 是只读的,拿出其中的值,这个值将不再是响应式对象
    // 方法 1
    const { msg } = toRefs(props.msg);
    watch(msg, (newV, oldV) => {});
    // 方法 2
    watch(() => props.msg, (newV, oldV) => {});
    

    watch 多个值

  • 使用数组

    // watch 多个值
    watch([() => props.msg, count] => props.msg, (newV, oldV) => {
    console.log(newV.value) // [第一个值,第二个值]
    });
    

    和 watchEffect 对比

  • 懒执行副作用:针对某个值执行副作用

  • watch 可以定义什么状态应该触发 watcher 重新运行
  • watch 可以访问数据变化前后的值,watchEffect 不能

    自定义函数 - hooks

  • 将相关的 feature 组合在一起

  • 非常易于重用
  • 界面的需求 - 转化为数据的描述

    优点

  • 以函数的形式调用,清楚的了解参数和返回的类型,更好的提示

  • 避免命名冲突
  • 代码逻辑脱离组件存在 ```javascript

```javascript
// useURLLoader.js
import { reactive } from 'vue'; // 响应式对象
import axios from 'axios';
interface  DataProps<T> {
  result: T | null,
  loading: boolean,
  loaded: boolean,
  error: any
}
const useURLLoader = <T = any>(url: string) => {
  const data = reactive<DataProps<T>>({
    result: null,
    loading: true,
    loaded: false,
    error: null
  })
  axios.get(url).then(resp => {
    data.result = resp.data;
    data.loaded = true;
  }).catch(e => {
    data.error = e;
  }).finally(() => {
    data.loaded = false;
  });
  return data;
};

export default useURLLoader;

对比 React 的自定义 hooks

  • 更新数据的方式
  • 触发的时机
    • 为什么要包裹在 useEffect 中?
    • 删除了会有什么问题?
    • 为什么 Vue3 不需要这样做也可以?