ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Vue3入门实战:从Composition API到工程化避坑指南

Vue3入门实战:从Composition API到工程化避坑指南 1. 核心思路与整体设计Vue3入门这个题目咱们今天必须认真聊一聊。前面几章如果你是从头跟过来的对Vue的基础玩法应该已经有了手感从这儿开始所有代码会慢慢换成Vue3的组合式风格。可以说Vue3不是一次简单的版本号升级而是整个框架底层的重构——响应式原理从Object.defineProperty换成了Proxy虚拟DOM的diff算法重写性能提升了一截同时也在写法上把“逻辑组织”这件事重新做了一遍。很多同学学Vue3最大的障碍不是语法难而是“被Vue2的习惯绑住了手脚”。这一章我用一个“从Vue2转过来的老玩家”视角把Vue3入门阶段最该先吃透的几个点拉一遍为什么Vue3值得学、环境怎么搭、Composition API到底怎么用、工程化实战有什么套路、常见坑怎么避。按照这个顺序敲一遍基本可以直接上手真实项目。不管你是准备面试、接手新项目还是想把手里的Vue2老项目迁移过来这一段内容都跑不掉。1.1 为什么Vue3不是“又一个大版本而已”先看响应的底层。Vue2的Object.defineProperty只能劫持对象上“已经存在”的属性所以你新增一个属性时要用Vue.set删属性要用Vue.delete对数组下标和length的操作也需要特殊处理。Vue3的Proxy直接对整个对象做代理属性访问、赋值、删除、in操作、for...in循环统统可以拦截新增属性和数组变更天然支持不再需要那些补丁方法。用一个生活类比Vue2的响应式像在每家每户门口装一个门铃谁装了铃谁才有提醒Vue3的Proxy像在小区门口装了一套人脸识别总闸任何人进出都会被扫描到覆盖面完全不同。其次是模板渲染和diff。Vue3在编译模板时会做静态分析给动态节点打上patchFlag标记diff阶段只对比有标记的部分不再整棵虚拟DOM树挨个遍历。大型列表、复杂页面的更新开销因此小了很多。再配合Fragment多根节点、Teleport传送门、Suspense异步组件等待这些新特性Vue3的工程能力已经不局限于传统页面应用做中后台、可视化大屏、低代码平台都更顺手。还有一个很现实的原因整个生态已经批量切换到Vue3。你随便打开一个招聘要求Vue3TypeScript几乎是标配关键词新出的组件库、工具库默认也只支持Vue3。2024年之后如果还在Vue2的老路子上打转接手的项目会越来越少。这一章把Vue3的核心差异理清楚后面不管学什么新东西都不会慌。1.2 Composition API与Options API到底怎么选这是Vue3学习中最容易纠结的问题。先说结论新项目优先Composition API老项目维护继续用Options API没什么丢人的两者可以共存。Options API把代码按“选项类型”归类data一块、methods一块、watch一块、computed一块。小型页面很清爽但功能一多就有问题——同一个业务逻辑的数据、方法、监听被拆到不同选项块里改一个功能要上下翻好几个区块复用只能靠mixin而mixin的来源不清晰、命名冲突、覆盖关系混乱项目一大就难维护。Composition API的核心思路是按“业务关注点”组织代码script setup import { ref, computed, onMounted } from vue // 计数模块数据 方法 派生状态 副作用全在一起 const count ref(0) const double computed(() count.value * 2) function add() { count.value } onMounted(() { console.log(页面加载完成) }) /script这样同一个功能的代码天然聚在一起。再往上走一步把这段逻辑抽成自定义hook比如useCounter多个组件复用就是一行const { count, double, add } useCounter()的事情不需要mixin那套黑魔法。用TypeScript的时候Composition API的优势更明显。ref、computed、props都有天然的类型推导而Options API里this的类型推断经常要写一堆额外的类型标注。这也是为什么Vue3 TS成为主流组合。至于Vue2老项目迁移不建议一次性推到重来更稳的方式是“新页面用Composition API老页面保持不动”等到页面真正要重构时再顺手转掉成本低很多。1.3 一个Vue3项目的全家桶与目录习惯聊完选型说说一个标准Vue3项目会用到的全家桶。构建工具基本是Vite——冷启动和热更新都明显快于webpack因为它基于原生ESM开发时不用打包整个项目只按需编译浏览器请求的模块。路由是Vue Router 4状态管理从Vuex换成了Pinia工具函数推荐VueUse一个基于组合式API的hooks集合里面useDebounceFn、useLocalStorage、useIntersectionObserver这类常用封装能省掉大量手写代码。一个常见的中后台项目目录大致长这样src/ api/ # 接口请求 assets/ # 静态资源 components/ # 公共组件 composables/ # 自定义组合式函数 layouts/ # 布局 router/ # 路由配置 stores/ # Pinia状态 styles/ # 全局样式 utils/ # 工具函数 views/ # 页面composables目录是这个阶段要特别强调的。以后你写分页逻辑、表单逻辑、权限判断都可以收敛成一个个hook页面代码会越来越薄出问题的概率也随之降低。2. 环境搭建与创建第一个Vue3应用2.1 Node环境与脚手架创建动手之前确认Node版本Vite 5要求Node 18以上建议统一用18.18或者20太低会报依赖兼容错误。命令行检查node -v npm -v创建项目推荐官方脚手架create-vue它底层基于Vite还帮你把Router、Pinia、ESLint、Prettier这些选择都问清楚了npm create vuelatest交互选项里我建议这样选TypeScript选YesJSX看团队习惯Router和Pinia选YesESLint和Prettier选Yes其余保持No。如果你只是想快速起一个不装全家桶的Demo用纯Vite模板也挺好npm create vitelatest my-vue3-app -- --template vue这俩的区别是create-vue带推荐工程化方案适合正经项目create-vite更轻适合自己练手。项目创建完执行npm install与npm run dev浏览器打开终端提示的地址看到默认页面就说明环境没问题。安装依赖时如果网络慢可以配置一下npm镜像这个看个人网络环境不强求。2.2 Vite配置别名、端口与代理一个Vue3项目最先要改的配置就是vite.config.ts。天天写../../../../这种相对路径绝对会疯所以第一件事就是配置别名指向srcimport { fileURLToPath, URL } from node:url import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } }, server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })这里有几个点要说透。alias用fileURLToPath(new URL(...))而不是直接写绝对路径是为了兼容Windows和Linux的路径分隔符避免跨平台出问题。server.proxy解决的是本地联调跨域——前端在5173端口后端在8080端口浏览器直接请求会跨域通过Vite的代理把/api前缀的请求转发到后端同时rewrite把/api去掉。为什么不用axios的baseURL直接写http://localhost:8080因为那样既跨域又得处理凭证cookie远不如代理干净。改完vite.config.ts记得重启开发服务器配置的热更新并不总是生效这是Vite新手最容易踩的坑。2.3 接入SCSS与全局样式变量热搜词里很多人问“Vue3安装scss”其实Vite对SCSS的支持已经非常顺滑只需要装一个sass依赖npm install -D sass装完之后在单文件组件里直接写style langscss就能用嵌套语法。注意现在官方推荐的是sass这个包不再推荐node-sass后者是LibSass时代的产物跟新版Node经常不兼容一堆编译报错都源于此。如果你有全局的SCSS变量和mixin想在每个组件里自动注入不要在每个页面里手动use直接在Vite配置里加additionalDatacss: { preprocessorOptions: { scss: { additionalData: use /styles/variables.scss as *; } } }这样所有组件的样式中都会自动带上变量文件写颜色、间距、断点之类的东西就不用重复引用了。新版sass的一个注意点是import已被废弃要用use和forward来管理模块除法运算也不能直接写/要用math.div。老教程里的写法在最新版本上可能直接编译失败遇到报错先查一下sass版本和语法别急着怀疑Vite。3. Composition API实战核心API必须吃透3.1 ref与reactive到底怎么选这是面试、日常开发都绕不开的经典题。一句话概括基础类型用ref对象类型用reactive但ref其实也能存对象最终内部还是会走reactive。区别在于访问方式ref返回一个带.value的引用对象模板里会自动解包写代码时不需要{{ ref.value }}reactive返回原始对象的代理属性访问直接state.xxx。script setup import { ref, reactive, toRefs } from vue // 基础类型统一走 ref const title ref(Vue3 入门) const count ref(0) // 对象类型用 reactive集中管理 const state reactive({ list: [], loading: false, page: 1 }) // 如果非要解构 reactive记得用 toRefs 保住响应性 const { list, loading } toRefs(state) // 修改 function update() { count.value state.page 2 } /script最大的坑就在解构。reactive返回的是代理对象直接解构出来的不再是代理而是原始值的快照改了不会触发更新。很多人写着写着把state.list解构成const { list } state然后push半天页面没反应就是这个原因。要么不接结构全程用state.xxx要么用toRefs转换。还有一个进阶技巧如果给ref传了一个对象那么这个对象内部也是用reactive做的深度响应式。对于three.js场景对象、海量图表实例这类体积大且不需要响应式的数据建议用shallowRef或者markRaw绕开深度代理避免性能和内存问题。后面聊three.js集成时会再提到。3.2 defineProps、defineEmits与v-model的变化Vue3组件通信里最常用的组合是definePropsdefineEmits。它们都是编译宏不需要导入直接在script setup里写就行。子组件示例!-- Counter.vue -- script setup langts // 定义 props支持类型标注 const props defineProps{ modelValue: number label?: string }() // 定义事件支持类型标注 const emit defineEmits{ (e: update:modelValue, value: number): void }() function add() { emit(update:modelValue, props.modelValue 1) } /script template button clickadd加一/button span{{ label }}: {{ modelValue }}/span /template父组件使用时的写法和Vue2有区别。Vue2的v-model默认对应value属性和input事件Vue3改成了modelValue属性和update:modelValue事件。好消息是模板用法一模一样Counter v-modelcount label当前数量 /更灵活的是Vue3支持多个v-model比如一个组件同时有v-model:title和v-model:content这个能力在表单类组件里特别有用直接替代了Vue2的.sync修饰符。理解v-model的本质很重要它只是语法糖展开后就是:modelValue加update:modelValue所以别怕和事件监听混淆。3.3 watch、computed与生命周期的对应关系组合式里watch和computed都要手动导入。computed的特点是缓存——只有依赖变化才会重新计算所以能放computed的别放方法里。watch的场景是“数据变了要做事”比如搜索关键字变化后防抖请求接口、弹窗开关变化后重置表单、路由参数变化后重新拉数据。script setup import { ref, computed, watch } from vue const keyword ref() const page ref(1) // 监听一个 ref watch(keyword, (newVal, oldVal) { console.log(搜索词变化, newVal, oldVal) }) // 监听一个 getter返回对象需要加 deep watch(() state.form, (val) { console.log(表单变化, val) }, { deep: true, immediate: true }) /script生命周期方面Vue2的created和beforeCreate在Vue3里直接由setup本身替代——你写在setup顶层或者script setup顶层的就是那个时机。其他生命周期映射成onMounted、onUnmounted等函数具体见表格Vue2写法Vue3组合式APIbeforeCreate / createdsetup 顶层代码beforeMountonBeforeMountmountedonMountedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeDestroyonBeforeUnmountdestroyedonUnmounted这个表格建议截图收藏。Vue2的destroyed在Vue3里叫unmounted找不着函数名是初学者高频问题。数据请求、事件监听、定时器清理这类逻辑分别写在onMounted和onUnmounted里成对出现养成这个习惯就能避免很多内存泄漏。4. 工程化实战与优化从Demo到能上线的项目4.1 路由懒加载与“搜索条件保留”问题项目进入工程化阶段第一步就是路由懒加载。Vue Router 4的写法是动态导入组件这样首屏只加载当前页面的代码其他页面按需请求import { createRouter, createWebHistory } from vue-router const router createRouter({ history: createWebHistory(), routes: [ { path: /, component: () import(/views/Home.vue) }, { path: /list, component: () import(/views/List.vue) } ] })接手过Vue2项目的同学应该熟悉() import的写法Router 4里保持了一致。另一个高频需求是“搜索条件保留”——列表页筛选完跳去详情再回来条件全丢了。通常两个方案一个是把搜索条件同步到URL的query参数里刷新、分享、回退都能保持状态另一个是用keep-alive缓存整个列表组件让它切换路由时不销毁。keep-alive用起来很简单router-view v-slot{ Component } keep-alive component :isComponent / /keep-alive /router-view但要注意被缓存的组件里如果有大量图表或长列表内存占用会升高所以“能不用就不滥用”反而是经验。我自己的习惯是列表详情结构的页面用keep-alive其他简单页面尽量不用。4.2 动态添加删除表单的行校验热搜里“vue3动态添加删除form表单一行数据”也经常出现这在中后台表单里太典型了。比如一个成员列表每行要填姓名和手机号用户可以自己加行、删行还要对每一行做表单校验。核心写法是把表单项放在数组里循环动态绑定的prop必须指向数组下标template el-form :modelform refformRef div v-for(item, index) in form.items :keyindex el-form-item :label成员${index 1} :propitems.${index}.name :rules{ required: true, message: 姓名不能为空, trigger: blur } el-input v-modelitem.name / /el-form-item /div /el-form /template script setup import { reactive, ref } from vue const formRef ref() const form reactive({ items: [{ name: , phone: }] }) function addRow() { form.items.push({ name: , phone: }) } function removeRow(index) { form.items.splice(index, 1) } async function submit() { await formRef.value.validate() // 通过校验后再提交 } /script这里最容易翻车的点就是:propitems.${index}.name很多人直接写propname结果校验的时候找不到字段触发不了报错。动态表单的响应式不用慌reactive数组的push、splice都是可响应的。如果做了删除操作后校验错误信息残留先调用formRef.value.clearValidate()再删行这是一个细节坑。4.3 状态管理为什么直接上PiniaVuex 4虽然还活着但新项目没人愿意再碰那一堆mutation样板代码了。Pinia才是Vue3的官方推荐。为什么好用三个理由没有mutation直接改state完全支持组合式写法跟script setup风格统一TypeScript支持好到基本不需要额外类型体操。示例store// stores/user.ts import { defineStore } from pinia import { ref } from vue export const useUserStore defineStore(user, () { const token ref(localStorage.getItem(token) || ) const nickname ref() function setToken(val: string) { token.value val localStorage.setItem(token, val) } function logout() { token.value nickname.value localStorage.removeItem(token) } return { token, nickname, setToken, logout } })在组件里使用script setup import { useUserStore } from /stores/user const userStore useUserStore() function loginSuccess(token: string) { userStore.setToken(token) } /script这种写法几乎等于把全局变量变成有类型的、可追踪的响应式对象。中后台项目里多个页面共享用户信息、权限列表、应用配置都会集中到这里管理。为什么不在组件里直接搞个全局常量因为普通变量不响应、不持久化、不好调试几十个页面一旦状态分散后面查问题会非常痛苦。4.4 后台管理系统选型与首屏加载优化热搜里“vue3 admin最精简框架”和“vue3后台管理系统”都是高频词。我的观点是别一上来就下载那些几千Star的重型后台框架很多自带了一堆你根本用不到的模块。最精简的起点就是create-vue脚手架 Pinia Vue Router 一个组件库Element Plus或Ant Design Vue再加上按需自动导入。组件库按需导入用unplugin-vue-components很舒服// vite.config.ts import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })配置完后模板里直接用el-button、el-table组件和样式会自动按需引入不需要手动import。很多人“引入UI框架不生效”的根源其实就是没装这个插件或者全量引入了JS但样式没引全。首屏加载优化的优先级我按实战效果排个队路由懒加载这个收益最大也最简单组件库按需引入把几百KB的打包体积降到几十KB异步组件defineAsyncComponent非核心区域延迟加载大依赖拆包配置build.rollupOptions.output.manualChunks把three.js、echarts这类超大库单独拆分图片懒加载、CDN分割、gzip/Brotli压缩这些属于基操。热搜里“vxetable怎么避免vue3首屏加载”其实就属于这类问题。VXETable是重型表格组件库全量引体积很大建议按模块引入只注册你需要的那几个表格组件并且配合按需插件使用。如果项目里同时用了Element Plus和VXETable注意区分两个resolver避免命名冲突。5. 常见问题排查与避坑实录5.1 高频问题速查表这部分是我平时带新人、看issue时整理出来的一线高频问题建议先收藏再往下看。现象可能原因排查思路与解决办法引入Element Plus等UI库不生效样式没引全、组件没注册检查是否用了unplugin自动导入手动引入时需import element-plus/dist/index.cssel-upload的on-success监听不到上传接口返回结构不符合预期或自定义了http-request后端返回必须包含response字段自定义上传时手动调用options.onSuccessform的rules日期校验不生效type没写或validator没写好日期字段加type: date区间校验用validator函数若依Vue3项目TS报错类型声明缺失、tsconfig路径没配安装types/node配置baseUrl与paths的/*必要时补declare modulesass装完编译报错node-sass残留或新版sass语法不兼容卸载node-sass改用sassimport改成use除法用math.div动态新增表单校验失效prop路径没指向数组下标用:propitems.${index}.name不要直接写死prop首屏加载慢VXETable体积大全量引入大组件库按需注册表格模块路由懒加载拆包uni-app里ref拿不到组件实例小程序平台限制或拿到了代理对象优先用props/events通信组件内用defineExpose暴露方法Edge浏览器右上角按钮点不了不是Vue3问题是全局样式或遮罩层遮挡检查body覆盖层、z-index、fixed元素命中了标题栏区域PDF预览打不开直接用window.open被浏览器拦截用iframe嵌入或组件vue-pdf-embed服务端配置好MIME页面搜索条件刷新后丢失没存URL也没缓存组件搜索条件同步到route.query或keep-alive缓存组件本地接口跨域前端端口与后端端口不一致用Viteserver.proxy代理不要开浏览器跨域每个问题背后都是一段真实的踩坑经历删删减减留下的基本就是最高频的那批。5.2 调试环境从页面快速定位到代码热搜里“vue3可以根据页面快速定位到代码的插件”指的主要是Volar和Vue DevTools的组合。Volar是VS Code上的Vue语言插件配合TypeScript Vue Plugin使用后模板里的组件名、变量都能正确跳转和补全。在.vue文件里按住Ctrl点击模板中的组件名可以直接跳到组件定义文件。浏览器里装好Vue DevTools后元素面板能看到组件树点击任意组件可以查看props、data、computed当前值。另一个隐藏技能是DevTools的时间旅行调试配合Pinia状态可以做“重置到某个时刻”的效果定位状态问题非常方便。调试异步问题时可以在DevTools里直接监听事件断点而不是到处console.log。5.3 几个实战场景的集成方案速览three.js机房可视化项目里用到vue3 three.js typescript核心注意点是three的场景对象、网格、材质都很大且不需要响应式通常用shallowRef或markRaw包住避免Proxy深度代理带来的性能损耗。渲染循环交给renderer.setAnimationLoop不要放进Vue的响应系统里。onlyoffice在线编辑前端主要是集成官方JS API用iframe加载文档服务地址并传配置。真正的工作量在服务端部署Document Server和权限控制前端注意文档地址要能跨域访问、JWT签名要和服务端一致。PDF预览轻量方案是iframe直接嵌入稍微复杂一点用vue-pdf-embed支持缩放、页码等交互。不建议前端用base64把PDF硬塞进组件大文件会卡死页面。离线地图打点先下载对应区域的瓦片数据放到本地静态目录再用地图库的baseURL指向本地瓦片服务底图和打点都走内网不依赖外网。数据量大的时候注意瓦片目录结构和缓存策略。5.4 面试题与项目经验怎么聊Vue3面试题翻来覆去就那几个核心点Proxy相比defineProperty的优势、patchFlag如何提升diff性能、Composition API的复用方式、ref与reactive的区别、keep-alive原理、响应式丢失场景。背答案谁都会面试官更想听到的是“你在项目里真的用过并且知道为什么”。比如讲动态表单校验失效的坑就能自然引出“响应式数组的操作、element-plus的prop绑定规则、校验触发时机”——这些问题串起来比单纯背Vue.set已移除要精彩得多。再比如讲首屏优化你提到VXETable按需引入和路由懒加载就可以顺带聊一下构建产物的体积分析和拆包策略这属于加分项。做项目时多留一份心把踩过的坑记下来面试和写简历都不用临时编。我个人在这套框架从零跑下来之后最想提醒你的是不要急着把Vue2老项目一次性迁移到Vue3先在新项目里把响应式边界、组件通信、工程化配置跑熟回过头再动老项目会从容很多。最后再分享一个小技巧把vite.config.ts的代理配置、按需组件插件配置、toRefs解构响应式对象这几个常用片段做成代码snippet以后开新项目就是三分钟的事。Vue3的学习曲线其实没有想象中陡峭真正陡峭的是你脑子里那些Vue2时代留下的习惯。
返回列表