0
点赞
收藏
分享

微信扫一扫

【Vue3】响应性数据

曾宝月 2022-02-15 阅读 77

reactive & ref

  • 接收参数值,并返回一个响应式的 Proxy 对象,即参数值的响应式副本
  • 参数值:引用类型数据
  • 优势:即使只是修改引用类型数据的一部分数据,eg:对象的某属性 / 数组的某一项,页面的数据也会发生变化
    在 Vue2 中,对于引用类型数据,代理的是数据本身,需要修改整个数据,页面的数据才会发生变化
  1. 引入 reactive 方法:
    import { reactive } from "@vue/reactivity"; / import { reactive } from "vue";
  2. 格式:reactive(响应式数据)
  3. 返回值:Proxy 对象,为参数值的代理(注意,返回的是对象
  4. 获取数据:直接获取
<template>
    <p>arr:{{ arr }}</p>
    <button @click="changeData">修改数据</button>
</template>

<script>
import { reactive } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let arr = reactive(["a", "b", "c"]);
        function changeData() {
            console.log("arr", arr);
            arr[1] = "e"; // 直接获取并修改元素数据
        }
        return { arr, changeData };
    },
};
</script>
  • 接受参数值,并返回一个响应式的 ref 对象。ref 对象仅有一个 .value 属性,指向参数值
  • 参数值:引用类型数据、基本类型数据
    • 对于引用类型数据,其实也是被 reactive 方法处理,并返回 1 个 Proxy 对象
    • 对于基本类型数据,是使用 Object.defineProerty() 配合 getset 获取并修改数据
  1. 引入 ref 方法:
    import { ref } from "@vue/reactivity"; / import { ref } from "vue";
  2. 格式:ref(响应式数据)
  3. 返回值:RefImpl 对象(注意,返回的是对象
  4. 获取数据:返回值.value
<template>
    <p>name:{{ name }}</p>
    <p>arr:{{ arr }}</p>
    <button @click="changeData">修改数据</button>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let name = ref("superman");
        let arr = ref(["a", "b", "c"]);
        function changeData() {
            console.log("name", name.value); // superman
            name.value = "superwoman";
            console.log("arr", arr.value); // Proxy
            arr.value[1] = "e";
        }
        return { name, arr, changeData };
    },
};
</script>

shallowReactive & shallowRef

  • shallowReactive 修饰的对象数据,只有第 1 层属性是响应的
  • 注意:修改属性值,页面没有响应,但是数据是已经被改变了的
    如果页面重新渲染了,还是会显示最新的数据
<template>
    <p>obj.name:{{ obj.name }}</p>
    <button @click="obj.name += '!'">修改 name</button>
    <p>obj.girlfriend.name:{{ obj.girlfriend.name }}</p>
    <button @click="obj.girlfriend.name += '!'">
        修改 obj.girlfriend.name
    </button>
</template>

<script>
import { shallowReactive } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = shallowReactive({
            name: "superman",
            // 深层的数据不是响应的
            girlfriend: {
                name: "superwoman",
            },
        });
        return {
            obj,
        };
    },
};
</script>
  • shallowRef 修饰的数据,只有数据本身是响应的,其属性都不是响应的
  • 注意:修改属性值,页面没有响应,但是数据是已经被改变了的
    如果页面重新渲染了,还是会显示最新的数据
<template>
    <p>obj.name:{{ obj.name }}</p>
    <!-- 修改属性,不会响应 -->
    <button @click="obj.name += '!'">修改 obj.name</button> |
    <!-- 修改对象本身,可以响应 -->
    <button @click="obj = newObj">修改 obj</button>
    <hr />
    <p>gender:{{ gender }}</p>
    <!-- 修改数据本身,可以响应 -->
    <button @click="gender += '!'">修改 gender</button>
</template>

<script>
import { shallowRef } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = shallowRef({
            name: "superman",
        });
        let newObj = {
            name: "superwoman",
        };
        let gender = shallowRef("male");
        return { obj, gender, newObj };
    },
};
</script>

