Blog of CodeJELLOW’S ENGINEERING NOTES
← 返回文章

VUE3之Composition API(二)

VUE3之Composition API(二) Reactive API的补充 toRefs和toRef ref API的补充 customRef setup中computed的使用 watchEffect setup中使用ref watch

Reactive API的补充

isProxy:检查对象是否是由reactive或readonly创建的proxy.

isReactive:检查对象是否是由reactive创建的响应式代理;如果该代理是readonly建的,但包裹了由reactive 创建的另一个代理,它也会返回true ;

isReadonly:检查对象是否是由readonly 创建的只读代理。

toRaw:返回reactive或readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。

shallowReactive:创建一个响应式代理,它跟踪其自身property 的响应性,但不执行嵌套对象的深层响应式转换(深层还是原生对象)。

shallowReadonly:创建一个proxy,使其自身的 property为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。

toRefs和toRef

toRefs:将reactive对象中所有属性都转为ref,建立连接

toRef:对其中一个属性进行转换为ref,建立连接

TEXT
<script>
import { reactive, toRefs, toRef } from "vue";
    export default {
        setup() {
            const info=reactive({name:"sk",age:18});
            // toRefs:将reactive对象中所有属性都转为ref,建立连接
            // let { name, age }=toRefs(info);
            // toRef:对其中一个属性进行转换为ref,建立连接
            let {name}=toRefs(info);
            let age=toRef(info,"age");
            const change=()=>{
                // age.value++;
                info.age++;
            }

            return {
                name,
                age,
                change
            }
        }
    }
</script>

ref API的补充

unref:如果我们想要获取一个ref引用中的value,那么也可以通过unref方法︰如果参数是一个ref,则返回内部值,否则返回参数本身;这是val = isRef(val) ? val.value : val的语法糖函数;

isRef:判断值是否是一个ref对象。

shallowRef:创建一个浅层的ref对象

triggerRef:手动触发和shallowRef相关联的副作用。

TEXT
<script>
import { ref, shallowRef, triggerRef } from "vue";
    export default {
        setup() {
            // 浅层响应式
            const info=shallowRef({name:"sk"});
            const changeInfo=()=>{
                info.value.name="james";
                triggerRef(info);
            }
            return {
                info,
                changeInfo
            }
        }
    }
</script>

customRef

customRef可以是我们能够决定如何跟踪依赖与什么时候触发更新。其工厂函数需要传入track和trigger参数,并返回一个拥有get和set方法的对象:

TEXT
import { customRef } from "vue";
// 自定义ref
export default function(value){
    let timer=null;
    return customRef((track,trigger)=>{
        return {
            get(){
                track();
                return value;
            },
            set(newValue){
                clearTimeout(timer);
                timer=setTimeout(()=>{
                    value=newValue;
                    trigger();
                },1000);
            }
        }
    })
}

setup中computed的使用

计算属性需要调用computed函数来获得,computed函数支持两种传参:1.传入一个get函数;2.传入一个对象;返回值是一个ref对象:

TEXT
<script>
import {ref, computed} from "vue";
    export default {
        setup(props) {
            const firstName = ref("kobe");
            const lastName = ref("bryant");

            // 用法一:传入一个getter函数
            // 返回值为一个ref对象computedRef
            // const fullName=computed(()=>firstName.value+" "+lastName.value);
            // 用法二:传入一个对象
            const fullName=computed({
                get:()=>firstName.value+" "+lastName.value,
                set:(newValue)=>{
                    const names=newValue.split(" ");
                    firstName.value=names[0];
                    lastName.value=names[1];
                }
            })
            const changeName=()=>{
                // firstName.value="james";
                fullName.value="shop keeper";
            }
            return {
                fullName,
                changeName
            }
        }
    }
</script>

watchEffect

基本使用

watchEffect会自动收集响应式的依赖,对于一个watchEffect中的函数,它会在一开始自动执行一次,并记录到当中用到的响应式数据,以后每当这些响应式数据发生变化时,就会再次调用这个函数:

TEXT
<script>
import { ref, watchEffect} from "vue";
    export default {
        setup() {
            // watchEffect:自动收集响应式的依赖
            const name =ref("sk");
            const age=ref(18);

            const changeName=()=> name.value="shopkeeper";
            const changeAge=()=> age.value++;

            watchEffect(()=>{
                console.log("name:",name.value,"age:",age.value);
            })
            return {
                name,
                age,
                changeName,
                changeAge
            }
        }
    }
</script>

停止侦听

watchEffect方法会返回一个方法,用于停止侦听:

TEXT
<script>
import { ref, watchEffect} from "vue";
    export default {
        setup() {
            // watchEffect:自动收集响应式的依赖
            const name =ref("sk");
            const age=ref(18);

            const stop=watchEffect(()=>{
                console.log("name:",name.value,"age:",age.value);
            });

            const changeName=()=> name.value="shopkeeper";
            const changeAge=()=> {
                age.value++;
                if (age.value>25){
                    stop();
                }
            };

            
            return {
                name,
                age,
                changeName,
                changeAge
            }
        }
    }
