【Vue3】响应性数据_1

reactive & ref

reactive 方法
  • 接收参数值,并返回一个响应式的 Proxy 对象,即参数值的响应式副本
  • 参数值:引用类型数据
  • 原理:通过使用 Proxy 来实现响应式 (数据劫持),并通过 Reflect 操作 [源对象] 内部的数据
  • 优势:即使只是修改引用类型数据的一部分数据,eg:对象的某属性 / 数组的某一项,页面的数据也会发生变化
    在 Vue2 中,对于引用类型数据,代理的是数据本身,需要修改整个数据,页面的数据才会发生变化
  1. 引入 reactive 方法: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 Proxy
            arr[1] += "e"; // 直接获取
        }
        return { arr, changeData };
    },
};
</script>
ref 方法
  • 接受参数值,并返回一个响应式的 ref 对象。ref.value 指向参数值
  • 参数值:引用类型数据 / 基本类型数据
    • 对于引用类型数据,其实也是被 reactive 方法处理,并返回 1 个 Proxy 对象
    • 对于基本类型数据,是使用 Object.defineProerty() 配合 getset 获取并修改数据
  1. 引入 ref 方法: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); // name superman
            name.value += "~"; // 通过 `返回值.value` 获取

            console.log("arr", arr.value); // arr Proxy {0: 'a', 1: 'b', 2: 'c'}
            arr.value[1] += "e"; // 通过 `返回值.value` 获取
        }

        return { name, arr, changeData };
    },
};
</script>

toRef & toRefs

  • 格式:toRef(响应式数据对象, "对象数据属性名")
  • 作用:将响应式对象中的某个属性单独提供给外部使用,方便数据的获取
    此时新创建的变量,会与原数据形成关联,变量改变的话,原数据也会跟着改变
<template>
    <p>name: {{ name }}</p>
    <button @click="name += '!'">修改 name</button>
    <p>girlfriend_name: {{ girlfriend_name }}</p>
    <button @click="girlfriend_name += '!'">修改 girlfriend_name</button>
</template>

<script>
import { reactive, toRef } from "vue";
export default {
    name: "App",
    setup() {
        let obj = reactive({
            name: "superman",
            girlfriend: { name: "superwoman" },
        });
        return {
            name: toRef(obj, "name"), // 单层属性
            girlfriend_name: toRef(obj.girlfriend, "name"), // 多层属性
        };
    },
};
</script>
toRefs
  • 用于将对象数据的所有属性单独拎出来使用
    因为有多个属性,所以还是以对象的形式存储着
  • 格式:...toRefs(对象数据)(使用 [解构赋值] 将属性取出)
  • 注意:这里只能解析 1 层属性数据
<template>
    <p>name: {{ name }}</p>
    <button @click="name += '!'">修改 name</button>
    <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" },
        });
        return { ...toRefs(obj) }; // 只能解析 1 层数据
    },
};
</script>

shallowReactive & shallowRef

shallowReactive
  • 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";
export default {
    name: "App",
    setup() {
        let obj = shallowReactive({
            name: "superman",
            girlfriend: { name: "superwoman" },
        });
        return { obj };
    },
};
</script>

这里,我们先修改 obj.girlfriend.name,发现页面并没有更新;
然后我们再修改 obj.name,可以发现,obj.name 和之前修改的 obj.girlfriend.name 都更新了;
是因为:修改深层数据,虽然页面没有更新,但数据是已经被改变了的。页面重新渲染的话,还是会显示最新的数据

shallowRef
  • shallowRef 修饰的数据,只有数据本身是响应的,其属性都不是响应的
    对于 [基本类型数据],和 ref 等效;对于 [引用类型数据],只能对数据本身进行响应,对其属性无响应性
  • 注意:对于属性值,通过事件修改后,其实数据是已经被改变了的,只是模版没有更新而已
    如果模本更新了,还是会显示最新的属性值数据
<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";
export default {
    name: "App",
    setup() {
        let obj = shallowRef({ name: "superman" }); // 修饰 [引用类型数据]
        let newObj = { name: "superwoman" };
        let gender = shallowRef("male"); // 修饰 [基本类型数据]
        return { obj, gender, newObj };
    },
};
</script>

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

customRef

  • 用于自定义 ref 方法:customRef(callback)
  • callback 接收 2 个函数参数:track-用于追踪数据的更新、trigger-用于重新渲染页面
  • callback 返回一个对象,该对象需有 gettersetter 方法
  • getter 方法需设置返回值,返回值为页面显示的数据
正常使用 ref:
<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>
自定义 ref
<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(track、trigger) 自定义 ref 方法
            return customRef((track, trigger) => {
                // 回调函数返回一个对象
                return {
                    // getter
                    get() {
                        console.log("get_value", value);
                        track(); // 返回数据之前 调用 track - 追踪数据变化
                        return value; // 返回显示的数据
                    },
                    // setter
                    set(newValue) {
                        console.log("set_value", value);
                        console.log("newValue", newValue);
                        value = newValue; // 修改数据
                        trigger(); // 修改完数据后 调用 trigger - 更新模板
                    },
                };
            });
        }
        let personName = myRef("superman");
        return { personName };
    },
};
</script>

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

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

<script>
import { customRef } from "vue";
export default {
    name: "App",
    setup() {
        function myRef(value, delay) {
            // 使用 customRef(track、trigger) 自定义 ref 方法
            return customRef((track, trigger) => {
                // 回调函数返回一个对象
                return {
                    // getter
                    get() {
                        console.log("get_value", value);
                        track(); // 返回数据之前 调用 track - 追踪数据变化
                        return value; // 返回显示的数据
                    },
                    // setter
                    set(newValue) {
                        console.log("set_value", value);
                        console.log("newValue", newValue);
                        // 设置节流
                        clearTimeout(timer)
                        timer = setTimeout(() => {
                            value = newValue; // 修改数据
                            trigger(); // 修改完数据后 调用 trigger - 更新模板
                        }, delay);
                    },
                };
            });
        }
        let personName = myRef("superman", 500);
        return { personName };
    },
};
</script>



【Vue3】响应性数据_2



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JS.Huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值