这里,我们先改变对象的属性值,可以发现页面并没有更新;然后我们再改变基本类型数据,可以发现,基本类型数据和之前修改的对象的属性值都更新了
是因为:修改对象的属性值,虽然页面没有更新,但数据是已经被改变了的。页面重新渲染的话,还是会显示最新的数据

toRef & toRefs

  • 格式:toRef(响应式数据对象, "对象数据属性名")
  • 作用:将响应式对象中的某个属性单独提供给外部使用,方便数据的获取
<template>
    <p>name:{{ name }}</p>
    <button @click="name += '!'">修改 name</button>
    <p>girlfriendName:{{ girlfriendName }}</p>
    <button @click="girlfriendName += '!'">修改 girlfriendName</button>
</template>

<script>
import { reactive, toRef } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = reactive({
            name: "superman",
            girlfriend: {
                name: "superwoman",
            },
        });
        // 在 return 这里设置对应的变量
        return {
            obj,
            // 使用 toRef 方法
            name: toRef(obj, "name"),
            girlfriendName: toRef(obj.girlfriend, "name"),
        };
    },
};
</script>
  • 此时新创建的变量,会与原数据形成关联,变量改变的话,原数据也会跟着改变
  • 用于将对象数据的所有属性单独拎出来使用
  • 格式:...toRefs(对象数据)
  • 注意:这里只能解析 1 层属性数据
<template>
    <p>name:{{ name }}</p>
    <button @click="name += '!'">修改 name</button>
    <!-- 注意:这里只能解析 1 层数据 -->
    <p>girlfriend.name:{{ girlfriend.name }}</p>
    <button @click="girlfriend.name += '!'">修改 girlfriend.name</button>
</template>

<script>
import { reactive, toRefs } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = reactive({
            name: "superman",
            girlfriend: {
                name: "superwoman",
            },
        });
        // 使用 toRefs(对象数据)
        let { name, girlfriend } = toRefs(obj);
        return { name, girlfriend };
    },
};
</script>

customRef

  • 用于自定义 ref 方法:customRef(callback)
  • callback 接收 2 个函数参数:track-用于追踪数据的更新、trigger-用于重新渲染页面
  • callback 返回一个对象,该对象需有 gettersetter 方法
  • getter 方法需设置返回值,返回值为页面显示的数据
<template>
    <p>App:{{ msg }}</p>
    <input type="text" v-model="msg" />
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let msg = ref("superman");
        return { msg };
    },
};
</script>
<template>
    <p>{{ personName }}</p>
    <input type="text" v-model="personName" />
</template>

<script>
import { customRef } from "vue";
export default {
    name: "App",
    setup() {
        function myRef(value) {
            // 使用 customRef 自定义 ref 方法
            return customRef((track, trigger) => {
                // 回调函数接收参数:track、trigger
                // 回调函数返回一个对象
                return {
                    // 设置 getter 方法
                    get() {
                        console.log("getValue", value);
                        // 调用 track 追踪数据变化
                        track();
                        // 返回显示的数据
                        return value;
                    },
                    // 设置 setter 方法
                    set(newValue) {
                        console.log("setValue", value);
                        console.log("newValue", newValue);
                        value = newValue;
                        // 调用 trigger 方法,让 vue 模板更新数据
                        trigger();
                    },
                };
            });
        }
        let personName = myRef("superman");
        return { personName };
    },
};
</script>

上例中,页面使用了 2 次 personName 数据,所以渲染时会输出 2 次 getValue 值

  • 我们可以在自定义 ref 方法中添加需要的功能:eg 需要修改数据 1s 后再渲染页面
<template>
    <p>{{ personName }}</p>
    <input type="text" v-model="personName" />
</template>

<script>
import { customRef } from "vue";
export default {
    name: "App",
    setup() {
        function myRef(value) {
            let trim = null; // 定义变量,用于防抖
            return customRef((track, trigger) => {
                return {
                    get() {
                        track();
                        return value;
                    },
                    set(newValue) {
                        // 使用前 clearTimeout (防抖)
                        clearTimeout(trim);
                        trim = setTimeout(() => {
                            value = newValue;
                            trigger();
                        }, 1000);
                    },
                };
            });
        }
        let personName = myRef("superman");
        return { personName };
    },
};
</script>