</script>

watchEffect清除副作用

watchEffect的入参的函数中可以有一个入参,该入参也是一个函数,用于在再次执行watchEffect函数或watchEffect被停止时调用,一般取名为onInvalidate

TEXT
            const stop=watchEffect((onInvalidate)=>{
                const timer=setTimeout(()=>{
                    console.log("网络请求成功");
                },2000)
                // 根据name和age两个变量发送网络请求
                onInvalidate(()=>{
                    // 在这个函数中清除额外的副作用
                    // request.cancel();
                    clearTimeout(timer);
                    console.log("onInvalidate");
                })
                console.log("name:",name.value,"age:",age.value);
            });

watchEffect的执行时机

watchEffect的入参的第二个参数可用于定义watchEffect的执行时机:

TEXT
<script>
import { ref, watchEffect} from "vue";
    export default {
        setup() {
            const title= ref(null);

            watchEffect(()=>{
                console.log(title.value);
            },{
                flush:"post"
            });

            return{
                title
            }
        }
    }
</script>

flush的取值:post为先挂载在执行、pre为先执行再挂载(默认)、sync是同时进行(不推荐)。

setup中使用ref

直接在template的ref标签中使用setup的数据即可:

TEXT
<template>
    <div>
        <h2 ref="title">哈哈哈</h2>
    </div>
</template>

<script>
import { ref, watchEffect} from "vue";
    export default {
        setup() {
            const title= ref(null);

            watchEffect(()=>{
                console.log(title.value);
            },{
                flush:"post"
            });

            return{
                title
            }
        }
    }
</script>

watch

watch的API完全等同于组件watch选项的Property :watch需要侦听特定的数据源,并在回调函数中执行副作用;默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;

与watchEffect的比较,watch允许我们∶懒执行副作用(第一次不会直接执行);更具体的说明当哪些状态发生变化时,触发侦听器的执行;口访问侦听状态变化前后的值;

基本使用

watch方法的第一个参数是要监听的数据,可以是ref对象、reactive对象、数组、getter方法,第二个参数是数据改变时的回调,第三个参数时options

TEXT
<script>
import { ref, reactive, watch} from "vue";
    export default {
        setup() {
            const info =reactive({
                name: "sk",
                age:19
            });
            // 侦听watch是,传入一个getter函数
            // watch(()=>info.name,(newValue,oldValue)=>{
            //     console.log("newValue",newValue,"oldValue",oldValue);;
            // });

            // 传入可响应式对象:reactive对象/ref对象
            // reactive对象获取到的newValue和oldValue都是reactive对象
            // watch(info,(newValue,oldValue)=>{
            //     console.log("newValue",newValue,"oldValue",oldValue);;
            // });
            // 如果希望newValue和oldValue是一个普通的对象
            watch(()=>{
                return {...info};
            },(newValue,oldValue)=>{
                console.log("newValue",newValue,"oldValue",oldValue);;
            });
            // ref对象获取到的newValue和oldValue都是值的本身
            const name = ref("sk");
            watch(name,(newValue,oldValue)=>{
                console.log("newValue",newValue,"oldValue",oldValue);;
            });
            const changeData=()=>{
                info.name="shopkeeper";
                // name.value="shopkeeper";
            }
            return{
                changeData,
                info,
            }
        }
    }
</script>

watch侦听多个数据源

TEXT
<script>
import { ref, reactive, watch} from "vue";
    export default {
        setup() {
            // 定义响应式对象
            const info =reactive({
                name: "sk",
                age:19
            });
            const name = ref("sk");
            // 侦听多个数据源
            watch([()=>{
                return {...info}
            },name],([newInfo,newName],[oldInfo,oldName])=>{
                console.log(newInfo,newName,oldInfo,oldName);
            });
            const changeData=()=>{
                info.name="shopkeeper";
                // name.value="shopkeeper";
            }
            return{
                changeData,
                info,
            }
        }
    }
</script>

watch的选项

和options中的watch一样:

TEXT
<script>
import { ref, reactive, watch} from "vue";
    export default {
        setup() {
            // 定义响应式对象
            const info =reactive({
                name: "sk",
                age:19,
                friend:{
                    name:"kobe"
                }
            });
            // 源码中,传入reactive会默认开启deep侦听,而传入getter方法不会
            watch([()=>{
                return {...info}
            }],(newInfo,oldInfo)=>{
                console.log(newInfo,oldInfo);
            },{
                deep:true,
                immediate:true
            });
            const changeData=()=>{
                info.friend.name="james";
            }
            return{
                changeData,
                info,
            }
        }
    }
</script>

通过 RSS 订阅更新 →