ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue 3 入门第一天:从环境搭建到响应式组件实战

Vue 3 入门第一天:从环境搭建到响应式组件实战 如果你今天才第一次打开 Vue 的文档大概率会被页面左侧那一长串目录劝退响应式、指令、组件、生命周期、路由、状态管理……文档当然没错但信息密度对一个“第一天学 Vue”的人来说太不友好了。没几个人能在第一个下午同时消化这么多概念硬啃的结果往往是半天之后依然只会照着示例改改文字一旦报错就完全懵。我带新人入门 Vue 时习惯把学习拆成一个个按天走的小任务。Day 1 不碰路由、不碰 Pinia、不碰各种花式魔法只做好一件事把开发环境跑起来写一个真正有交互的小页面并且能说清楚页面上的每一行代码在干什么。这篇文章就把这第一天最该掌握的知识点完整过一遍按我自己带新人的顺序来排里面也掺了不少这些年实际踩过的坑。不管是零基础想入门还是后端转前端想快速上手 Vue照着走完这一天你的 Vue 之路就算真正迈出第一步了。1. 第一天学 Vue先圈定知识边界1.1 Vue 到底解决的是什么问题Vue 是个前端框架这是你肯定知道的事。但你得先想明白“框架”到底帮你干了什么活后面学起来才不会跑偏。在没有框架的时代我们要在页面里显示一段后端返回的数据典型操作是这样的用document.getElementById找到那个 DOM 节点再手动把数据拼接成 HTML 塞进去。这还不算完用户改了输入框的值你要去监听 input 事件再更新另一个元素的内容。一个页面只有三五个交互点还能忍等页面一复杂代码里到处都是“先查 DOM、再改 DOM”的重复劳动改一个地方漏三处的情况比比皆是。Vue 的核心思路是你别去碰 DOM你只管维护一份数据页面该长什么样由 Vue 根据这份数据自动算出来。数据变了页面跟着变用户操作变了数据页面也会反向更新。这个“数据和页面自动同步”的机制就是所有人嘴里说的“响应式”。第一天你不需要把响应式原理完全吃透但必须建立起这个心智模型页面是数据的投影你改数据页面自己会刷新。现在网上还能搜到大量 Vue 2 的教程我的建议是直接忽略它们。Vue 3 从 2020 年发布到现在已经是绝对主流组合式 API 的写法比 Vue 2 的选项式 API 更符合人的直觉生态里几乎所有新工具都默认支持 Vue 3。你现在学 Vue 2等于考完驾照先去学开手动挡旧车能开但没必要。1.2 第一天的必学清单与先别碰清单我见过太多人第一天就跑去研究 vue-router 的动态路由、各种插槽嵌套、store 里的复杂状态管理结果半天下来把自己学自闭了。第一天你需要的是一条最短路径让我先把范围划清楚。第一天必须掌握先别碰等过几天再说用 Vite 创建项目、跑起开发服务器vue-router 路由尤其是动态路由和路由守卫模板插值{{ }}Pinia / Vuex 状态管理指令v-bind、v-on、v-model、v-if、v-for插槽 slot 的进阶用法默认插槽会用即可ref、reactive 与 computed自定义指令、Teleport、KeepAlive组件拆分、props 传参、emit 触发事件SSR、Nuxt、低代码平台这类重型架构这个清单不是我随便拍的。仔细看第一天必学的内容你会发现它们刚好凑成一个最小的“闭环”你能创建项目能渲染数据能处理用户点击和输入能实现条件展示和列表循环能把自己的页面拆成组件。完成这个闭环你就能写出一个带搜索、待办、留言功能的实用小页面。有了完成感和正反馈后面学路由、学状态管理才会有底气。1.3 第一天的实战目标一个待办事项页面学任何框架纯看教程都是最慢的路径。我第一天给新人布置的实战任务很固定做一个小型待办事项页面必须包含这些功能顶部一个输入框按下回车或点击按钮把内容添加到下方列表列表可以展示“已完成”和“未完成”两种状态点击文字可以切换状态每一项后面有一个删除按钮点击后移除该项一个简单的统计信息显示还剩几项没完成这个任务看着小但它几乎覆盖了第一天所有语法v-model 做输入绑定、v-for 渲染列表、v-if 做条件展示、click 处理删除和切换、ref 维护数据、computed 计算剩余数量。后面我讲的每个知识点你都用这个例子去练一遍比看十遍文档都管用。2. 环境搭建从空目录到页面真正跑起来2.1 先检查 Node 版本这步偷懒后面全是坑很多人的环境配置失败不是操作问题而是 Node 版本不匹配。Vite 是现在 Vue 项目最常用的构建工具它对 Node 版本有硬性要求Vite 5 需要 Node 18 以上更新的 Vite 6 推荐 Node 20.19 以上。如果你机器上装的是 Node 16就算项目创建成功一跑npm run dev也可能报一堆语法错误新手很容易在这一步直接心态爆炸。打开终端先检查一下node -v npm -v如果版本低于 18别想着硬磨直接用 nvmNode 版本管理器装一个新版本。nvm 允许你在电脑上装多个 Node 版本随时切换比你去系统目录里手动卸载重装干净得多。装好之后重新打开终端再跑一次node -v确认版本号上来了再继续。2.2 用 Vite 创建项目并理解目录结构Vue 官方推荐的脚手架从 Vue CLI 慢慢转移到了 Vite新手直接选 Vite 没有任何问题。创建一个项目npm create vitelatest vue-day1命令执行后它会问你几个问题框架选 Vue语法选 JavaScript。这里我不推荐第一天就选 TypeScript——不是说 TS 不好而是第一天你的注意力应该全部放在“Vue 本身怎么用”上TS 的类型报错会额外干扰你判断问题。先跑通再上类型这是更平滑的学习曲线。创建完成后进入目录安装依赖cd vue-day1 npm install npm run dev浏览器打开终端里打印的地址看到一个有 Vue 图标的欢迎页说明你的开发环境已经通了。趁这个热乎劲我建议你花两分钟看一下项目里这几个文件是干嘛的后面改代码才不迷路package.json记录项目依赖了哪些包以及有哪些脚本命令。npm run dev后面跑的就是这里定义的dev命令。index.html整个项目的 HTML 入口。Vite 开发模式下它会在浏览器里加载/src/main.js作为启动文件。/src/main.jsJS 入口负责创建 Vue 应用并挂载到页面上。里面有一行createApp(App).mount(#app)意思就是把根组件App渲染到index.html里 id 为app的那个 div 内。/src/App.vue根组件文件你现在看到的欢迎页就是它渲染出来的。后面改页面基本就是从它下手。第一次看到这些文件不用紧张你先记住一件事.vue文件是 Vue 项目的核心单元页面最终是由一个个 .vue 组件拼装起来的。2.3 安装依赖最容易翻车的三个点npm install这一步看着简单实际每一天都会有人卡住。根据这些年看到的报错最常见的有这么几类镜像问题。国内直接执行npm install有时候会很慢甚至超时解决办法是让 npm 走国内镜像npm config set registry https://registry.npmmirror.com设置之后再装依赖速度会明显快一截。权限问题。如果在安装时出现EACCES permission denied多半是 npm 没有足够的文件写入权限。不要直接sudo npm install那条路会让后续所有的 node_modules 文件权限都变得很怪。正解是检查你的 Node 是不是装在系统保护目录比如/usr/local里如果是请用 nvm 重装 Node把 npm 全局目录放到用户目录下之后再装依赖就不会有权限问题了。node_modules 该不该发别人。这算一个常识node_modules是本地安装依赖后生成的文件夹动辄几百兆里面有大量重复文件它不应该被提交到 Git也不应该别人要源码时直接打包发过去。正确做法是只发源码含package.json和package-lock.json别人拿到后执行npm install就能恢复同样的依赖环境。这个原则后面讲“项目源码怎么发给别人”时还会再提。2.4 热更新与第一个亲手修改Vite 开发服务器有一个很贴心但新手常忽略的功能热更新替换HMR。你打开src/App.vue把 template 里的欢迎文字随便改成“你好Vue”保存浏览器里那个页面不需要你手动刷新就已经变了。第一天你一定要亲手体验一次这个“改了代码页面自己变”的反馈因为后面写页面时你的工作流就是改代码 - 切到浏览器看效果 - 再回来改。理论上最优先掌握的是最小 Vite 环境的知识开发服务器是 Vite 在跑它会把你的 .vue 文件实时编译成浏览器认识的 JavaScript然后推送更新给页面。理解到这一层就够了Vite 内部做了什么复杂的依赖预构建等你有兴趣了再深入。3. 模板语法与指令让页面学会“说话”3.1 插值表达式最直观的数据展示Vue 页面里最常见的就是双花括号插值template p{{ message }}/p /template script setup const message Hello Vue /script{{ message }}会把 script 里定义的message变量的值显示到页面上。这里有个规律不用死记script 里定义的普通变量默认情况下只要被模板引用就会参与响应式更新。但注意Day 1 为了不绕晕最好在模板里只写简单表达式比如{{ firstName lastName }}或者{{ count 0 ? 有货 : 缺货 }}。如果你发现模板里写了一长串计算逻辑那通常应该挪到方法或计算属性里而不是堆在双花括号里。可能会有老教程提到v-text指令也能展示文本实际开发里几乎没人用双花括号已经是惯例这个知识点知道有它就行。3.2 从属性到事件指令与简写光有插值只解决了“把数据显示出来”这件事。页面还要能设置元素的属性、监听用户点击。这就是指令登场的时候。v-bind用于动态绑定 HTML 属性比如给图片动态换地址img v-bind:srcimgUrl / !-- 简写 -- img :srcimgUrl /v-on用于监听 DOM 事件button v-on:clickaddItem点我/button !-- 简写 -- button clickaddItem点我/buttonVue 里很多指令都有简写我把最常用的几个整理成一张表方便你直接记住指令完整写法简写说明v-bindv-bind:href:href绑定属性v-onv-on:clickclick绑定事件v-modelv-modelinputVal无单独简写双向绑定v-if / v-else / v-showv-ifcondition无单独简写条件渲染v-forv-foritem in list无单独简写列表循环上面表格里特意没写“指令简写”相关的补充是因为这里才是真正的重点v-bind 和 v-on 有简写但 v-model、v-for、v-if 没有所谓更短的写法。有些新同学搜“vue指令简写”看到网上文章才彻底明白:src就是v-bind:srcclick就是v-on:click仅此而已。v-model单独拿出来说。它做的是双向绑定也就是视图和数据“互相联动”。最典型的场景是输入框input v-model.triminputVal placeholder请输入待办事项 /v-model会自动做到两件事数据变化时输入框的值跟着变化用户在输入框里打字时数据inputVal也会自动更新。.trim是修饰符意思是在输入内容时自动去首尾空格。类似的修饰符还有.number转数字、.lazy改为失焦或回车时同步而不是每次敲键都同步。Day 1 记住.trim就足够了其他用到再查。3.3 条件渲染与列表渲染有了变量绑定、事件绑定、双向绑定你还需要让页面能“根据数据状态决定显示什么”以及“把一组数据铺开来展示”。条件渲染是一组配套指令p v-ifdone这条已经完成/p p v-else这条还没完成/pv-if是直接把元素加进或移出 DOMv-show则只是用 CSS 把元素隐藏display: none。怎么选一个粗糙但好用的判断是如果状态切换很频繁用 v-show如果这个分支很少变成现实用 v-if 更干净。拿电灯做类比v-if 是直接“把灯泡摘下来”v-show 是“把灯关上但电线还在”。第一天不用过度纠结绝大多数情况下 v-if 就够你用了。列表渲染用 v-forli v-foritem in todoList :keyitem.id {{ item.text }} /likey是一个很关键的属性。它的作用是给每个列表项一个稳定的身份标识让 Vue 在列表变化时能准确对比新旧节点知道哪些项需要复用、哪些需要删除。如果你不给 key或者用数组下标当 key在列表有增删、排序操作的场景下会出现各种诡异的状态错乱——比如你删了第 2 项结果第 3 项输入框里残留的内容跑到第 2 项上去了。这个坑你越早养成习惯越好v-for 永远带上 key而且 key 不要用 index除非你的列表是纯静态展示。这里再补一个很多新手踩过的坑不要在同一个元素上同时使用v-if和v-for。旧版 Vue 中 v-for 的优先级更高同时使用很容易出现意思和你直觉不符的条件判断即使能跑也容易引发意外。如果你的需求是“列表为空时显示空提示有数据时渲染列表”正确做法是分开两个元素用v-if判断数组长度再用另一个v-for渲染列表或者把过滤逻辑放到 computed 里处理。3.4 第一个综合小例子到这里我们第一天必学的模板语法已经全部到齐。我把它们拼成一个极简待办输入框你可以自己在 App.vue 里改来试手template div input v-model.triminputVal keyup.enteraddItem placeholder输入后回车 / ul li v-foritem in todoList :keyitem.id input typecheckbox v-modelitem.done / span{{ item.text }}/span /li /ul /div /template script setup import { ref } from vue const inputVal ref() const todoList ref([]) const addItem () { const text inputVal.value if (!text) return todoList.value.push({ id: Date.now(), text, done: false }) inputVal.value } /script注意上面代码里出现了一个前面没详细讲的ref()。别急这正是下一节要拆解的内容——Vue 的响应式系统也是你理解“为什么数据变了页面自己会刷”的最后一块拼图。4. 响应式系统为什么数据变了页面自己会刷新4.1 从手动操作 DOM 到声明式渲染先回到最开始那个问题在没有框架的年代你要把输入框的内容同步到别的元素上得手动监听事件再手动赋值。Vue 把这件事变成了“声明式”你只管说“这个输入框的内容对应变量 x这个页面的文字对应变量 message”剩下的事情它帮你看着办。这个“自动看着办”的系统就是响应式系统。你在 script 里用ref定义了一个变量模板里引用了它之后无论你在代码里什么时候修改这个变量的值模板里所有用到它的地方都会自动更新。不需要你去写任何一行操作 DOM 的代码。4.2 Vue 3 的 ref 与 reactive怎么选Vue 3 的响应式 API 有好几个ref和reactive是最常碰到的。怎么快速理解它们的分工const count ref(0) // 推荐能放数字、字符串、数组、对象 const state reactive({ // 推荐用于复杂对象但直接替换会有问题 list: [], loading: false })Vue 3 内部是通过Proxy代理对象来实现拦截属性读取和修改的所以你在写count.value 1时实际是触发了代理的 set 拦截逻辑Vue 发现这个值变了就去通知所有依赖它刷新视图的地方。ref和reactive最直观的区别是ref在 script 里访问值要用.valuereactive直接.属性名就能访问。不过在 Vue 3.3 之后你可能听说过ref可以做响应式解构之类的高级特性但 Day 1 记住下面的结论就够用了基础类型的数字、字符串、布尔值必须用ref只有它能处理这些类型。数组、对象建议也用ref包一层虽然代码里写着.value有点烦但统一心智模型最省心。reactive存在的意义是让你少写.value它有隐患直接替换整个对象时响应性会断开比如reactive({ a: 1 })再state { a: 2 }就会失效解构出来的变量也会丢失响应性。模板里使用ref不需要写.valueVue 会自动给你解包所以 script 里的一点点繁琐换来的是模板里的简洁。第一次接触.value的人会觉得十分别扭这完全正常。我的建议是第一天别去研究响应式原理的细节你只需要知道用ref定义数据修改时记得加上.value模板中使用时不用加。等你写顺手了再慢慢去体会 Proxy 和 ref 解包的深水区。4.3 v-model 背后的语法糖原理一般来说初学者第一眼看到 v-model 会觉得它就是个绑定输入框的神奇属性记住就行。但你要是想真正理解 Vue 组件的通信闭环必须知道 v-model 不过是“值绑定 事件监听”的语法糖。input v-modelinputVal / !-- 等价于 -- input :valueinputVal inputinputVal $event.target.value /也就是说v-model 本质上做了两件事用:value把数据放进去用input监听用户输入并把输入框的最新值写回inputVal。将来你自定义一个支持 v-model 的组件时按同样的套路实现:model-value和update:model-value就行。理解这一点会给很多问题带来启发。比如为什么有个modelValue这种奇怪的名字因为它就是 v-model 默认绑定的属性名。再比如你在模板里写input v-modelobj.text /这种嵌套对象的绑定为什么能生效因为 v-model 的语法糖里本来就不限制绑定到对象的哪个属性上。4.4 计算属性让“依赖数据的答案”自动跟新待办页面的“还剩几项未完成”这个统计如果你真的用模板双花括号里写复杂表达式又会变成一坨难读的东西。更好的做法是使用计算属性computedimport { computed, ref } from vue const todoList ref([ { id: 1, text: 学习 Vue, done: false }, { id: 2, text: 敲示例代码, done: true } ]) const remainingCount computed(() { return todoList.value.filter(item !item.done).length })模板里直接用{{ remainingCount }}就行。计算属性的关键是它会把内部依赖的响应式数据记住只要这些数据没变多次访问remainingCount都会直接用缓存结果不会反复执行过滤逻辑一旦依赖的todoList变了它才会重新计算。对比一下如果你写一个普通函数每次在模板里调用页面每次重新渲染它都会重新跑一遍。这才是一个很好的启发点凡是“依赖已有数据计算出来的新值”都应该优先用 computed 而不是普通函数。计算属性还有一个干净的好处它看起来就像普通变量模板里不需要加括号调用。这对模板的可读性提升非常明显。5. 组件化把整个页面拆成能复用的积木5.1 单文件组件一个 .vue 文件的三段式结构前面说了Vue 项目里页面是由 .vue 文件组合起来的。一个 .vue 文件内部通常分成三个区块template写页面的 HTML 结构Vue 的指令也只能在这种组件模板里用script setup写逻辑包括数据定义、事件处理、计算属性style scoped写这个组件的专属样式建议的新手拆法是把“待办输入区”拆成一个组件TodoInput把“待办列表区”拆成另一个组件TodoList。父组件 App 只负责把数据给它们并约定它们怎么把事件抛出去。这么做的好处是等你要做更复杂的页面时每个组件只需要关心自己的事情行数少、逻辑清晰、出 bug 也好定位。5.2 父传子用 props 把数据递下去父组件要把todoList传给子组件去渲染语法很简单!-- 父组件 App.vue -- TodoList :itemstodoList /子组件里用defineProps声明接收这个属性!-- TodoList.vue -- script setup defineProps({ items: { type: Array, required: true } }) /script template ul li v-foritem in items :keyitem.id{{ item.text }}/li /ul /templateprops 就好比父组件递给子组件一张只读的说明书子组件能看但不能私自修改传进来的数据。为什么不让子组件直接改因为数据源在父组件那里如果子组件能随便改父组件和其他共享这份数据的组件就全乱了套。想改数据怎么办通过事件也就是下面要说的 emit。5.3 子传父用 emit 把“消息”抛上去子组件删掉一个待办时它自己不能直接动items正确做法是告诉父组件“有人删除了这个 id你看着办”。子组件里定义事件并触发!-- TodoItem.vue -- script setup const emit defineEmits([delete]) const onDelete () { emit(delete, item.id) } /script template li{{ text }} button clickonDelete删除/button/li /template父组件里监听这个事件TodoItem v-foritem in items :keyitem.id :itemitem deletehandleDelete /父组件收到 id再执行真正的删除逻辑const handleDelete (id) { todoList.value todoList.value.filter(item item.id ! id) }emit这个名字有朋友觉得很抽象我一般建议把它理解为“往父组件喊话”子组件自己不处理它只是把消息喊出去让父组件来决策。这个通信方向在 Vue 组件体系里是最底层的规则后续学 provide/inject、vuex、pinia 都只是在此基础上的增强。5.4 样式冲突与 scoped为什么明明一样的关键字却各自为政Day 1 的组件最容易出问题的场景之一是样式冲突。你写了一个.title红色另一个组件里也写了一个.title蓝色结果页面里哪个title的样式生效完全看加载次序非常玄学。Vue 给单文件组件提供的方案是scoped属性style scoped .title { color: red; } /style加上 scoped 后Vue 在编译时会自动为当前组件的元素加一个类似>style scoped :deep(.some-outer-class .some-inner) { color: #333; } /styleDay 1 你只要记住两条经验写组件样式第一反应就加 scoped别犹豫遇到“为什么我这个组件的样式老被别人的覆盖”时优先检查是不是没加 scoped。6. 第一天实测会遇到的问题与后面的路线6.1 三个常见报错的排查思路failed to load tsconfig vue/tsconfig/tsconfig.web.json: tsconfig not found。这个报错一般出现在用某些带 TypeScript 的脚手架创建项目时。它的问题是项目的tsconfig.json里继承了vue/tsconfig这个包提供的配置但脚手架或项目里没有正确安装这个依赖或者版本不匹配。新手最直接的解法绕开 TypeScript重新用纯 JavaScript 模板创建项目如果你确实需要保留 TS那就执行npm install -D vue/tsconfig把缺失的依赖补上再检查tsconfig里配置的路径是不是和这个包一致。npm install卡住或报网络错误。除了前面提到的镜像问题还有可能是网络代理设置导致 npm 无法出网。排查时可以看报错里有没有ETIMEDOUT、ECONNRESET之类的关键字多半就是网络问题切镜像后再试即可。端口被占用。跑npm run dev时如果提示Port 5173 is already in useVite 默认会自动换一个端口所以通常不用管如果你想指定端口改vite.config.js里的server.port配置即可。6.2 项目源码怎么发别人不要一上来就打包 node_modules这个热词我特别熟悉因为几乎每个人新手期都干过“把整个文件夹压缩发给同事”的事。500M 的压缩包里 node_modules 占了 480M对方解压后项目照样跑不起来因为 node_modules 里可能有本机安装的工具链差异和缓存残留。正确做法分两种想让对方继续开发把整个项目文件夹提交到 Git 仓库记得写.gitignore把 node_modules 和 dist 排掉对方git clone后执行npm install npm run dev就能启动。只想让对方看成品执行npm run build然后把生成的dist目录给他。dist里是所有源码编译压缩后的产物可以直接部署到任意静态服务器也可以双击本地打开查看页面的实际效果。这两种方式和“把源码发给别人”是两件不同的事别混淆。开发要源码看效果要 dist。6.3 学会 Day 1 之后下一步怎么走第一天的知识点已经够你写很多小工具页面了。之后再往后走主线仍然很清晰第二天学 vue-router把“页面跳转”和“导航切换”纳入体系理解动态路由参数。第三天学组件通信的进阶形态插槽 slot、provide/inject以及生命周期与网络请求。第四天开始接触状态管理 Pinia同时你就会发现任何复杂的交互在掌握了组件化和响应式后都只是流程的组装。等你把这些主线走完再去接触实战业务会无比顺畅比如用 HLS 流处理 m3u8 视频播放、在 Vue 项目里集成地图组件、甚至理解为什么市面上那么多低代码平台喜欢选 Vue 做基石——因为组件化、响应式、可复用的思维方式正是低代码的底座。这些听起来高大上的东西拆开看全是你在第一天打下的基础原理。学 Vue 头一天的体验很多人会感觉像学任何一门新语言语法有点绕环境有点烦。但说句实在话只要你把今天这些内容亲手敲过一遍、改过几个报错后面所有的 Vue 知识都会从这个最小的闭环里生长出来。我见过太多新人在第一天纠结于“我还没记住所有 API”其实完全没必要API 是查出来的真正值钱的是你脑子里的那张地图——数据驱动视图组件拼装页面父子之间通过 props 和 emit 说话。把这套心智模型刻进去明天进入路由的时候你会回来感谢今天认真搭建环境的自己。
返回列表