readonly & shallowReadonly

  • readonly 修饰的数据,不可被修改
<template>
    <p>obj.name:{{ obj.name }}</p>
    <button @click="obj.name += '!'">修改 obj.name</button>
    <hr />
    <p>reObj.name:{{ reObj.name }}</p>
    <!-- 点击不会被修改,控制台会抛出警告 -->
    <button @click="reObj.name += '!'">修改 reObj.name</button>
</template>

<script>
import { reactive, readonly } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = reactive({
            name: "superman",
        });
        let reObj = readonly(obj);
        return { obj, reObj };
    },
};
</script>
  • shallowReadonly 修饰的数据,浅层数据不可被修改,但深层数据可以被修改
<template>
    <!-- 浅层数据 -->
    <p>obj.name:{{ obj.name }}</p>
    <button @click="obj.name += '!'">修改 obj.name</button>
    <p>reObj.name:{{ reObj.name }}</p>
    <!-- 点击不会被修改,控制台会抛出警告 -->
    <button @click="reObj.name += '!'">修改 reObj.name</button>
    <hr />
    <!-- 深层数据 -->
    <p>obj.girlfriend.name:{{ obj.girlfriend.name }}</p>
    <button @click="obj.girlfriend.name += '!'">
        修改 obj.girlfriend.name
    </button>
    <p>reObj.girlfriend.name:{{ reObj.girlfriend.name }}</p>
    <!-- 点击可以修改 -->
    <button @click="reObj.girlfriend.name += '!'">
        修改 reObj.girlfriend.name
    </button>
</template>

<script>
import { reactive, shallowReadonly } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = reactive({
            name: "superman",
            girlfriend: {
                name: "superwoman",
            },
        });
        let reObj = shallowReadonly(obj);
        return { obj, reObj };
    },
};
</script>

toRaw

  • reactive 响应式数据变为普通数据:toRaw(响应式数据)
<template>
    <p>obj.name:{{ obj.name }}</p>
    <button @click="obj.name += '!'">修改 obj.name</button>
    <hr />
    <p>raObj.name:{{ raObj.name }}</p>
    <button @click="raObj.name += '!'">修改 raObj.name</button>
</template>

<script>
import { reactive, toRaw } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        let obj = reactive({
            name: "superman",
        });
        let raObj = toRaw(obj);
        return { obj, raObj };
    },
};
</script>

可以发现,toRaw 修饰的数据,会变为普通数据,修改普通数据,页面不会重新渲染
但是数据是已经被改变了的,页面如果重新渲染的话,还是会显示最新数据

markRaw

  • 用于标记一个对象数据,使其永远不能成为一个响应式数据
<template>
    <p>obj:{{ obj }}</p>
    <p>
        第一步:选择接收数据
        <button @click="changeObj">changeObj</button> |
        <button @click="maChangeObj">maChangeObj</button>
    </p>
    <p>
        第二步:选择改变的数据
        <button @click="changeName">changeName</button> |
        <button @click="changeGirl">changeGirl</button>
    </p>
</template>

<script>
import { markRaw, reactive } from "@vue/reactivity";
export default {
    name: "App",
    setup() {
        // 定义响应式数据
        let obj = reactive({
            name: "superman",
        });

        // 假设通过 Ajax 接收一个对象数据
        function changeObj() {
            let girl = { name: "superwoman" };
            obj.girlfriend = girl;
        }

        // 假设通过 Ajax 接收一个对象数据,并用 markRaw 修饰
        function maChangeObj() {
            let girl = markRaw({ name: "superwoman" });
            obj.girlfriend = girl;
        }

        // 改变接收的数据
        function changeGirl() {
            obj.girlfriend.name += "!";
        }

        // 改变原数据
        function changeName() {
            obj.name += "!";
        }

        return {
            obj,
            changeObj,
            changeGirl,
            maChangeObj,
            changeName,
        };
    },
};
</script>

可以发现,正常接收的对象数据,是可以被修改并渲染到页面上的;但是,用 markRaw 修饰的对象数据,数据会被修改,但不会被渲染到页面上(当然,页面重新渲染时,还是会显示最新数据)

举报

相关推荐

0 条评论