Vue3 —— to 全家桶及源码学习

  • 该文章是在学习 小满vue3 课程的随堂记录
  • 示例均采用 <script setup>,且包含 typescript 的基础用法

前言

本篇主要学习几个 api 及相关源码:

  • toRef
  • toRefs
  • toRaw

一、toRef

  • toRef(reactiveObj, key)
    • 接收两个参数,第一个是 响应式对象,第二个是 指定的key
    • 作用:将 响应式对象一部分也变为响应式,通过 .value 修改
  • toRef 对 非响应式 对象无能为力,修改后 视图不会更新
  • 直接解构 reactiveObj,不使用 toRef,会使解构出的值 丧失响应式
  • 应用场景:某个单独的 属性key 需要被单独使用,并 希望它是响应式的

① 对非响应式对象无能为力

html 复制代码
<div class="">hobby:{{ hobby }}</div>
<button @click="change">修改</button>
ts 复制代码
//  普通对象
const man = {
  name: "xiaoman",
  age: 18,
  hobby: "ball",
};
const hobby = toRef(man, "hobby"); // 对普通对象使用,修改后仅修改值 但不更新视图

const change = () => {
  hobby.value = "sing";
  console.log("hobby", hobby); // Ref<"sing">,但视图不更新
};

打印更新:

视图不更新:

② 将响应式对象的一部分也变为响应式

html 复制代码
<div class="">hobby2:{{ hobby2 }}</div>
<button @click="change">修改</button>
ts 复制代码
// 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,会使解构出的值 丧失响应式

csharp 复制代码
// reactive 响应式对象
const man2 = reactive({
  name: "xiaoman",
  age: 18,
  hobby: "ball",
});
const { age } = man2; // 直接解构会丧失响应式
console.log("直接解构 age------", age);

解构出的就只是一个普通的值:

二、toRefs

  • toRefs(reactiveObj)
    • 和 toRef 作用一样,只是不再指定某个key,而是把全部属性都变为响应式
    • 也是需要 传入响应式对象,之后若对其解构,解构出的也是响应式对象
  • 外层不再是响应式,内部的每一个key才是响应式

① 简单实现 toRefs 的源码

其实就是定义一个循环,循环体中 调用 toRef

ts 复制代码
const toRefsCopy = <T extends object>(obj: T) => {
  const map: any = {};

  for (let key in obj) {
    map[key] = toRef(obj[key]);
  }
  return map;
};

② toRefs 使用

html 复制代码
<div>refs:{{ refs }}</div>
<div>refs2:{{ refs2 }}</div>
<button @click="change2">修改</button>
csharp 复制代码
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都是响应式:

直接解构出的 key 也是响应式,会立刻更新:

三、toRaw

  • toRaw(reactiveObj)
    • 同样接收一个 响应式对象
    • 作用:toRaw 使 响应式对象 变为 普通原始对象
  • 取出 响应式对象__v_raw 对应的值,跟 toRaw 之后的结果相同(__v_raw 是源码内部的操作)
ts 复制代码
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 的原始普通对象
 *
 *
 */
相关推荐
雨季mo浅忆21 小时前
记录Vue3项目中的各类问题
前端·bug·vue3
八目蛛4 天前
八目蛛网络(免费工具网站导航)
css·vue.js·开源·vue3·html5·ai编程
颂love4 天前
Vue3基础入门
前端·学习·vue3
海市公约5 天前
Vue3组合式API中watch传值生命周期与自定义Hook实战
vue3·生命周期·watch·props·组件通信·defineexpose·自定义hook
海市公约6 天前
Vue3组合式API与响应式系统核心机制详解
vue3·computed·reactive·ref·响应式系统·composition api·script setup
小茴香3537 天前
Vue3路由权限动态管理
前端·前端框架·vue3
暗冰ཏོ11 天前
《2026 Vue2 + Vue3 完整学习指南:基础语法、路由缓存、登录拦截、项目实战与面试题》
前端·vue.js·vue·vue3·vue2
曲幽12 天前
写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了
vue3·web·vite·icon·element plus·vs code·import·unplugin
小云小白13 天前
若依-vue3 把深色版本改成天蓝色-含登录页
vue3·若依·天蓝色
曲幽14 天前
FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示?
python·vue3·api·fastapi·web·ant design·view·menu·frontend