Vue Day4:组件化、路由与状态管理,从简单页面到工程化项目

Vue Day4:组件化、路由与状态管理,从简单页面到工程化项目 先说个结论Vue这个框架之所以能火这么多年核心不是语法多炫而是它的学习曲线真的平缓。哪怕你前三天学得迷迷糊糊Day4照样能跟下来因为今天我们只干一件事——把“页面”升级成“项目”。前三天我们搞定了模板语法、指令、事件绑定这些基本功今天开始进入组件化开发从一个简单的demo变成真正能跑的工程化项目同时把Vue Devtools、路由、状态管理这几个绕不开的词一次性讲透。这篇内容适合三类人正在跟着系列学习的小白、学过一遍但始终搞不清组件通信的新手、以及想从“写页面”过渡到“做项目”的进阶学习者。我会按实际开发习惯把环境搭建、组件通信、路由配置、调试技巧串成一条完整链路。这篇的价值在于你按这个顺序操作完会得到一个能运行、能加页面、能传参、能打包发布的完整项目骨架而且是能迁移到真实工作中的那种——不是玩具。1. 内容整体设计与思路拆解1.1 Day4的核心目标建立组件化思维前三天的练习基本都是“单文件作战”——一个HTML页面里用new Vue()或createApp()挂载所有模板、数据、方法都写在一起。这种写法在前几天没问题它能帮我们快速理解响应式、指令、事件这些底层概念。但到了Day4如果还这么写后面的路由、状态管理、工程化配置全都会变成一盘散沙。所以今天的第一块基石是学会把界面拆成组件。组件化不是Vue的发明但Vue把组件通信做得极其顺手。我会用一个“待办事项列表”作为贯穿项目拆成三个组件TodoHeader输入框和按钮、TodoList列表渲染、TodoItem单个项目。这个拆分逻辑顺便把今天的知识点全串起来了每个组件都是独立的template、script setup、style结构组件之间用Props传数据用自定义事件向上反馈操作。组件化的核心意义是降低复杂度。你写一个200行的页面出了问题你得从上往下排查你拆成4个50行的组件每个组件自带边界问题一搜就定位了。这就是为什么实际公司项目里几乎没有“大页面”全是“小组件拼装”。今天的项目虽小但反过来就是在演示这个思维。1.2 为什么推荐用Vite而不是Vue CLI写Day3时你会发现我们还在用vue create或脚手架生成的webpack。但到了Day4我强烈建议你切到Vite。原因很简单Vite在开发环境下的启动速度是秒级Webpack在大型项目里可能要十几秒甚至几十秒。这个差距在小项目里不明显但一旦你的项目路由超过10个、组件超过30个Webpack每次保存后的热更新会让你明显焦躁而Vite几乎一直是毫秒级刷新。Vite的优势本质在于它利用浏览器原生ESModule按需加载而不是像Webpack那样把所有模块打包后再启动。尤其对小白来说更快的反馈意味着更短的等待时间、更高的学习效率。如果你是在今天才第一次接触Vite不用怕后面我会一步步带你怎么创建项目。另外顺带解决一个热词里的高频问题很多人会搜“Vue安装及环境配置”其实安装Vue的环境核心就是Node.js和npm。你在终端输入node -v能看见版本号就说明基础环境没问题。接下来创建Vite项目时框架会自带Vue依赖不用你手动单独装。2. 核心细节解析与实操要点2.1 环境准备Node.js安装与依赖管理前面提到Node.js是Vue项目的基础环境这里补全一个细节Node.js装哪个版本。我的经验是不要装最新版也不要装太老的LST版本一般装当前LTS长期支持版就行。版本太新可能有些依赖还没适配太老则可能不支持Vite高版本。安装完成后终端验证两个命令node -v npm -vnpm是Node自带的包管理器后面安装依赖全靠它。不过国内环境跑默认npm源会特别慢强烈建议配置一下镜像源这个操作不涉及任何违规内容纯粹是换一个更快的下载通道npm config set registry https://registry.npmmirror.com设置好以后后面安装任何依赖都用默认命令即可。这里有个小细节如果你用的是公司电脑可能npm源是内网私服的那就别乱改保持现状即可。我是习惯个人开发环境统一用镜像源省下的时间都用来写业务代码了。2.2 创建项目Vite脚手架实操环境OK之后开一个空白目录在终端里执行npm create vitelatest vue-day4执行后会出现交互式提示选择Vue框架再选择JavaScript版本即可。想一步到位的话可以这样npm create vitelatest vue-day4 -- --template vue cd vue-day4 npm install npm run devnpm run dev启动后终端会显示一个本地地址默认是localhost:5173浏览器打开就能看到Vite的默认欢迎页。这个欢迎页里有几个链接不用急着点重点关注它生成的目录结构vue-day4/ ├── src/ │ ├── components/ │ ├── App.vue │ └── main.js ├── index.html ├── package.json └── vite.config.js小白第一次看到这个目录别慌其实关键只有四个src是你的主要工作区App.vue是根组件main.js是创建Vue实例的入口文件index.html是最终挂载壳子。平时写业务你的重心集中在src下。2.3 Vue Devtools浏览器里最重要的调试工具热词里有人踩过“谷歌浏览器vue插件”的坑今天专门把这个捋清楚。Vue Devtools是Vue官方出的浏览器扩展它能直观展示当前页面的组件树、实时数据、路由地址堪称排查问题第一神器。安装方法很简单Chrome应用商店搜索“Vue.js devtools”安装后浏览器右上角会出现一个Vue字样图标。安装好之后有个常见误解为什么打开自己的页面没看到Vue Devtools图标原因通常是——你正在访问的页面不是Vue项目或者生产环境构建的Vue项目默认不启用组件树只有开发环境才显示。你在本地npm run dev开发模式下打开页面Vue Devtools才能亮起来。Devtools最常见的用法有几种一是看组件树确认页面被拆成了哪些组件二是点某个组件看它的props、data、computed实时修改并观察页面变化三是配合后面的路由查看当前路由地址和路径参数。学会使用Devtools以后调试效率至少提升一倍因为很多问题不用靠猜了直接看数据流。2.4 核心组件通信props、emit与v-model组件化开发中最绕不开的就是组件通信。简单说父组件要把数据交给子组件用props子组件要把数据反馈给父组件用emit自定义事件。很多新手会在这里绕晕其实可以用生活化的方式理解父组件是老板子组件是员工。老板给员工分配任务时可以带上任务说明props员工做完任务以后要向老板汇报“完成了”emit。我们在待办事项项目里就这样拆App.vue作为父组件TodoHeader负责接收用户输入点击添加按钮后把新任务名通过emit抛给AppApp把任务清单通过props传给TodoListTodoList再循环渲染出多个TodoItem。这样就形成了“数据单向向下事件向上反馈”的闭环这也是Vue推荐的单向数据流模式。很多新手会问那v-model到底是啥它其实是“props emit”的语法糖。在子组件上写v-modelkeyword本质上约等于把keyword传进去同时监听update:keyword事件。所以想在自定义组件上用v-model你需要在子组件里这么写template input :valuemodelValue input$emit(update:modelValue, $event.target.value) / /template script setup defineProps([modelValue]) defineEmits([update:modelValue]) /script这种写法在表单类组件里很常见理解了它后面的组件库比如Element Plus的二次封装会顺手很多。3. 实操过程与核心环节实现3.1 待办事项项目从零手撸下面进入今天的重头戏——用Vite项目骨架从零实现一个待办事项。先不要急着复制代码而是带着问题看每一步在解决什么问题我的习惯是做完项目后用一句话说出这个项目的数据流能说出来才算真懂了。第一步改造App.vue。删除默认的Demo代码整成如下结构template div classapp TodoHeader addaddTodo / TodoList :todosfilteredTodos toggletoggleTodo removeremoveTodo / /div /template script setup import { ref, computed } from vue import TodoHeader from ./components/TodoHeader.vue import TodoList from ./components/TodoList.vue const todos ref([ { id: 1, text: 学习组件通信, done: false }, { id: 2, text: 搭建工程化项目, done: true } ]) const addTodo (text) { todos.value.push({ id: Date.now(), text, done: false }) } const toggleTodo (id) { const todo todos.value.find(t t.id id) if (todo) todo.done !todo.done } const removeTodo (id) { todos.value todos.value.filter(t t.id ! id) } const filteredTodos computed(() todos.value) /script这个文件是项目总入口所有数据都在这里集中管理。用ref定义响应式数组用computed做派生数据用方法处理新增、切换、删除。组件间的交互全部通过模板里的add、:todos、toggle、remove暴露出来从外部看一目了然。第二步创建TodoHeader.vuetemplate div classheader input v-modeltext keyup.entersubmit / button clicksubmit添加/button /div /template script setup import { ref } from vue const emit defineEmits([add]) const text ref() const submit () { const trimmed text.value.trim() if (!trimmed) return emit(add, trimmed) text.value } /script这里有个容易忽略的细节提交前必须trim()去空格。不然用户按了个空格你的列表就会多出一条空白任务这种问题定位起来还挺浪费时间的。另外空值判断可以避免无效任务这在真实项目中属于基础防御性编程。第三步创建TodoList.vue和TodoItem.vue。TodoList负责遍历数组、复制每个itemTodoItem负责显示一项任务并提供勾选和删除按钮。这里的关键是TodoItem内部不能直接修改外部传进来的对象属性必须通过emit触发父级的方法再由父级集中处理。这样做的好处是当项目复杂以后数据来源永远是清晰的你不需要从十个组件里找哪里动了数据。3.2 路由配置与传参今日第二个关键模块是Vue Router。项目毕竟要变多页面路由就是页面和URL的映射。先安装依赖npm install vue-router4然后在src下新建router/index.jsimport { createRouter, createWebHistory } from vue-router import Home from ../views/Home.vue import About from ../views/About.vue const routes [ { path: /, name: home, component: Home }, { path: /about, name: about, component: About } ] const router createRouter({ history: createWebHistory(), routes }) export default router之后在main.js里挂载路由import { createApp } from vue import App from ./App.vue import router from ./router createApp(App).use(router).mount(#app)路由参数是热词里被反复搜索的“vue路由参数”这里直接给出最常用的两种写法路径参数和查询参数。路径参数是/user/123配置时写成{ path: /user/:id, component: User }页面里用useRoute().params.id取值查询参数是/search?keywordxx页面里用useRoute().query.keyword取值。还有一个进阶点经常被问到的就是“vue路由拦截器”。路由拦截本质是导航守卫比如未登录用户跳转到登录页router.beforeEach((to, from, next) { if (to.meta.requiresAuth !localStorage.getItem(token)) { next(/login) } else { next() } })我建议小白先别急着在这个阶段使用全局守卫容易把自己绕晕。今天的重点是能把路由跑通能传参并接收参数拦截器之类的留到做后台管理系统时再深入。3.3 状态管理Pinia与Vuex怎么选热词里有个高频对比是“vue pinia vs vuex”。今天我们从务实角度说一下结论并给出可直接落地的方案。历史背景是Vuex是Vue2时代官方的全局状态管理库Vue3初期也是用它但在2022年之后Pinia被官方推荐为新一代方案。现在新项目基本无脑选Pinia原因是它的API更简洁、支持组合式风格、TypeScript友好、体积也更小。装Pinia就两步npm install pinia在main.js里import { createPinia } from pinia const pinia createPinia() createApp(App).use(pinia).use(router).mount(#app)然后定义storeimport { defineStore } from pinia export const useTodoStore defineStore(todo, { state: () ({ todos: [] }), actions: { addTodo(text) { this.todos.push({ id: Date.now(), text, done: false }) } } })在组件里使用import { useTodoStore } from ../stores/todo const store useTodoStore() store.addTodo(学习 Pinia)对比起来Pinia省去了Vuex里繁琐的mutations概念直接在actions里同步修改state这对小白来说极其友好。所以我的建议是今天项目练手直接用Pinia感受一下全局状态管理的链路。等以后接手老项目时再补Vuex的旧语法也不迟。3.4 打包与部署前的检查开发环境下一切正常接下来就是让项目能发布上线。运行npm run build打包Vite会把所有源代码压缩、合并、输出到dist目录。“vue 打包后 布局异常”是热词里的高频问题这里直接说我遇到的最常见场景打包后图片或字体文件路径404了页面布局就会变成裂图错位。解决方式是在vite.config.js里设置相对路径export default defineConfig({ base: ./ })还有一个更隐蔽的坑部署到服务器后刷新某个路由页面直接404。原因是你的服务器没有把所有路径都指向index.html只处理了/其他路径找不到文件自然404。这时候需要在Nginx配置里加一行try_files $uri $uri/ /index.html;。很多人第一次部署到这里就蒙了其实这属于路由历史模式和服务器配置的配合问题跟Vue本身关系不大。另外打包前检查一下有没有环境变量配置问题。热词里“vite vue env.production”就是这个主题——Vite按照不同模式加载.env.development和.env.production文件。比如你的接口地址开发环境用http://localhost:8080/api生产环境用https://api.example.com/api就可以用import.meta.env.VITE_API_BASE_URL分别读取。这条容易忽略但实际项目里几乎必用。4. 常见问题与排查技巧实录4.1 npm install各种报错怎么办从热词里看得出来很多人卡在“vue安装依赖”这一步。常见错误大致分几类一是网络问题表现为下载超时或ETIMEDOUT解决办法是换镜像源或加长超时时间二是版本锁定失败提示ERESOLVE unable to resolve dependency tree多数是老依赖和新依赖冲突。我的排查顺序是先看报错信息最后几行定位是哪个包的问题如果是冲突可以试试删除node_modules和package-lock.json后重新安装如果还不行就查具体包的文档看版本区间要求。排查流程我总结成一张表方便你对照报错关键字常见原因解决方向ETIMEDOUT网络连接不上源切换镜像源ERESOLVE依赖版本冲突删锁文件重装EACCES权限不足在终端使用当前用户权限ENOTFOUND域名解析失败检查网络/DNSERR! code 1编译失败查看报错完整日志记住看到任何带ERR!的大红字先不要慌npm install的报错一般都比其他工具体贴你细心读一遍通常能直接定位到是哪个包不行。4.2 调试Vue组件的三板斧热词里“vue打debug”反复出现这里分享我的三个调试习惯。第一板斧是console.log打点在关键数据变化的位置打印出来看数据是否按预期走。比如在方法开头打印参数、在computed里打印依赖数据基本能定位80%的逻辑问题。有人可能会问“nodejs console.log可以反给vue页面上吗”答案是开发时Vue页面里用的是浏览器console浏览器F12面板的Console就能看到如果用的是SSR或Node环境那后者才会跑到Node控制台上。第二板斧是Vue Devtools。在Chrome开发者工具里找到Vue面板实时查看组件树和状态。修改state时页面立即响应这比靠眼睛看页面判断要精准得多。第三板斧是VSCode里的调试器。在vite.config.js里设置断点配置然后用F5附加到Chrome进程这种方式适合深入排查复杂逻辑但对小白来说优先级要排在前面两个工具之后。先学会打点和Devtools效率已经比大多数初学者高很多了。4.3 前后端联调的常见问题热词里有一类组合是“springboot vue前后端分离”这已经是现代开发的默认模式。前面端完成页面后端用SpringBoot、Node或其它提供API两边通过HTTP对接。联调中最常见的问题是跨域CORS表现为浏览器里发出请求后报CORS policy错误。开发环境最简单的方案是在vite.config.js里配置代理export default defineConfig({ plugins: [vue()], server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这样你的前端请求路径写成/api/usersVite开发服务器就会转成http://localhost:8080/api/users从浏览器视角看请求是同源的就绕过了跨域限制。生产环境部署时更推荐由Nginx统一配置反向代理把/api前缀转发到后端服务前后端同域部署就不存在跨域问题了。另一个联调常见问题是端口冲突。前端默认5173后端SpringBoot默认8080两个端口一般不会撞但如果你自己改了端口就要小心。比如某次我临时把前端改到8080直接和后端撞车两个服务崩溃了一整晚。现在我的习惯是统一整理端口前端用5173、后端用8080、数据库用3306写在README里多看两眼就长记性了。4.4 打包后布局异常与资源404排查前面提过base: ./可以解决大部分资源404。如果改完之后还有问题按下面顺序排查第一步打开浏览器F12看Console网络标签里查看有哪些请求返回404第二步看具体404的是JS文件、CSS文件还是图片资源第三步按资源类型对症下药。JS和CSS一般就是路径问题图片也可能是路径问题但还可能是因为你用了绝对路径或服务器目录不对。还有一个容易被忽略的现象打包后刷新正常首页但刷新内部页面就报404。这个问题前面说了要用Nginx的try_files解决。如果你本地预览打包产物dist目录里打开index.html可能一片空白。这是因为很多资源路径是/assets/xxx没有服务器支持就加载不了。测试打包产物建议用npm run previewVite内置了一个静态服务器能还原生产环境的大多数表现。4.5 组合式API和Options API怎么选很多新手学习Vue3时会被两种写法规格搞纠结是用script setup组合式API还是用旧的Options API我的建议是新项目一律用script setup加组合式API今天的所有示例也都按这个风格来。原因不复杂第一官方现在主推这种写法生态和文档都靠拢第二代码复用靠composable函数比mixins清晰第三TypeScript支持更好以后项目升级顺畅。但是老项目或旧教程里还是大量Options API包括Vue2迁移到Vue3的项目也很大概率保留旧写法。所以小白起码要能看懂两种风格别出现“看到一个data()就懵了”的情况。好在两者的心智模型是相通的之前用data定义响应式数据现在用ref/reactive之前用methods定义方法现在直接在script setup里定义函数之前用computed选项现在导入computed函数。4.6 常见报错速查表最后把今天所有步骤中可能遇到的常见报错整理成表方便你保存备查现象可能原因快速解决npm run dev 报错“vite不是内部命令”依赖没装好执行 npm install页面空白且控制台无报错挂载点ID或入口文件不匹配检查 index.html 里的 div id 是否为 appImport XX from .vue 报错没有写 .vue 后缀Vite 下一般建议写全补全文件后缀组件属性没生效Props 名称大小写问题HTML 中建议用 kebab-casescript 中用 camelCase事件没触发忘记在子组件 defineEmits补上 defineEmits路由跳转了但页面没变RouterView 没放在App里在 App.vue 模板加 router-view修改数据后页面不更新对象新增属性未用新引用用 ref/reactive 的 API 更新或用新对象替换打包后图片403图片是远程链接且防盗链后端允许或下载到本地静态目录这张表本身也是一份很好的复习提纲。你可以过一遍哪一行有疑问就回看对应章节用今天的项目做实验验证。5. 从Day4到Day5给后续学习铺路5.1 用今天的项目做三个练手改造如果你今天把上面的项目完整跑通了恭喜你已经越过“能看懂”和“能动手”之间的坎了。但光跑通还不够我强烈建议你花一小时改造三个细节让知识真正落地把待办事项拆出两个新组件筛选栏全部/未完成/已完成和统计栏还剩N项未完成。给项目加上Vue Router做一个“关于作者”页面并把当前待办数量显示在页面上。把某个表单组件改造成支持v-model的自定义组件以此加深对双向绑定的理解。这三个练手分别对应组件拆分、路由使用、组件通信设计正好覆盖了Day4全部核心知识。别贪多一个一个来每改完一步就刷新一次浏览器观察变化是否如预期。5.2 建议搭配学习的两个工具链话题有热词提到“vue 3 snippets”和“谷歌浏览器vue插件”这两个今天简单提一下前者是VSCode扩展打字时能自动补全Vue模板和组合式API后者前面已经详细演示。我的建议是Day4阶段Vue Devtools一定要装好VSCode扩展可以装一个Vue Language Features (Volar)这样代码高亮和语法提示都会舒服很多。还有一个高频词是“vue和react的区别”今天不展开细聊但给你一个心理锚点框架是工具工具换着学不冲突。你现在的核心任务是先把Vue这一条线走通形成“能独立开发一个完整项目”的能力以后再谈对比和选型。你不用今天就把React也学一遍。5.3 关于面试题的简单预热热词里有“vue面试题”和“js深入浅出vue”说明有一部分人学Vue是奔着找工作去的。我的建议是Day4阶段不要急着背大量面试八股但有两个原理值得提前铺垫。第一Vue对于PHP/Java系开发者极其友好的尤其是你做过模板渲染相关的页面理解起来会顺畅很多。第二如果你想知道“vue框架原理”可以先留个好奇心为什么我给数据重新赋值页面会自动更新这个问题的核心是响应式系统——Vue3用Proxy拦截对象的读写操作。等Day4的项目跑熟之后建议去读一读Vue官方文档中“深入响应式系统”那章思路会拨云见日。不过现阶段把每个组件的职责划分清楚、能调试出来、能正常打包发布比背诵原理重要得多。知识和技能的区别就在于此知识告诉你“为什么”技能告诉你“怎么用”面试官问到底的时候技能会让你从容很多。写到这里Day4的内容基本就完整了。最后分享一个我自己的习惯每次学完一个框架的阶段性内容立刻用一天的实战项目做沉淀。别急着进入下一个知识点先把今天代码重新敲一遍试着不看文档把组件通信写出来。我在教新人时发现能靠记忆独立写出项目骨架的人往后的学习速度会越来越快因为信心和肌肉记忆同时建立了。今天的TodoHeader、TodoList、TodoItem和路由配置建议你也这样过一遍明天我们继续往上走。