- 该文章是在学习 小满vue3 课程的随堂记录
- 示例均采用
,且包含
typescript
的基础用法
本篇主要学习几个 api 及相关源码:
toRef
toRefs
toRaw
toRef(reactiveObj, key)
响应式对象
,第二个是 指定的key
响应式对象
的 一部分也变为响应式
,通过 .value
修改非响应式
对象无能为力,修改后 视图不会更新
直接解构
reactiveObj,不使用 toRef,会使解构出的值 丧失响应式
属性key
需要被单独使用,并 希望它是响应式的
<div class="">hobby:{{ hobby }}div>
<button @click="change">修改button>
// 普通对象
const man = {
name: "xiaoman",
age: 18,
hobby: "ball",
};
const hobby = toRef(man, "hobby"); // 对普通对象使用,修改后仅修改值 但不更新视图
const change = () => {
hobby.value = "sing";
console.log("hobby", hobby); // Ref<"sing">,但视图不更新
};
视图不更新:
<div class="">hobby2:{{ hobby2 }}div>
<button @click="change">修改button>
// reactive 响应式对象
const man2 = reactive({
name: "xiaoman",
age: 18,
hobby: "ball",
});
const hobby2 = toRef(man2, "hobby");
const change = () => {
hobby2.value = "dance";
console.log("hobby2", hobby2, man2) // hobby2 和 man2 都会更新,视图也会更新
};
打印:
视图更新:
若 直接解构
响应式对象,不使用 toRef,会使解构出的值 丧失响应式
// reactive 响应式对象
const man2 = reactive({
name: "xiaoman",
age: 18,
hobby: "ball",
});
const { age } = man2; // 直接解构会丧失响应式
console.log("直接解构 age------", age);
解构出的就只是一个普通的值:
toRefs(reactiveObj)
把全部属性都变为响应式
传入响应式对象
,之后若对其解构,解构出的也是响应式对象
其实就是定义一个循环,循环体中 调用 toRef
const toRefsCopy = <T extends object>(obj: T) => {
const map: any = {};
for (let key in obj) {
map[key] = toRef(obj[key]);
}
return map;
};
<div>refs:{{ refs }}div>
<div>refs2:{{ refs2 }}div>
<button @click="change2">修改button>
const blue = reactive({
name: "blue",
age: 19,
});
const refs = toRefsCopy(blue);
const refs2 = toRefs(blue);
console.log("refs---", refs, refs2);
const change2 = () => {
// 解构出的每一个key都是响应式
const { age } = refs2;
age.value = 24;
console.log("toRefs", refs, age);
};
toRefs 和 toRefsCopy 处理过后,每个key都是响应式:
toRaw(reactiveObj)
响应式对象
toRaw
使 响应式对象
变为 普通原始对象
响应式对象
中 __v_raw
对应的值,跟 toRaw 之后的结果相同(__v_raw 是源码内部的操作)const people = reactive({
name: "bill",
age: 12,
});
// 打印结果:people 是具有响应式的对象,toRaw 后就变成了普通原始对象
console.log("toRaw-------", people, toRaw(people));
// 取出 __v_raw 对应的值,跟 toRaw 的结果相同
console.log("__v_raw-------", people["__v_raw"]);
源码贴图:
源码理解记录:
/**
*(reactivity.cjs.prod.js)搜索 function toRef 即可找到
*
* 1、function toRef (source, key, defaultValue)
* - 先判断 isRef,true的话直接返回
* - 再判断是不是函数类型,GetterRefImpl 内部仍然是直接返回,但会增加一些必要的标记(__v_isRef、__v_isReadonly)
*
* - 再判断是不是object,是的话走进 propertyToRef
* - 看 source[key] 是否满足 isRef,
* true的话直接返回(已经设置过响应式了)
* 否则走进 ObjectRefImpl,这就是 toRef 的核心方法
* - ObjectRefImpl 与 RefImpl(ref) 内部同样有 get、set方法,
* 但是区别在于 ObjectRefImpl 没有收集依赖(track)、触发更新(trigger) 的操作
* 所以 toRef 对普通对象来讲没有响应式,只对已经有响应式的对象有用
*
* - 上述类型都不属于的话,直接 ref(source)
*
*
*
* 2、function toRefs (object)
*
* - 和上面自己写的 toRefsCopy 思路基本一致
* - 先初始化一下,[]或者{}
* - 然后循环,
* 判断每个值 若 isRef=true 直接返回
* 否则都 走进 ObjectRefImpl 中变为 ref 类型
*
*
* 3、function toRaw(observed)
*
* - 判断 observed 是否存在 __v_raw ,存在的话继续递归 toRaw,否则直接返回 observed
* - 取出的结果就是 不带 __v_raw 的原始普通对象
*
*
*/