reactive
& ref
- 接收参数值,并返回一个响应式的 Proxy 对象,即参数值的响应式副本
- 参数值:引用类型数据
- 优势:即使只是修改引用类型数据的一部分数据,eg:对象的某属性 / 数组的某一项,页面的数据也会发生变化
在 Vue2 中,对于引用类型数据,代理的是数据本身,需要修改整个数据,页面的数据才会发生变化
- 引入 reactive 方法:
import { reactive } from "@vue/reactivity";
/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[1] = "e"; // 直接获取并修改元素数据
}
return { arr, changeData };
},
};
</script>
- 接受参数值,并返回一个响应式的 ref 对象。ref 对象仅有一个
.value
属性,指向参数值 - 参数值:引用类型数据、基本类型数据
- 对于引用类型数据,其实也是被 reactive 方法处理,并返回 1 个 Proxy 对象
- 对于基本类型数据,是使用
Object.defineProerty()
配合get
和set
获取并修改数据
- 引入 ref 方法:
import { ref } from "@vue/reactivity";
/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); // 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
返回一个对象,该对象需有getter
、setter
方法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 修饰的对象数据,数据会被修改,但不会被渲染到页面上(当然,页面重新渲染时,还是会显示最新数据)