Vue 3 响应式原理:computed的模板解包机制

前言

vue中的计算属性相关知识:

核心概念对比

特性 脚本环境(JS) 模板环境(Template)
ref 访问 .value 必需 自动解包(无需 .value
computed 访问 .value 必需 自动解包(无需 .value
原始对象访问 直接访问 直接访问

二话不说先上代码,大家觉得会输出什么呢?

JS 复制代码
<template>
  <div>{{ firstName }}+{{lastName}}</div>
  <div>{{ name }}</div>
  <div>{{ name.value }}</div>
</template>

<script setup>
import { ref, computed } from "vue";
const firstName = ref("ac");
const lastName = ref("bc");

console.log(firstName);

const name = computed(() => {
  return firstName.value + lastName.value;
});
console.log(name);
console.log(name.value);
</script>

<style>
</style>

结果如下所示,大家可能有点疑惑?接下来就看下面知识点:

js 复制代码
const firstName = ref("ac");   
// 创建响应式引用,value = "ac"
const lastName = ref("bc");    
// 创建响应式引用,value = "bc"

控制台首次输出

javascript 复制代码
console.log(firstName);  // 输出 RefImpl 对象
console.log(name);       // 输出 ComputedRefImpl 对象
console.log(name.value); // 输出计算值 "acbc"

模板渲染机制

模板编译结果对比

模板写法 实际渲染值 原理说明
{{ firstName }} "ac" 自动解包 ref,等价于 firstName.value
{{ name }} "acbc" 自动解包 computed,等价于 name.value
{{ name.value }} 空/undefined 错误用法,因为 name 解包后得到字符串,字符串没有 .value 属性

为什么表现不同?

  1. Ref 对象结构

    typescript 复制代码
    interface Ref<T> {
      value: T
      __v_isRef: true
      // ...其他内部属性
    }
  2. Computed 对象结构

    typescript 复制代码
    interface ComputedRef<T> extends Ref<T> {
      readonly effect: ReactiveEffect<T>
      // ...其他计算属性特有属性
    }
  3. 模板自动解包规则

    • 遇到 {{ variable }} 时:

      javascript 复制代码
      function renderValue(target) {
        return isRef(target) ? target.value : target
      }

控制台输出详解

1. console.log(firstName)

输出示例:

javascript 复制代码
RefImpl {
  __v_isRef: true,
  _value: "ac",
  value: "ac"  // 通过 getter 访问
}

2. console.log(name)

输出示例:

javascript 复制代码
ComputedRefImpl {
  __v_isRef: true,
  _dirty: false,
  _value: "acbc",  // 缓存的计算结果
  effect: ReactiveEffect {...}
}

3. console.log(name.value)

输出结果:

arduino 复制代码
"acbc"  // 直接获取计算值

正确用法总结

脚本区域(<script setup>

javascript 复制代码
// 必须使用 .value 访问
const fullName = firstName.value + lastName.value
console.log(name.value) // 正确

模板区域(<template>

vue 复制代码
<!-- 直接使用变量名 -->
<div>{{ name }}</div>  

<!-- 等价于 -->
<div>{{ name.value }}</div>  <!-- 错误! -->

常见误区解释

  1. 为什么模板不能写 .value

    • Vue 的模板编译器已经自动处理解包
    • name.value 相当于尝试访问字符串的 .value 属性
  2. computed 和普通 ref 的区别

    特性 常规 ref computed
    值获取 直接赋值 通过计算函数获取
    依赖追踪 自动追踪
    模板使用 自动解包 自动解包
  3. 调试建议

    vue 复制代码
    <!-- 调试 computed 对象 -->
    <pre>{{ { computedObject: name } }}</pre>

说人话:

1. refcomputed 的本质

  • 它们都是 Vue 的**"魔法盒子"**,里面装着值(比如字符串、数字等)
  • 但你要从盒子里拿值,在 JS 里 必须用 .value 打开盒子
  • 在模板里,Vue 会自动帮你打开盒子,你直接拿就行

2. 你的代码问题

html 复制代码
<div>{{ name.value }}</div>  <!-- 错误! -->

相当于:

  1. Vue 先自动打开 name 盒子 → 得到字符串 "acbc"
  2. 然后你让 Vue 去拿 "acbc".value → 字符串哪有 .value 啊!(报错)

3. 正确姿势对比

场景 JS 代码里 模板里 原因
普通 ref firstName.value {{ firstName }} 模板自动拆盒子
computed name.value {{ name }} 模板自动拆盒子
原始值 直接 "ac" {{ "ac" }} 本来就不是盒子

4. 通俗比喻

  • JS 环境 :就像你在厨房做菜(需要自己动手开冰箱拿食材)

    js 复制代码
    // 必须自己开盒子!
    const food = fridge.value 
  • 模板环境 :就像你点外卖(商家自动帮你打包好)

    html 复制代码
    <!-- 直接吃现成的 -->
    <div>{{ food }}</div>  

5. 记住三句话

  1. <script setup> 里:必须写 .value(手动开盒)
  2. <template> 里:永远不要写 .value(自动拆盒)
  3. computedref 在模板里待遇一样,都自动拆

这样写就对了:

vue 复制代码
<template>
  <div>{{ name }}</div>  <!-- 显示 "acbc" -->
</template>

<script setup>
const name = computed(() => "ac" + "bc")
console.log(name.value) // 这里必须写 .value
</script>
相关推荐
WebGISer_白茶乌龙桃几秒前
Cesium实现“悬浮岛”式,三维立体的行政区划
javascript·vue.js·3d·web3·html5·webgl
小Tomkk4 分钟前
⭐️ StarRocks Web 使用介绍与实战指南
前端·ffmpeg
计算机学姐6 分钟前
基于SpringBoot的汽车租赁系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·spring·汽车·推荐算法
不一样的少年_7 分钟前
产品催: 1 天优化 Vue 官网 SEO?我用这个插件半天搞定(不重构 Nuxt)
前端·javascript·vue.js
-dcr9 分钟前
50.智能体
前端·javascript·人工智能·ai·easyui
BingoGo10 分钟前
免费可商用商业级管理后台 CatchAdmin V5 正式发布 插件化与开发效率的全面提升
vue.js·后端·php
行者9619 分钟前
Flutter跨平台开发适配OpenHarmony:进度条组件的深度实践
开发语言·前端·flutter·harmonyos·鸿蒙
云和数据.ChenGuang20 分钟前
Uvicorn 是 **Python 生态中用于运行异步 Web 应用的 ASGI 服务器**
服务器·前端·人工智能·python·机器学习
IT_陈寒21 分钟前
SpringBoot 3.0实战:这5个新特性让你的开发效率提升50%
前端·人工智能·后端
哈__26 分钟前
React Native 鸿蒙跨平台开发:LayoutAnimation 实现鸿蒙端页面切换的淡入淡出过渡动画
javascript·react native·react.js