ARTICLE DETAIL

资讯详情

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

Vue 3从零到实战:环境搭建、组件路由与项目部署

Vue 3从零到实战:环境搭建、组件路由与项目部署 带过几批从零开始学前端的新人后我发现一个很有意思的现象听到Vue基本使用这个需求很多人第一反应是去找语法教程但劝退大多数人的根本不是某个语法难点而是还没写出第一行代码就卡在了环境上。Node版本不对、脚手架起不来、IDE插件没装对、启动报错看不懂随便哪一步折腾你半天学习的热情就凉了一半。所以这篇东西我打算按自己带新人的路子来写先让你在尽量短的时间里跑起来一个Demo建立正反馈再逐个啃透模板语法、组件、路由这几块硬骨头最后落到样式、图表、文件上传、打包部署这些开发中真正绕不开的高频场景。适合刚接触Vue的朋友也适合从后端转前端的同学快速建立整体概念有经验的老手可以直接跳到第5章和第6章看实战部分。1. 环境搭建的坑先把第一个Demo跑起来再说很多教程上来就讲语法我个人经验是反着的环境通了写代码才有反馈反馈越快学习动力越强。所以这一章先解决跑起来的问题让你在十几分钟内看到一个可以改的页面。1.1 安装Node.js时最容易忽略的版本问题Vue项目开发依赖Node生态Node里自带npm包管理器所有第三方库都通过它来安装。第一个坑往往出在版本上Vite是Vue 3官方推荐的构建工具要求Node版本在18以上如果你随便装了个比较老的Node 14创建项目时不报错但一启动开发服务器终端就会提示当前Node版本不支持。我见过好几个初学者在这里卡了一整天排查到最后才发现是Node版本的问题。所以我建议装LTS版本即长期支持版本不要因为好奇去装最新的奇数版。如果你需要同时维护多个项目最好用版本管理工具来切换Node版本Mac和Linux下用nvmWindows下用nvm-windows。装好之后在终端验证一下node -v npm -v两条命令都有输出说明环境基本就绪。另一个容易被忽视的坑是下载速度。npm默认仓库放在海外网络环境不好时执行npm install经常卡在某个包上或者直接超时失败。解决方式很简单把默认仓库切换成国内镜像源npm config set registry https://registry.npmmirror.com执行npm config get registry确认一下输出地址带npmmirror前缀就说明切换成功。这一步和项目代码没有关系但能替你省下大量等待和报错的时间。镜像源的本质就是换一个更快的下载节点没什么玄机团队协作时统一配置还能避免我这边装得上你那边装不上的差异。1.2 用Vite还是Vue CLI当下新项目只有一个答案创建Vue项目时老教程会让你用Vue CLI也就是vue create命令底层基于webpack启动慢、配置繁琐。Vue官方现在对新项目的推荐是Vite底层用esbuild开发服务器启动几乎秒开热更新也更快。我的判断标准很简单凡是最近几年才立项的新项目直接用Vite只有当你在维护旧项目、仓库里已经写好了一套vue.config.js和webpack自定义配置时才需要继续用Vue CLI。维度ViteVue CLI底层工具esbuild Rollupwebpack冷启动速度快毫秒级慢秒级到十秒级官方推荐度新项目默认已停止积极开发常用场景全新项目存量项目维护创建Vue 3项目可以执行npm create vuelatest这是官方create-vue脚手架会交互式问你项目名、要不要TypeScript、要不要Vue Router、要不要Pinia按需选择即可。如果不想交互也可以一步到位npm create vitelatest vue-basic -- --template vue创建完成后按提示依次执行cd vue-basic npm install npm run dev看到终端输出的localhost:5173浏览器打开是Vue的欢迎页你的第一个Demo就诞生了。这里还有一个容易忽略的IDE问题在VSCode里写Vue单文件组件时如果语法高亮和标签跳转不生效通常是插件装错了。Vue 3对应的插件叫Volar发布名是vue-official不是老插件Vetur。装了Volar之后按住Ctrl点击template里的组件或变量才能正常跳转到定义处。热词里vscode中点vue中的标签没有跳转基本就是这个原因。1.3 第一次启动项目时的常见报错和排查思路第一次跑项目见到的报错大部分就那几种不用害怕。我列一个最容易遇到的表单按报错长什么样、大概率原因、怎么处理来排查报错现象大概率原因处理方式vite 不是内部或外部命令依赖没安装在项目目录下执行npm installerror when starting dev serverNode版本太低升级到Node 18以上EADDRINUSE :::51735173端口被占用换端口或结束占用进程页面空白且控制台报错main.js挂载点写错检查index.html里的#app和main.js的mount依赖装一半报错网络不稳或缓存问题配置镜像源后删掉node_modules重新安装排查报错有个通用顺序先看终端完整报错再按CtrlC停掉服务如果是依赖问题删掉node_modules和package-lock.json后重装如果是页面问题按F12打开浏览器控制台看红色信息通常能直接定位到哪个组件哪一行。这套流程熟练之后后面所有项目都会用到。2. 模板语法三板斧插值、指令与事件绑定环境跑通之后就该理解Vue最核心的思维数据驱动视图。写页面时不再直接操作DOM而是描述数据长什么样页面就长什么样。这个思维转换是新手和老手之间最大的分水岭。2.1 数据驱动视图的核心感觉插值与响应式新建一个Vue组件最基础的样子是这样的template p你好{{ name }}/p /template script setup import { ref } from vue const name ref(小明) /script{{ name }}就是插值表达式模板里的name会自动对应script里的变量。这里要注意Vue 3的一个约定在模板中ref类型的数据不需要写.valueVue会自动解包但在script的JavaScript代码里读取和修改都要用name.value。我第一次写ref的时候经常忘记这点直接打印name出来的不是字符串而是一个带value属性的对象这就是响应式的载体。所谓响应式直观理解就是你改了数据页面里用到这个数据的地方自动更新。Vue 3用ES6的Proxy对象拦截数据的读取和赋值数据被读取时记录依赖被修改时触发依赖更新。相比Vue 2用Object.defineProperty逐条拦截Proxy可以拦截新增属性、删除属性性能也更好。理解这个机制之后写组件的基本心态就变成了你只需要管理数据渲染交给框架。这句话说起来简单但很多人写着写着还是会去手动操作DOM比如document.getElementById去改样式改文本这在Vue里是应该极力避免的因为那等于绕过了响应式系统代码会越写越乱。2.2 v-if、v-show、v-for怎么选以及key为什么重要指令是Vue模板里以v-开头的特殊属性最常用的是条件渲染和列表渲染。v-if和v-show都能控制元素显示隐藏但两者完全不同v-if是真正的条件渲染条件不满足时元素根本不会渲染到DOM中v-show不管真假都会渲染只是用CSS的display:none隐藏。判断标准很简单需要频繁切换显示状态的比如折叠面板内容用v-show初始化时就不该出现、之后切换频率低的比如权限相关的按钮用v-if。前者开销在初始渲染后者开销在每次切换别用反了。列表渲染统一用v-for配合:key比如ul li v-foritem in list :keyitem.id{{ item.name }}/li /ulkey的作用是给每个DOM节点一个稳定身份Vue做虚拟DOM diff时靠它判断哪些节点能复用、哪些需要重建。很多人图省事直接写:keyindex这在列表顺序变化或列表项里含有输入框时会造成状态错位。举一个真实例子一个列表每行前面有个勾选状态你删除第一行后index全部变化后续所有行的勾选状态会错到另一行去。所以key必须选一条业务上不会变的数据比如数据的id、唯一编码实在没有就用内容拼接。还有一个高频坑是v-if和v-for别写在同一个元素上。Vue 3里v-for的优先级高于v-if等于每次循环都做一次判断白白浪费性能。正确做法是先用template标签包住循环把条件判断放在内部或者更推荐用计算属性先把列表过滤一遍再渲染代码更干净性能也更好。2.3 v-model双向绑定其实是个语法糖表单输入是Web应用绕不开的场景v-model把输入框的值同步到数据这件事变成了单向属性绑定加事件监听的组合。下面两种写法效果完全等价input v-modelusername /input :valueusername inputusername $event.target.value /v-model只是语法糖默认绑定value属性监听input事件。想明白这层就知道它不只用于文本输入框下拉框、单选框、textarea都可以只要它们遵循value和input这套约定。以后看到别人的自定义组件也支持v-model本质就是组件内部实现了value属性和input事件的约定。v-model还有几个实用修饰符v-model.lazy把input事件换成change事件输入过程中不同步失焦或回车时才同步v-model.number自动把输入内容转为数字避免取到的是字符串这个经典坑v-model.trim自动去掉首尾空格处理用户名输入很常用。用得最多的表单场景是搜索框加下拉联动搜索框用.trim去掉多余空格城市选择用.number确保拿到的值是数字而不是字符串。这类小修饰符能省去不少手工处理代码但知道它们存在的人反而不多。2.4 事件修饰符少写一段e.preventDefault()模板里给元素绑定事件用事件名最常见的是clickhandleClick。事件处理中的很多细节Vue都提供了修饰符专门用来减少模板里的重复代码。比如表单提交你希望先阻止浏览器默认的刷新行为再执行自己的逻辑form submit.preventhandleSubmit button typesubmit提交/button /form.prevent等价于在事件函数里写e.preventDefault().stop等价于e.stopPropagation()阻止事件冒泡。冒泡问题在实际开发中非常常见一个表格行本身绑定了点击查看详情行里又有个删除按钮绑定了点击删除。如果删除按钮不写.stop点击删除会同时触发行点击详情弹窗和删除弹窗一起弹出来。我第一次做这种表格时就踩过之后就养成了按钮级事件必加.stop的习惯。键盘事件同样有修饰符比如输入框里按回车触达搜索input keyup.enterhandleSearch /不用在函数里判断event.keyCode了。整体来看模板语法的学习重点不是背下每个修饰符而是理解Vue想把对DOM的操作收敛到模板声明里这种设计思路。遇到新场景先想有没有对应的修饰符想不起来再查文档效率最高。3. 组件化开发页面从整块变成积木一个页面塞满所有代码维护三个星期就没人敢动了。组件化就是按功能边界把一个页面拆成一块块积木每块积木有自己的模板、逻辑和样式拼装起来就是完整页面。这是Vue工程化能力里最值得花时间学透的部分。3.1 单文件组件SFC的骨架template、script、styleVue用单文件组件Single File Component组织代码一个.vue文件由三部分组成template div模板区域写HTML结构和Vue指令/div /template script setup // 业务逻辑和数据定义 import { ref } from vue const count ref(0) /script style scoped /* 组件私有样式 */ .count { color: red; } /stylescript setup是Vue 3.2之后推荐的写法。过去的选项式写法要把数据放在data、方法放在methods、生命周期单独一块逻辑分散而setup语法允许你按业务逻辑聚合一个计数器的ref、函数、watch全在同一个代码块里几乎不需要来回滚动找代码。这也是Composition API的核心思想按业务逻辑聚合而不是按选项类型聚合。在script setup里还有一个很爽的点导入的组件可以直接在template使用不需要手动注册。比如import Header from ./components/Header.vue之后模板里直接写Header /即可。代码量少了一大截也让组件即变量的直觉成立。3.2 父子组件通信props向上emit向下组件拆开后就必须解决通信问题。父组件往子组件传数据用props子组件内部数据需要上级知道时通过emit事件向上传。先看父组件template Child :titlepageTitle savehandleSave / /template script setup import { ref } from vue import Child from ./Child.vue const pageTitle ref(用户管理) const handleSave (data) { console.log(收到子组件提交的数据, data) } /script再看子组件script setup const props defineProps({ title: { type: String, required: true } }) const emit defineEmits([save]) const submit () { emit(save, { name: 张伟 }) } /script这里有两个容易犯的错误。第一props数据不要在原位修改。数据在父组件里是单一数据源子组件只负责展示使用如果多个子组件都直接改同一个props父组件反而不知道是谁改的排查起来非常痛苦。非要修改时要么复制一份到本地ref要么emit一个事件让父组件去改。第二defineEmits声明了事件名后模板里父组件用save监听命名要保持一致。如果是驼峰式事件名saveData模板里建议统一写成save-data。这个约定一开始不习惯但多人协作时能避开大小写引发的隐蔽bug。单向数据流看似多写了代码实际上是强迫让数据的流向可追踪这是多人项目里最有价值的一条约定。反正我在公司里看代码第一眼就会从props和emit的边界判断这个组件质量靠不靠谱。3.3 插槽Slot让组件内容可定制的一种更优雅的方式先看一个场景你封装了一个Card卡片组件希望它在不同页面有不同的标题和正文但外壳的边框、圆角、头部样式完全一样。这时候再用props传复杂结构会很别扭正确做法是留一个插槽。子组件模板div classcard div classcard-headerslot nametitle //div div classcard-bodyslot //div /div父组件这样填充内容Card template #title订单列表/template p这里是卡片正文会落到默认插槽中/p /Cardslot /是默认插槽slot nametitle /是具名插槽父组件通过#title对应填充。这种做法把组件容器和容器里的具体内容解耦比用props传一串HTML字符串优雅得多。再进阶一步是作用域插槽它解决的是父组件想控制某块内容的展示但数据在子组件里的问题。典型例子是表格组件表格行数据由子组件管理但某列比如状态列怎么渲染父组件希望自己决定。子组件把row对象暴露给插槽DataTable :rowsrows template #default{ row } span{{ row.status 1 ? 启用 : 停用 }}/span /template /DataTable父组件拿到的只是使用数据的权利数据本身仍在子组件手里改数据仍然走emit。理解插槽之后再去翻组件库里的table、dialog、dropdown实现你会发现很多复杂机制都能看懂个大概了。4. 路由系统SPA多页面的导航地图单页应用里的页面切换并不刷新浏览器而是通过JavaScript控制渲染区域的内容。vue-router就是管理这套映射关系的核心库也是Vue项目里几乎必装的模块。路由学明白之后多页面应用才算真正搭起来。4.1 使用vue-router路由怎么配置跳转怎么写安装路由后在src/router/index.js里做配置import { createRouter, createWebHistory } from vue-router const routes [ { path: /, component: () import(../views/Home.vue) }, { path: /about, component: () import(../views/About.vue) } ] 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)之后App.vue里只需要放两个关键组件router-link to/about关于/router-link负责渲染成可点击的导航链接router-view /负责根据当前路由渲染对应组件。为什么不能用普通的a标签因为a标签的默认行为是发起一个完整页面请求浏览器会重新下载资源、刷新整个页面SPA里的JavaScript状态全部丢失。router-link内部用history API拦截了跳转只让路由变化、重新渲染组件页面不刷新资源不重载。路由配置里的component用了() import(...)这种写法叫路由懒加载。它告诉Vue只有访问到该路径时才下载对应组件代码而不是一开始就把所有页面代码塞给浏览器。项目页面多起来之后这个写法对首屏加载速度影响巨大算是前端性能优化里成本最低的收益点。4.2 动态路由与路由参数列表页到详情页几乎每个后台系统都有列表页点到详情页的需求路由这样配置{ path: /user/:id, component: () import(../views/UserDetail.vue) }这个:id就是动态路由参数页面里这样取script setup import { useRoute } from vue-router const route useRoute() const userId route.params.id /script列表页跳转则用useRouter拿到路由实例后pushimport { useRouter } from vue-router const router useRouter() const goDetail (id) { router.push({ path: /user/${id}, query: { from: list } }) }query参数在详情页用route.query.from读取适合保存上一页是谁这类非必要标识。这里有一个面试几乎必问的坑同一路由只改变参数时组件实例会被复用不会重新走生命周期。比如你在详情页点下一条数据路径从/user/1变成/user/2但因为没切换组件onMounted不会重新触发页面显示的仍是前一条数据。解决办法是监听参数变化import { watch } from vue watch(() route.params.id, (newId) { // 用新id重新请求数据 fetchDetail(newId) })这个问题在列表详情切换场景里几乎必现提前写进代码里能少掉不少头发。4.3 路由守卫与登录鉴权页面进入前的关卡很多页面要求只有登录后才能访问你当然可以在每个页面的onMounted里检查一下登录状态但这样既重复又容易漏。更优雅的解法是路由守卫——在路由匹配完成、组件渲染前拦截一下router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login }) } else { next() } })配合路由配置里的meta字段{ path: /admin, component: AdminLayout, meta: { requiresAuth: true } }这样每次进入受保护的页面守卫自动检查token没有就踢回登录页。为什么用守卫而不是每个组件自己判断核心原因是横切关注点应当集中管理权限校验、登录态同步、埋点上报这些逻辑和具体页面无关散落在每个组件里必然出现漏写和写法不一致集中写在路由守卫里一处改动处处生效。进阶之后还可以给不同角色加meta.permission字段在守卫里做更细的权限判断或者在afterEach里做访问日志埋点。做法相同扩展思路比记API更重要。5. 实战高频场景样式、图表、文件上传与多媒体展示入门语法学完真正开始做业务页面时会遇到一批每个项目都得上一次的需求。我把其中几类高频场景的处理套路整理在这里都是直接能抄的姿势。5.1 scoped样式的作用域与样式穿透单文件组件里的style scoped加scoped后Vue编译阶段会给当前组件的每个元素添加一个data-v-xxxxxxxx属性同时给CSS选择器加上[data-v-xxxxxxxx]前缀。这样写出来的样式只影响当前组件不会污染全局。这是组件化的一个重要保障你的button是红色的别人的button是蓝色的互不干扰。但scoped不是万能的它有一个让新手困惑的设计父组件的scoped样式无法影响子组件内部的元素除了子组件的根节点。子组件根节点会同时带上父组件的scope属性所以父组件可以对子组件根元素设置margin、width这类外围样式。当你想改子组件内部某个元素的样式时最常见的是覆盖Element Plus、Vant这类组件库的内部class就要用样式穿透.card :deep(.inner-box) { background: #f0f0f0; }:deep()告诉Vue这个选择器要保留原样不带scope后缀让它能穿透到子组件深层。样式穿透要和style属性优先级、选择器权重一起考虑否则容易出现写了半天不生效实际是被scoped挡住的迷局。排查这类问题的口诀先看元素上有没有data-v属性再看样式选择器有没有被改写最后看谁的权重更大。5.2 内容折叠展开、文本超出隐藏高频CSS细节折叠展开在Vue里实现非常直接因为控制显示隐藏的指令很好用template button clickexpanded !expanded {{ expanded ? 收起 : 展开 }} /button div v-showexpanded这里是需要折叠的内容/div /template script setup import { ref } from vue const expanded ref(false) /script这里用v-show而不是v-if是因为折叠内容通常需要频繁切换v-show只需要切换display属性开销小。如果还想让折叠过程有平滑动画可以给div加max-height过渡或者使用transition组件等做项目时再按需深化。文本超出隐藏几乎是每个列表页都会遇到的样式需求。单行省略.ellipsis { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }多行省略.multi-ellipsis { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }为什么多行没有text-overflow这种正统方案因为CSS规范只为单行文本提供了text-overflow属性多行截断目前最常用的就是-webkit-line-clamp这个非标准方案但现代主流浏览器都支持直接用没有兼容性负担。Vue本身不需要为这些CSS写额外逻辑按需把class绑在元素上就行。5.3 在Vue里集成ECharts画柱状统计图做管理后台基本绕不开图表ECharts是其中最常用的选择。集成套路非常固定先装包再初始化npm install echarts组件里这样写template div refchartRef classchart / /template script setup import { ref, onMounted, onBeforeUnmount } from vue import * as echarts from echarts const chartRef ref(null) let chart null onMounted(() { chart echarts.init(chartRef.value) chart.setOption({ xAxis: { type: category, data: [一月, 二月, 三月] }, yAxis: { type: value }, series: [{ type: bar, data: [120, 200, 150] }] }) }) onBeforeUnmount(() { chart chart.dispose() }) /script style scoped .chart { width: 100%; height: 400px; } /style有两个坑最常踩。第一个初始化时机必须在onMounted之后因为此时DOM才真正渲染完成ref指向的元素有真实宽度。如果你在setup顶层就调用echarts.init拿到的还是没挂载的DOM初始化出来的图表是空白的。第二个页面有多张图时比如要画两个柱状统计图每个图表要独立的ref接收容器不能用同一块DOM初始化两次。窗口大小变化时需要监听resize并调用chart.resize()否则图表会变形window.addEventListener(resize, () chart.resize())组件销毁时用dispose释放实例可以避免数据频繁刷新时的内存泄漏。这套模式在ECharts项目里几乎长一样熟练后可以封装成一个公共图表组件需要什么配置直接往option里传业务页面就干净了。5.4 多文件上传、PDF预览、m3u8播放的处理套路先看多文件上传最简单的方式是原生input加multiple属性input typefile multiple changehandleFiles /const handleFiles (e) { const files Array.from(e.target.files) const formData new FormData() files.forEach((file) formData.append(files, file)) axios.post(/api/upload, formData) }多个文件必须append成同一个字段名后端用数组类型接收。如果要前端预览用URL.createObjectURL(file)生成一个临时URL赋给img或video标签即可。文件上传本身不是Vue特有的知识难点在进度条、断点续传、大文件分片这些进阶专题别在一开始就陷进去。vue image能显示pdf吗这类问题的答案其实是决定权在浏览器不在Vue。浏览器天然支持PDF预览Vue能做的只是把后端返回的PDF地址交给iframe或embed标签去展示。如果PDF是接口返回的二进制流可以先用Blob包装再生成object URL套进去const blob new Blob([res.data], { type: application/pdf }) const url URL.createObjectURL(blob) window.open(url)同理m3u8本质是多媒体切片索引文件原生video标签并不直接支持需要借助hls.js这类库把m3u8转成浏览器能播的流或者让后端直接提供mp4。Vue层做的事就是引一个库、调用它的加载播放API本质上和页面渲染无关。把这些浏览器能力边界分清楚遇到类似问题就不会再把锅甩给Vue了。6. 从开发到交付源码交接、打包集成与版本选型代码写完了还得让别人能跑起来、能部署上去。这一章聊开发收尾阶段最常问的几件事都挺琐碎但每件都能省一次出丑。6.1 项目源码怎么发给别人更专业直接把整个项目文件夹压成zip甩给同事是最常见也最不专业的做法。node_modules目录动辄几百兆与操作系统、Node版本强相关别人解压后往往并不能直接用。正确做法是压缩前先删除node_modules和dist两个目录保留package.json、package-lock.json以及src、public等源码目录。对方拿到压缩包后在项目根目录执行npm install再npm run dev就能启动。package-lock.json就是依赖版本的锁确保对方装到的依赖和你的完全一致。项目里没有锁文件、只靠package.json里的大版本号很容易出现我这边好的你那边报错的经典纠纷。比压缩包更推荐的做法是用Git管理项目把代码推到远程仓库GitHub、Gitee、私有GitLab都可以把仓库地址发给对方。对方clone下来再按README里写的命令安装依赖、启动开发服务器。带上历史提交记录对方也更容易理解你的开发思路。记得在.gitignore里忽略node_modules和dist否则推上去的仓库又大又凌乱。6.2 打包后放进SpringBoot后端的正确姿势纯前端项目上线时最终运行包是构建产物。执行npm run buildVite会生成dist目录里面就是可以直接部署的静态文件。做前后端一体部署时常见做法是把dist里的静态文件放进SpringBoot的src/main/resources/static目录随Jar一起发布用同一个端口对外提供接口和页面。但这中间最容易出问题的是路由的history模式。vue-router使用createWebHistory时URL里不带#号用户刷新/user/1这个路径时浏览器会向服务器发起/user/1的请求。生产阶段服务器没有对应的静态文件SpringBoot默认会返回404或白屏。有两种修法。第一种是后端兜底转发在SpringBoot里注册一个统一转发规则把非静态资源的未知路径都转发到index.html交给Vue路由接管。第二种是前端改用createWebHashHistory创建路由URL变成/#/user/1本质上只有路径部分变化不会发新请求也就不存在后端404问题。代价是URL带个#号不算美观但小团队省事很多。我的建议内部管理系统用hash模式完全够用对外产品URL外观很重要就上history模式加后端转发。6.3 Vue 2还是Vue 3、Nuxt到底是什么新手问Vue 2还是Vue 3不需要纠结太久新项目一律Vue 3理由很直接——Vue 2.7之后官方已停止功能更新新生态的组件库、插件、工具链基本都围绕Vue 3展开。Vue 3带来的实质性变化包括Composition API、Proxy响应式、更好的TypeScript支持、更小的运行时体积。如果你没有特别强的历史包袱直接在Vue 3里学基本使用不会走弯路。至于Nuxt它不是一个独立的框架而是Vue的完整应用框架。普通Vue项目只是一个纯前端SPA搜索引擎爬虫拿到的是空壳HTML不利于SEO服务端渲染、路由预取、大量工程化配置都需要自己搭。Nuxt把路由、SSR、SEO、请求层全部内置好了甚至支持静态站点生成。粗粒度判断标准是做后台管理、内部工具这类不需要搜索引擎收录的系统Vue本身足够做官网、内容站这类需要SEO和更好首屏体验的公开页面优先考虑Nuxt。两者不是学一个就放弃另一个的关系而是同一个底层在不同场景下的差异化封装。先把Vue的基本使用学扎实再看Nuxt时你会觉得大部分概念都能互相印证。最后分享一点我带新人的个人体会。很多初学者喜欢排一条特别长的学习路线今天指令、明天组件、周末开始啃Pinia结果战线太长两三周后前面学的忘干净。我自己的建议是把跑通一个小而完整的页面作为第一个目标哪怕只是一个带搜索框、列表、详情跳转的极简页面把这里讲到的环境、模板语法、组件通信、路由参数全部串起来用一遍效果胜过刷十遍文档。遇到问题多看看浏览器控制台报错和官方文档的快速上手再配合Vite的秒级反馈基本几天就能建立起对Vue的整体手感。以后真碰到状态管理、SSR、工程化优化这些带着真实需求再去查印象反而更深。
返回列表