[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(); // 销毁
- 在 setup 或生命周期钩子函数中使用
使副作用失效
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
Loading todo{{ todo.result && todo.result.title }}
```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 不需要这样做也可以?