reactive
& ref
reactive 方法
- 接收参数值,并返回一个响应式的 Proxy 对象,即参数值的响应式副本
- 参数值:引用类型数据
- 原理:通过使用 Proxy 来实现响应式 (数据劫持),并通过 Reflect 操作 [源对象] 内部的数据
- 优势:即使只是修改引用类型数据的一部分数据,eg:对象的某属性 / 数组的某一项,页面的数据也会发生变化
在 Vue2 中,对于引用类型数据,代理的是数据本身,需要修改整个数据,页面的数据才会发生变化
- 引入 reactive 方法:
import { reactive } from "vue";
- 格式:
reactive(响应式数据)
- 返回值:Proxy 对象,为参数值的代理(注意,返回的是对象)
- 获取数据:直接获取
<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()
配合get
和set
获取并修改数据
- 引入 ref 方法:
import { ref } from "vue";
- 格式:
ref(响应式数据)
- 返回值:RefImpl 对象(注意,返回的是对象)
- 获取数据:脚本中通过
返回值.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
返回一个对象,该对象需有getter
、setter
方法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>