Vue 3 全景:从 React 视角进入
Vue 3 全景与第一个应用 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完这一节,你要能用自己的话说清三件事:Vue 的「渐进式」到底渐进在哪、单文件组件由哪三段组成、以及 Vue 的响应式和 React 的状态更新在机制上差在哪。这三件事想明白,后面 19 门课都是在它们上面加细节。
问题场景
你已经能熟练写 React,某天接到一个 Vue 3 项目:目录里一堆 .vue 文件,<script setup> 里的变量没经过 useState 却能让界面变化,v-model 双向绑定看得你发懵。按 React 的经验硬猜,很容易写出「用 watch 手动同步状态」这种别扭代码。问题不在 API 不熟,而在心智模型还没换过来。
核心解释
Vue 自称「渐进式框架」:你可以只用它做页面里的一小块交互(引入一个 script 标签就够),也可以用它配 Router、Pinia、构建工具做完整应用。React 其实也渐进,但 Vue 把这个定位做进了官方叙事和工具链,生态里「只用一个 CDN 文件」的用法是一等公民——这也是本站练习沙箱能跑 Vue 的原因。
真正要换的心智模型是渲染机制。React 的做法是:状态不可变,setState 后整个组件函数重新执行,用虚拟 DOM diff 找变化。Vue 的做法是:状态是响应式对象,读写时被追踪,某个值变了就精确通知用到它的地方重新渲染。对比一下同一个计数器:
<!-- Vue:ref 是响应式容器,.value 读写被追踪 -->
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<button @click="count++">计数:{{ count }}</button>
</template>
// React:状态不可变,触发重新渲染后整个函数重跑
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>计数:{count}</button>;
}
差别不在语法糖。Vue 里 count++ 是直接改一个被追踪的对象,框架知道「谁读了 count,谁就需要更新」;React 里你永远不能改 count 本身,只能用新值替换旧值。这带来两个连锁差异:Vue 不需要 useMemo/useCallback 来稳住引用(依赖追踪自动精确),但也意味着「解构响应式对象」这类操作会丢追踪——后面 V03 会专门讲。
单文件组件(SFC)是 Vue 的代码组织单位,一个 .vue 文件三段:
<script setup>
// 逻辑:组合式 API 写在这里,顶层变量自动暴露给模板
import { ref } from "vue";
const message = ref("你好,Vue");
</script>
<template>
<!-- 结构:类 HTML 的模板,指令以 v- 开头 -->
<p>{{ message }}</p>
</template>
<style scoped>
/* 样式:scoped 表示只作用于当前组件 */
p { color: #2563eb; }
</style>
<script setup> 是编译时语法糖:里面声明的变量、函数、import 的组件,模板里直接可用,不需要 return,也不需要注册。这和 React 函数组件「return JSX」的组织方式不同——Vue 把「逻辑」和「结构」分成两块,靠编译期建立联系。
示例拆解
把上面的计数器放进真实项目结构里看。Vue 官方推荐的脚手架是 create-vue,基于 Vite:
在空目录执行 npm create vue@latest my-vue-app,按提示勾选 TypeScript 与 Router,然后 cd my-vue-app && npm install && npm run dev(默认 5173 端口)。
生成的项目里,src/main.js 是入口,做一件事:创建应用并挂载。
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");
对照 React 的 createRoot(document.getElementById("root")).render(<App />),职责完全一样:找到挂载点,把根组件渲染进去。index.html 里有一个 <div id="app"></div>,Vite 直接以 HTML 为入口,不像 CRA/Vite-React 那样由 JS 创建根节点。
动手任务
- 用
npm create vue@latest建一个项目,选择 TypeScript 和 Router,跑起来后找到src/main.js(或main.ts),说出createApp这一行做了什么。 - 打开任意一个
.vue文件,分别指出 script、template、style 三段,把 style 的scoped删掉再观察样式作用范围的变化。 - 用一句话写下「Vue 的 count++ 和 React 的 setCount 本质区别」,写不出来就回到核心解释再读一遍。
常见错误
- 把 ref 当普通变量用:在
<script setup>里写console.log(count)看到的是 Ref 对象而不是数字,必须count.value;模板里则自动解包,不用写.value。症状是打印出{value: 0}或比较结果永远不对。 - 在模板里调用
count.value:模板已自动解包,再写.value反而取不到值(除非嵌套在对象里)。症状是页面显示空白。 - 用 React 思维手动同步派生状态:定义了
firstName、lastName,又用watch维护fullName。Vue 里应该用computed,下一门课就讲。 - 以为 SFC 必须配构建工具:
.vue文件需要编译,但 Vue 也提供 global build 直接写模板字符串,本站练习沙箱用的就是这种形式。
检查与复盘
- 能解释「渐进式」的两个极端用法:CDN 引入 vs 完整工程化
- 能说出 SFC 三段各自职责,以及
<script setup>省掉了什么 - 能讲清「依赖追踪」和「重新渲染」的差别,并举出一个由此产生的 API 差异
参考资料
- Vue 官方文档「简介」:渐进式定位与两种 API 风格的官方说明
- Vue 官方文档「快速上手」:
create-vue脚手架与项目结构 - Vue 官方文档「单文件组件」:SFC 规范与
<script setup>的编译行为 - Vite 官方文档:开发服务器与构建的基本命令
读完这一课了?
标记完成后,工作台会安排接下来的学习。