Vue3基础篇核心笔记:从响应式原理到工程实践避坑指南

发布时间:2026/9/23 5:02:33

Vue3基础篇核心笔记:从响应式原理到工程实践避坑指南 刚开始正经用Vue3做项目那阵子我其实挺不耐烦把官网上那部分基础文档当成“阅读材料”去啃的。但做了两三个项目再碰上团队要统一技术栈、面试又高频追问Vue3问题时我发现自己最缺的居然不是“写了多少业务代码”而是对基础篇里那些核心概念的清晰边界。所以我把官网基础篇重新过了一遍结合自己踩过的坑整理成这份Vue3基础篇官网笔记。它不是文档的翻译稿而是把容易混淆的点、实操环节里最该注意的细节都摊开来讲适合从Vue2迁移过来的老朋友也适合刚用Vite搭好项目想系统打一遍底子的新同学。1. 这份基础篇笔记到底在记什么1.1 为什么有Vue2基础的人也要重新过一遍很多人觉得Vue3和Vue2差不了太多模板语法熟悉一下、生命周期换个写法就能上手。真正做项目时会发现差异远远不止表面那点。Vue3的整个响应式系统重写了组件逻辑组织方式从选项式变成了组合式模板编译逻辑也有调整连v-model在组件上的用法都变了。如果你心里默认“反正还是那套Vue”很容易用着用着就撞上奇怪的问题。举个例子。Vue2里给对象新增一个属性用this.$set才能保证响应式Vue3换成了Proxy做拦截新增属性和删除属性都能被感知到。这个改变听起来很爽但它同时带来一个反向问题如果你把reactive对象解构出来用响应式连接直接就断了。很多从Vue2过来的人会自然而然地写const state reactive({ count: 0 }) const { count } state然后在模板或逻辑里修改count发现页面纹丝不动。这不是Vue3退步了而是“解构会断开响应式”这个坑在Vue2里没有被暴露得这么明显。官网基础篇里对这个场景有明确解释也给了toRefs和ref两种解法这恰恰是基础篇里最值得反复看的部分。从面试角度来看Vue2和Vue3的对比几乎是必考题。被问得最多的几项包括响应式原理的差异、Composition API与Options API的取舍、生命周期改名与对应关系、v-model在组件上的写法变化、插槽语法变化以及全局API挂载方式的变化。这些内容在官网基础篇里都有权威答案比自己翻各种二手文章要可靠得多。1.2 官网文档应该怎么读才有效率官网文档最大的优点是权威、准确但它的编排是按知识分类来的不是按业务场景来的。直接从头到尾读很容易觉得枯燥而且读完了印象不深。我的做法是带着三个问题去读第一这个API帮我们解决什么问题第二它和旧写法相比差别在哪第三什么业务场景下我真正会用到它。比如讲ref的时候官网会先给最基础的用法再讲模板中的自动解包最后才解释深层响应性原理。很多人只记了ref需要.value这个表面规则却不知道模板中为什么能自动解包也不知道对象类型的ref底层其实还是会转成reactive。这些细节看起来不影响第一个demo跑通但会直接影响你在写复杂逻辑时判断响应式是否生效的能力。读官网还有一个技巧结合官方Playground。官网上每个示例都带一个“在Playground中尝试”的入口我会把示例代码复制出来私自改几个变量观察页面变化。比如把ref(0)改成ref({ count: 0 })看模板里的写法有没有区别把watch的回调改成异步的看看执行顺序。这样折腾一圈比自己写一整个小项目再排查问题要高效得多因为你会更清楚地知道是哪一个具体的行为导致了结果差异。2. 从零初始化一个Vue3项目2.1 用Vite搭项目时最容易忽略的几个点现在创建Vue3项目官方推荐的方式是通过Vite。命令行执行npm create vuelatest这一步会拉取官方脚手架然后问你一堆问题比如要不要装TypeScript、Router、Pinia、Vitest等。如果只想快速体验Vue3核心语法可以全部选No如果是准备正式做项目建议按需选择。创建完之后再安装依赖npm install npm run dev有个细节容易被忽略npm create vuelatest和npm create vitelatest看起来区别不大但前者生成的是官方Vue项目模板默认包含vite.config.js里的一些Vue相关配置还顺带准备好了路径别名、ESLint等可选内容。后者是纯Vite模板需要自己手动装vitejs/plugin-vue并创建vite.config.js。对新手来说直接用前者更省心能少踩不少配置上的坑。Node版本也是个高频踩坑点。Vite 5以上要求Node 18及以上如果电脑里还是Node 14安装依赖时会出现各种奇怪的警告甚至直接报错。我的建议是直接用Node LTS版本别用太旧的也不会刻意追最新的激进版。装好之后用node -v确认一下版本省得后面排查半天最后发现是环境问题。2.2 目录结构和入口文件的变化Vue3项目的入口文件变化很大。Vue2时代入口是这样import Vue from vue import App from ./App.vue new Vue({ render: h h(App) }).$mount(#app)Vue3变成了import { createApp } from vue import App from ./App.vue createApp(App).mount(#app)别小看这个变化。Vue2里全局API是通过Vue.use()、Vue.prototype.$xxx这种方式挂的Vue3里全部改成从vue包里按需导入或者通过app.config.globalProperties来挂全局内容。这也带来一个实际影响同一个页面里如果同时存在多个Vue应用实例它们不再共享全局配置了。过去Vue2那种Vue.prototype的全局污染问题在Vue3里彻底被避免但代价是你得知道“全局”都是跟着某个app实例走的。App.vue的写法也有变化。官方模板默认使用的是script setup语法糖组件代码比之前精简很多script setup import { ref } from vue const count ref(0) /script template button clickcount{{ count }}/button /template这里注意一点script setup不是凭空出现的新特性它本质上是把Composition API的代码编译成setup()函数的形式。所以你在script setup里写的顶层变量、函数都会被自动暴露给模板不需要再手动return。这个语法糖让代码简洁了很多但它有一个隐含约束不能在里面使用普通的export default也不需要写export default。对于刚从Vue2切过来的人这个写法需要一个适应期但适应之后写起来是真的顺手。2.3 开发环境接口转发与部署路径配置开发阶段大家都会遇到跨域问题。Vite的解决方案是在vite.config.js里配置开发服务器的转发规则export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:3000, changeOrigin: true } } } })这样前端请求/api/xxx时开发服务器会自动把它转发到http://localhost:3000上绕开浏览器跨域限制。这里的changeOrigin: true务必保留否则部分后端服务会因为请求头里的Origin不一致而拒绝响应。部署时有个很容易踩的坑如果项目不是部署在域名根路径而是部署在类似http://example.com/web/这样的子路径下必须在vite.config.js里设置baseexport default defineConfig({ base: /web/, // ... })很多人没设base直接把构建产物丢到服务器的子目录结果打开页面白屏控制台全是Uncaught SyntaxError: Unexpected token或者静态资源404。这个问题在热词里出现频率特别高本质上就是资源路径没对上。如果用了Nginx还需要把子路径的请求正确rewrite到index.html上否则刷新页面就会找不到路径。关于这个报错后面我会再专门展开讲。3. 响应式核心ref、reactive与computed到底怎么选3.1 ref和reactive的边界判断Vue3响应式最基础的两个API就是ref和reactive。官网的说明倾向于根据数据类型来选择对象用reactive基础类型用ref。但实际开发中团队里最常见的写法是“万物皆可ref”。我自己也是这个风格的支持者原因是ref的使用统一性更好尤其是配合script setup时模板里自动解包不需要每次都思考到底要不要.value。如果你在写复杂对象时用reactive一定要记住一个警告解构会丢失响应性。官网明确推荐用toRefs来解包import { reactive, toRefs } from vue const state reactive({ count: 0, name: vue }) const { count, name } toRefs(state)这样count和name是ref对象修改count.value可以保持响应式。但说实话如果逻辑已经复杂到需要频繁解构我反而会建议直接把整个状态改成多个ref理由很简单代码易读也不容易踩解构的坑。再讲一个响应式原理层面的细节。reactive底层用的是Proxy这种拦截方式有一个明显优势能监听到对象属性的新增和删除。这在Vue2里做不到Vue2只能对初始化时存在的属性递归遍历并重新定义getter和setter。所以Vue3里你可以放心地给响应式对象加新属性比如const user reactive({ name: 张三 }) user.age 18 // 页面同步更新这个能力极大简化了数据动态变化的场景。ref则是对值类型做了一层包装底层存的是{ value: ... }这样的结构所以ref也可以用来包对象。当你给ref传对象时它其实内部会把这个对象转成reactive。也就是说ref本身没有重复造轮子它只是给外部提供了一个统一的接口。3.2 computed的计算缓存与购物车数量校验场景热词里有一条“vue3 设置购物车个数不小于0”这其实很适合用computed来演示它的能力。先看最朴素的写法const quantity ref(0) function increase() { quantity.value } function decrease() { if (quantity.value 0) { quantity.value-- } }这样做当然没问题但如果你有多个入口可以修改数量输入框、加减按钮、批量修改每次都要手动判断会漏掉边界。更推荐的做法是给数量一个“规范化”出口让无论哪里赋值都经过同一层校验import { ref, computed } from vue const inputQuantity ref(0) const quantity computed({ get() { return inputQuantity.value 0 ? 0 : inputQuantity.value }, set(value) { inputQuantity.value Math.max(0, value) } })这样不管通过什么方式修改最终读到的值都不会小于0。这种写法充分利用了computed的两大特性一是缓存只有依赖的响应式数据变化时才重新计算二是双向绑定能力即getter和setter可以一起定义。一定要注意computed是有缓存的这是它和普通函数最大的区别。普通函数每次调用都重新计算computed只有在依赖改变时才会重新求值。所以如果你在模板里用computed去做一些开销较大的数据转换比如过滤长列表体验会明显好于调用方法。3.3 watch和watchEffect的适用场景watch和watchEffect都用来响应数据变化但适用场景完全不同。watch是惰性的你需要明确告诉它要监听谁并且只有被监听的数据变化时才会触发回调。它适合做“某个数据变化后执行一个副作用”这件事比如搜索框内容变化后防抖请求接口import { watch, ref } from vue const keyword ref() watch(keyword, (newVal, oldVal) { // 防抖请求接口 })要小心一点如果你监听的是一个对象像watch(obj, callback)这样写回调触发时拿到的newVal和oldVal可能都是同一个对象引用因为对象内部属性变了引用本身没变。这时需要开启deep: true来深度监听但由于Proxy的介入深度监听对性能的影响整体比Vue2小很多。watchEffect则不同你不需要指定监听谁它会在执行过程中自动收集所有访问过的响应式依赖依赖变化时重新执行。这个非常适合做那些“依赖多个数据且不需要关心旧值”的场景比如import { watchEffect } from vue import { userId } from ./state watchEffect(async () { // 只要 userId 变化就会重新发起请求 const data await fetchUser(userId.value) render(data) })好多新人分不清什么时候用哪个。我的经验判断标准很简单需要知道变化前后的值且监听目标明确时用watch只是希望“某个响应式数据变了之后自动重新跑一段逻辑”且不关心旧值用watchEffect更省代码。另外注意watchEffect在组件挂载时就会先执行一次如果你不希望首次执行回调里要自己加判断或改用watch的immediate: false。4. 组件化开发中要记住的API细节4.1 defineProps、defineEmits与defineExpose在script setup里父子组件通信最常用的就是defineProps和defineEmits。这两个宏在编译期就会被处理所以你不需要手动从vue里导入它们。基础的写法script setup const props defineProps({ title: { type: String, default: } }) const emit defineEmits([update:title]) /script如果用TypeScript可以写成纯类型声明的形式更简洁interface Props { title: string id?: number } const props definePropsProps() const emit defineEmits{ (e: update:title, title: string): void }()后者是现在团队里更通用的写法因为类型提示更完整组件使用方也能直接看到类型约束。关于TS和JS的选择后面我会单独说。defineExpose解决的场景是父组件需要直接调用子组件内部的方法或读取子组件的内部状态。在Vue2里模板ref默认就能拿到子组件实例上的所有方法但在script setup里子组件的内部数据默认对外不可见必须用defineExpose显式导出script setup import { ref } from vue const open ref(false) function openModal() { open.value true } defineExpose({ openModal }) /script父组件用ref拿到子组件实例后就能调openModal了。这里容易忽略的点是没有defineExpose的话父组件拿到的实例上不会有任何东西。如果哪天你把一个从Vue2抄过来的弹窗组件直接搬到Vue3里发现父组件调用this.$refs.children.openModal()不生效十有八九就是这个原因。4.2 插槽和作用域插槽的几个容易踩的坑Vue3对插槽语法做了一些调整尤其是作用域插槽的写法变得更容易混淆。Vue2里写作用域插槽用的是slot-scopeVue3统一改成了v-slot并且推荐使用简写#。具名插槽在子组件里的声明是这样!-- Child.vue -- div header slot nameheader / /header main slot / /main /div父组件里的写法Child template #header h1标题/h1 /template p默认内容/p /Child作用域插槽是为了让父组件能拿到子组件暴露的数据。举个例子子组件渲染用户列表希望父组件能自定义每行展示的内容!-- UserList.vue -- div slot namerow :useruser :indexindex / /div父组件UserList :usersusers template #row{ user, index } span{{ index 1 }}. {{ user.name }}/span /template /UserList这里最容易踩的坑是解构语法。#row{ user, index }花括号里是解构不是传入一个对象。很多人第一次写会写成#rowuser然后发现模板里的user.name报错因为拿到的是整个对象而不是解构出来的字段。另一个坑是如果你在slot上用了v-if或v-for要注意slot属性在哪些条件下会失效这时候用template包一层会更稳妥。4.3 生命周期与内置组件的使用变化Vue3的生命周期整体改名了。beforeDestroy变成了beforeUnmountdestroyed变成了unmounted而且组合式写法里全部要加on前缀onMounted、onUnmounted、onBeforeUnmount等。这些API必须在setup执行期间同步调用不能在异步回调里调用。比如在setTimeout里写onMounted会直接报错因为Vue已经丢失了当前组件实例的上下文。内置组件方面日常最常用的是Teleport和KeepAlive。Teleport用来把元素渲染到指定DOM节点下最常见的使用场景是弹窗和通知组件它们通常需要挂在body下以避免被父组件的overflow: hidden或z-index影响。写法很直观Teleport tobody div classmodal弹窗内容/div /TeleportKeepAlive的作用是缓存组件状态切换路由时组件不会重新销毁重建KeepAlive component :iscurrentView / /KeepAlive搭配动态组件:is使用时KeepAlive的include和exclude属性可以指定哪些组件需要被缓存按组件名匹配。如果组件用了script setup组件名默认是文件名需要确认文件名和include里写的一致否则缓存不会生效。还有一个可以留意的内置组件是Suspense它用来处理异步组件的加载状态。如果业务里有大量异步依赖用Suspense可以统一显示loading态不过它目前仍然是实验性API生产环境我会谨慎点。5. 高频报错与疑难杂症排查实录5.1 部署上线后Uncaught SyntaxError: Unexpected token热词里“vue3项目部署后uncaught syntaxerror: unexpected token”出现的频率很高。这个报错分两类场景。一类是开发环境本地运行没问题部署到服务器后白屏并报这个错这时候八成是静态资源路径问题。前面提过子目录部署时要在vite.config.js里配base: /子路径/。构建产物里index.html引用的JS和CSS路径应该是绝对路径的子路径如果仍然是/assets/...开头浏览器会去域名根目录找资源自然找不到。另一类场景是浏览器缓存问题。你部署了新版本但用户浏览器里还缓存的旧版本的index.html里面引用的还是旧的JS文件名。新旧版本混在一起容易触发Unexpected token。解决办法是构建时给文件名带hash。Vite在build时默认会生成带hash的文件名所以重点检查服务器有没有把index.html设置成不缓存或者短缓存location /index.html { add_header Cache-Control no-cache; } location / { try_files $uri $uri/ /index.html; }这种配置在Spa应用部署里几乎是标配。顺便提一句如果项目用了路由的createWebHistory模式Nginx如果没有配置try_files刷新非根路径页面时会返回404很多人会误以为是自己代码问题其实是因为服务器没有把所有路径都回退到index.html。5.2 JS还是TS新手到底怎么选“vue3用ts好还是js好”也是出现频率很高的问题。我的看法很直接如果你的项目是长期维护的正式项目或者团队有多人协作直接上TypeScript。Vue3的官方生态和类型系统配合度是历史上最好的阶段defineProps和defineEmits用纯类型声明显得极其自然开发时编辑器提示特别完整重构时也能省下大量排查低级拼写错误的时间。如果你的目的只是学Vue3本身、或者做一个短期的个人demo先写JS也完全可以。Vue3的JS入门曲线比TS更平缓不会因为类型报错打断思路。但我的经验是一旦你打算长期用Vue3开发不如直接一步到位用TS。因为Vue3的很多高级用法比如泛型组件、类型推导、defineExpose对外暴露类型的提示都是在TS环境下才能体验到价值的。比“踩坑”更难受的是你用了TS但到处写any等于浪费了这套类型系统还会在团队联调时被同事吐槽。5.3 几个常见业务场景的排查清单我把自己用Vue3开发时遇到过的、以及热词里容易出现的几类问题整理成一张速查清单方便遇到问题时直接对照。第一类组件样式修改不生效。比如热词里“vue3修改tabs标签页样式”。Element Plus这类组件库的样式通常会加一层作用域隔离直接写在style scoped里的规则优先级不够或者选择器层级不够会被组件内部样式覆盖。解决办法是用深度选择器:deep()style scoped :deep(.el-tabs__nav-wrap) { background: #fff; } /style:deep()的作用是让这个选择器不经过>
延伸阅读

更多相关文章

2026/9/23 5:02:33

JS逆向补环境:原型链伪造的完整套路与穿帮细节

最近调一个带环境检测的加密站点,window、navigator、document 这些老熟人都补了一圈,代码还是卡在一个莫名其妙的 undefined 上报错。顺着调用栈翻到底才发现,问题根本不是缺值,而是某个构造函数对应的原型链上少了一个 Symbol.t…

2026/9/23 5:02:33

皮肤病变检测数据集:YOLO与VOC双标签格式详解及训练实战

简介:这是一套面向皮肤病变检测的妇科皮肤病数据集,覆盖黑色素、猴痘、水痘、痤疮、疣、花斑癣、粉刺、银屑病、湿疹、癣等十余类常见病灶,适用于YOLO系列算法训练与验证,也适合目标检测入门者练习标注格式处理。资源已按训练、验…

2026/9/23 5:02:33

iPhone 18 Pro首发实测:A20 Pro芯片与VC均热板能效散热深度解析

1. 首发实测:A20 Pro 芯片的真实提升幅度1.1 从跑分到体感,性能提升到底有多少每年新 iPhone 发布,最先被拿出来讨论的永远是那颗芯片。今年 iPhone 18 Pro 搭载的 A20 Pro,官方口径是“历代最大幅度能效跃升”,但真正…

2026/9/23 5:52:35

Agent技能库设计实战:从工具封装到参数契约的完整指南

做AI Agent开发这段时间,我最大的感触就是:模型能力决定Agent的下限,技能库决定Agent的上限。让大模型“说”不难,难的是让它“做”——去做检索、调接口、改文件、跑报表。而这一切的前提,就是得有一套设计良好的agen…

2026/9/23 5:52:35

HT06近场探头:DC-1Hz~20GHz电磁干扰精准定位原理与实战

1. 这不是普通探头,是EMC工程师的“听诊器”和“显微镜”你拆开一块刚返工回来的电源板,示波器上50MHz的尖峰刺眼得像根针;客户产线突然批量出现Wi-Fi断连,产测报告里只有一句“辐射超标”,却没人能说清是哪个电容在漏…

2026/9/23 5:52:35

开源大模型技术演进与中文优化实践

1. 开源大模型的技术演进与国内现状2023年堪称大模型技术爆发的元年,全球范围内GPT-4、Claude等闭源模型引发热潮的同时,开源社区也迎来了Llama 2、Falcon等重量级选手。在国内市场,这场技术竞赛呈现出独特的"双轨并行"特征&#x…

2026/9/23 5:52:35

珠宝品牌跨界体育营销策略与数字化实践

1. 项目背景与商业价值解析杭州大亨珠宝近期推出的"体育冠军点赞祝福"营销活动,本质上是一次典型的品牌跨界营销案例。这类活动通常选择在年末或重要时间节点推出,通过名人效应与情感营销的结合,实现品牌形象升级与用户情感连接的双…

2026/9/22 10:02:42

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/22 9:07:39

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/23 0:01:54

3个实战技巧搞定形式英语:从看教程到跑通性能优化

3个实战技巧搞定形式英语:从看教程到跑通性能优化 看了一堆教程还是不会写项目?别慌,这种“眼高手低”的困境在开发者圈子里太常见了。很多人以为卡点在语法,其实真正拦路虎是缺乏将知识点串联成完整链路的能力。今天咱们不聊虚的,直接拿【形式英语】这…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码