ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Vue基本使用与项目实战:从数据驱动视图到组件化开发

Vue基本使用与项目实战:从数据驱动视图到组件化开发 我第一次真正觉得 Vue 好用是在一个后台管理项目里做表格筛选。那时候我已经手写了很多 jQuery 风格的 DOM 操作每加一个字段就要同步改模板、清空节点、重新渲染项目一大了就容易顾此失彼。后来我把数据交给 Vue视图和数据之间就像自动接了一根管子我只需要改数组表格会自己刷新。这种“数据驱动视图”的体验几乎是我现在向别人介绍 Vue 时都会先提的一句话。回到标题本身“Vue 基本使用”其实是个很宽的题目。Vue 官网文档写得已经足够规范但新手真正卡住的往往是环境怎么搭、项目怎么建、组件怎么写、路由和第三方插件怎么接。这篇文章不打算重复文档里每个 API而是把我自己从 0 到一个可用项目的过程拆开讲先交代工具链选择再演示核心语法顺手把路由、样式、打包和几个高频场景的坑处理掉。不管你之前只写过 HTML 还是已经会一点 JavaScript按照这条路径走下来应该都能把 Vue 项目跑起来并且知道下一步去查什么。1. 动手之前先把 Vue 的定位和开发方式理顺1.1 声明式渲染和组件化到底是怎么回事Vue 最基本的心智模型可以概括成八个字状态变视图变。传统写法是你告诉浏览器“先找到 id 为 list 的容器然后循环拼接表格行再插入”Vue 的做法是你声明一个数据容器然后在模板里告诉它“这里放数组的每一项”至于数据变化后如何更新 DOM框架自己算。这个差距在实际项目里非常明显。比如有十个页面都要显示用户列表传统做法要么复制十段 DOM 操作要么抽公共函数但抽完仍然要小心每次调用时 DOM 是否已经存在。用 Vue 就把列表封装成一个组件传入不同的数据源外观和交互保持一致。组件化带来的复用能力才是 Vue 在真实项目中能站稳的底层原因。我会用生活里的例子来理解把 Vue 想象成表格软件里的单元格公式。你只要在单元格里写“A1B1”当 A1 变化结果自动刷新传统 DOM 操作则是每次数据变了都要手动重算并填进单元格。两种方式最终都能得到结果但维护成本完全不是一个量级。1.2 开发工具链VS Code 插件和环境版本怎么定在动手写代码之前我建议先把开发环境准备好。现在的 Vue 项目基本都跑在 Node.js 上所以第一步是装 Node。装完以后在终端执行两条命令确认能看到版本号node -v npm -v我个人的习惯是直接用 nvm 管理 Node 版本这样不同项目要求不同版本时切换很方便。社区里还有一个用法是安装 pnpm 代替 npm安装命令是npm install -g pnpm它最大的优点是从磁盘复用依赖安装速度通常比 npm 快不少。如果只是做 Vue 入门npm 也完全够。编辑器建议用 VS Code配三个插件就够了Volar官方推荐的结构语言支持新版叫 Vue - Official、ESLint 和 Prettier。这三个插件解决的是提示、语法检查和统一格式问题。不要装旧的 Vetur那是 Vue 2 时代的产物在 Vue 3 项目里反而会干扰。1.3 CDN 和 Vite 工程化两种起步方式怎么选Vue 官方支持两种使用方式很多人不太清楚它们各自适合什么场景。一种是通过 CDN 直接引入。在 HTML 里写一个 script 标签指向 vue.global.prod.js再定义一个根节点就能开始写 Vue。这种方式不需要构建工具、不依赖 Node适合在一个已有的小页面或者老系统里快速做交互。缺点是代码不好拆分复用也不方便用单文件组件.vue 文件。如果你只是想感受一下语法或者做个一次性小页面CDN 方式足够。比如下面这就是一个最简 CDN 页面我很推荐第一次接触 Vue 的人先把它跑起来!DOCTYPE html html langzh-CN head meta charsetUTF-8 / titleVue CDN 案例/title script srchttps://unpkg.com/vue3/dist/vue.global.js/script /head body div idapp p{{ message }}/p button clickcount你点了 {{ count }} 次/button /div script const { createApp, ref } Vue; createApp({ setup() { const message ref(你好Vue); const count ref(0); return { message, count }; } }).mount(#app); /script /body /html另一种是工程化方式用 Vite 或 webpack 搭建项目。Vite 是目前官方推荐的脚手架启动速度快配置简单也是这篇博文后面演示用的方案。工程化项目适合业务系统、中后台、移动端 Web 等需要长期维护的场景。两种方式并不冲突很多老项目里也能用 CDN 结合一部分 Vue 来做局部改造关键是明确手头需求属于哪种。2. 用 Vite 从零搭建一个 Vue 项目的完整流程2.1 安装 vue 依赖前的环境检查正式开始项目前我习惯先检查几个环境项。第一步确认 Node 版本全局运行node -vVue 3 和 Vite 5 对 Node 的版本有要求Vite 官方文档会写建议使用 Node 18 或 20。如果你打开项目后发现“ unsupported engine”或者直接报语法错误十有八九是 Node 版本偏低。第二步确认包管理器可用npm 和 pnpm 任选一个。我还建议把包管理器锁在项目里具体做法是项目根目录放一个.npmrc或packageManager字段避免团队成员用不同工具产生分歧。这些不是硬性步骤但能减少后续很多麻烦。这里多提一句安装依赖时要是网络很慢处理方式是配置镜像源这个过程和 Vue 本身无关。用npm config set registry https://registry.npmmirror.com能明显提升下载速度但不同团队的网络策略不同没有统一答案。2.2 创建项目并看懂目录结构我推荐直接执行官方脚手架命令而不是手动一个文件一个文件地建因为官方脚手架会顺手把 Vite、ESLint、开发服务器和热更新都配好# 交互式创建项目名可以换成你自己的 npm create vuelatest my-vue-app执行过程中会询问你要不要 TypeScript、路由、状态管理第一次使用如果没有特殊要求建议先选最小配置TypeScript 可以先不启用。最终生成的目录结构大致如下my-vue-app/ ├── index.html ├── package.json ├── vite.config.js └── src/ ├── main.js ├── App.vue ├── components/ └── views/其中index.html是整个应用的页面载体src/main.js是入口App.vue是最外层根组件。views目录习惯放页面级组件components目录放可复用组件。我想强调一个容易忽视的点Vue 项目本质上还是 HTML 页面只不过页面内容由 JavaScript 动态生成。index.html里通常会有一个空节点div idapp/divmain.js会做类似这样的事import { createApp } from vue import App from ./App.vue createApp(App).mount(#app)理解了这两步后面看任何 Vue 项目的入口文件都不会慌。2.3 浏览器里的 Vue DevTools 一定要装很多新手在调试 Vue 时只会用 console.log看到控制台里打印的是 Proxy 对象打开一层又一层看了半天也找不到关键数据。这里我强烈建议先装 Vue DevTools。以 Chrome 为例正常路径是在浏览器应用商店搜索“Vue.js devtools”安装后浏览器扩展栏会出现图标。打开 Vue 页面时可以直观看到组件树、当前组件的 props、data 或 setup 里暴露的状态还能直接修改数据触发视图更新。如果所在网络访问商店不方便也可以去官方仓库下载打包好的扩展通过手动加载已解压的扩展程序来安装这是浏览器普遍支持的常规做法不需要额外工具。装好以后打开 Vue 页面看到图标亮起来就说明工具生效了。这不是说把 DevTools 当成万能。它适合查看响应式状态和组件层级排查网络请求时还是要看浏览器的 Network 面板。3. Vue 核心语法与组件通信入门3.1 模板语法、v-model 和常用指令Vue 模板里最常见的几个功能点其实不复杂。插值表达式用双大括号p{{ message }}/p给属性绑定动态值用v-bind通常简写成冒号img :srcimageUrl /循环数组用v-for同时记得写keyul li v-foritem in items :keyitem.id{{ item.name }}/li /ul条件显示用v-if和v-show两者区别在于v-if直接控制节点是否存在v-show只是切换 display。建议频繁切换用v-show首次渲染条件不确定时用v-if。表单输入最常用到的是双向绑定input v-modelusername /v-model不是魔法它在内部做的事情是把 value 绑定到数据再监听 input 事件把输入值写回。所以你在自己封装输入组件时也可以用modelValue加上emit(update:modelValue)来实现同样的双向绑定效果。这属于 Vue 进阶的细节但这个理解会直接影响你对表单组件的掌控力。关于v-if和v-show很多面试题会考实际项目里更多是习惯选择。结论是如果节点在页面里只是偶尔出现用v-if如果切换非常频繁比如 tab 切换用v-show更不容易产生布局抖动。两者都写了也不用有压力后续按场景体会即可。3.2 组合式 API 和选项式 API 如何取舍这是 Vue 3 绕不开的话题。先解释清楚选项式 API 是 Vue 2 时代就有的写法数据写在data()方法写在methods相当于把一个组件分成几个固定格子组合式 API 是 Vue 3 主推的写法用ref、reactive、computed、watch这样的函数把逻辑组织起来。同一功能的两段对比代码你体会一下差别选项式script export default { data() { return { count: 0 } }, methods: { add() { this.count } } } /script组合式script setup import { ref } from vue const count ref(0) function add() { count.value } /script这两个计数器组件功能完全相同区别只在组织方式。初学阶段不用过度纠结选哪个我的建议是新项目统一用组合式 API 和script setup原因是复用逻辑时可以写成单独的 composable 函数不用带着一堆 mixin 到处跑。旧项目如果还是选项式也完全能继续维护两者可以在一个项目里同时存在但我不建议在同一个页面里混着写因为可读性会明显下降。就“组合式和选项式混合开发”这个问题我的回答是行但你最好保证一个文件内用同一种风格。组合式 API 里还有一个常被问到的词叫“虚拟 DOM”。很多人以为 vnode 是个组件其实不是。Vue 把模板编译成 JavaScript 对象来描述结构这个对象就是 vnode。组件渲染时会拿 vnode 和真实 DOM 对比只更新变化的部分。理解这一点遇到“vnode 怎么用”这类问题就不会被带偏。3.3 组件通信props、emit 与 provide/inject组件之间传递数据有几个层次。父子组件之间最直接的方式是父传 props子组件用 props 接收子组件要通知父组件通过 emit 抛出一个自定义事件。!-- 父组件 -- UserCard :useruserData clickhandleClick /// 子组件 UserCard.vue script setup const props defineProps({ user: { type: Object, required: true } }) const emit defineEmits([click]) /script如果需要跨层级传递比如爷爷组件要把配置传给最底层的孙组件一层层传 props 会非常啰嗦。这时可以用provide和inject在爷爷组件提供在孙组件注入中间组件不用参与。这个机制适合主题、语言包、用户信息之类的全局配置。那什么时候引入 Pinia 或 Vuex 这样的状态管理我个人的标准是多个页面共享一份数据而且这份数据会被不同组件修改时才值得引入。如果只是父子组件之间的临时数据用 props 和 emit 就好。过早引入状态管理是很多项目开始混乱的原因。4. 路由、样式和第三方库的接入技巧4.1 快速搭一套 vue-router 并理解传参实际业务系统几乎没有不用路由的。Vue Router 是官方路由库安装命令如下npm install vue-router然后在src下新建router/index.jsimport { createRouter, createWebHistory } from vue-router import Home from ../views/Home.vue const routes [ { path: /, name: home, component: Home }, { path: /about, component: () import(../views/About.vue) } ] const router createRouter({ history: createWebHistory(), routes }) export default router在main.js里挂载const app createApp(App) app.use(router) app.mount(#app)模板里的出口是router-view/router-view路由匹配到的组件会渲染在这个位置。路由参数分两种一种是路径参数比如/users/123用useRoute().params.id读取另一种是查询参数比如/users?page2通过useRoute().query.page读取。跳转时写法也不一样路径参数适合定位某个资源查询参数适合筛选和分页。前后端分离场景下开发时最典型的问题是跨域。以 Spring Boot 为后端举例前端通过 Vite 的 devServer 配置接口转发开发环境下请求就不会撞上跨域限制生产环境则由后端或中间层处理同源问题。这部分配置不属于 Vue 语法但项目里几乎一定会遇到。4.2 动态路由与按钮权限的落地思路动态路由在后台管理项目里很常见。它不是在代码里写死跳转路径而是根据登录用户返回的菜单和权限在运行时往路由表里追加页面。基本思路是先定义静态路由比如登录页和 404 页用户登录后拿到菜单数据用router.addRoute注册权限路由。再配合路由全局前置守卫用户未登录或没有权限时直接跳转登录页。按钮权限也是中后台的高频需求。常见做法有两种一种用v-if判断权限码button v-ifhasPermission(user:create)新增用户/button另一种是把判断封装成自定义指令app.directive(permission, { mounted(el, binding) { const required binding.value const allowed currentUser.permissions || [] if (!allowed.includes(required) el.parentNode) { el.parentNode.removeChild(el) } } })用法变成button v-permissionuser:create新增用户/button两种写法我都用过后面这种自定义指令的好处是按钮级权限判断逻辑收敛在一处新页面加权限只加属性不用每个页面重复写判断。如果你的项目里已经有完善的状态管理权限码完全可以从 store 里读取这里只是演示核心思路。4.3 样式处理和 Element 等组件库的引入Vue 组件的样式有三个常见关注点scoped 隔离、全局变量、预处理器。单文件组件里style 标签加上scoped属性样式就只作用于当前组件。这个特性很有用但新手常碰到一个问题子组件内部的样式改不掉。因为子组件内部节点有自己作用域父组件的 scoped 样式不会穿透到子组件内部。这时可以用:deep()选择器style scoped .card :deep(.title) { color: #d33; } /style意思是父组件能“穿透”到子组件内部去改某个类名的样式。注意scoped不等于完全隔离它只是给选择器加属性范围理解这一点碰到样式不生效时就知道从哪查起。第三方 UI 库现在 Vue 3 项目里最常用的是 Element Plus。安装npm install element-plus菜单栏这种常见后台布局Element Plus 有现成的 Menu 组件配合 router 模式和 index 路径就能实现路由切换。如果只需要其中几个组件按需引入如果整个后台都在用全量引入开发阶段也没太大压力。样式方面我再推荐两个思路一是把全站颜色、间距抽成 CSS 变量方便做主题切换二是公共布局类名和组件内部类名分开命名省得后面为了找重叠样式反复调试。5. 真实项目里的疑难杂症与进阶场景5.1 打包、缓存和浏览器兼容的经典坑Vue 项目一旦进入部署阶段很多问题会比开发阶段更棘手。先说“启动时 hard-source 卡住”这个问题。vue-cli 项目如果用了 hard-source-webpack-plugin它会在磁盘上缓存编译中间产物让二次编译更快。但缓存一旦损坏启动时可能长时间卡住表现就是终端停在 hard-source 相关输出上不动。处理办法是删掉项目里的node_modules/.cache/hard-source目录再重新启动。如果你的项目不是 vue-cli 而是 Vite这类问题基本不会出现这也是我推荐新项目优先 Vite 的原因之一。另一个常见需求是打包后的 JS 文件名带 hash。webpack 项目里默认输出可能是app.js这种名字想要带 contenthash 以配合缓存可以在vue.config.js里调整module.exports { chainWebpack: config { config.output .filename(js/[name].[contenthash:8].js) .chunkFilename(js/[name].[contenthash:8].js) } }这样每次内容变化打包文件名也会变浏览器就不会继续用旧缓存。Vite 项目默认已经对生产构建输出加了 hash一般不需要额外处理。再讲浏览器兼容。Vue 3 对旧版浏览器的支持明显收缩尤其是 360 浏览器的兼容模式旧 IE 内核在很多情况下跑不起来。现实项目里比较稳妥的做法是提醒用户切换到 360 浏览器极速模式Chromium 内核或者由运维层强制站点用某种内核渲染再配合 babel 的 core-js 和 polyfill 处理老语法。如果业务真的必须支持 IE 内核那只能回到 Vue 2 时代的技术方案。这不是 Vue 的缺陷而是现代前端框架对运行环境的普遍要求。我整理了一张小表遇到类似问题可以直接按这个思路排查现象可能原因处理方式启动时 hard-source 卡住磁盘缓存损坏删除node_modules/.cache/hard-source后重启打包文件名没有 hashwebpack 输出配置不足调整output.filename和chunkFilename360 兼容模式白屏Vue 3 不兼容旧 IE 内核切极速模式或降级 Vue 2 方案组件销毁后视频仍在播放hls 实例未释放在onBeforeUnmount中调用destroy()5.2 M3U8 直播播放、地图和企业微信 JS-SDK 的场景实践实际开发里Vue 项目经常要和视频播放、地图、通讯工具这类第三方能力打交道。M3U8 是 HLS 视频流常见的索引文件网页原生 video 标签在部分浏览器里不能直接播放通常需要引入 hls.js。在 Vue 里可以封装成一个播放器组件script setup import { ref, onMounted, onBeforeUnmount } from vue import Hls from hls.js const videoRef ref(null) let hls null onMounted(() { if (Hls.isSupported()) { hls new Hls() hls.loadSource(你的.m3u8地址) hls.attachMedia(videoRef.value) } }) onBeforeUnmount(() { if (hls) hls.destroy() }) /script注意m3u8 一定要是合法授权的流媒体地址。使用 hls.js 时组件销毁时要记得调用destroy()否则可能出现声音继续播放或资源没释放的问题。地图场景以腾讯地图为例可以在index.html里引入官方 JS API然后在组件的onMounted里创建地图实例。Vue 本身不限制把外部 class 实例挂到组件数据上但如果直接放进reactive状态深层响应式包装会带来不必要的性能消耗。我一般用shallowRef保存地图实例只保持引用不追踪内部属性。企业微信 JS-SDK 在 Vue 项目里接入也有固定套路。需要先安装wecom/jssdk用wx.config注入签名等信息再在对应生命周期初始化后调用能力接口。注意 SDK 版本如果用了低于 2.3.2 的版本部分企业微信能力可能不可用。在管理后台和企微内嵌网页场景里先判断当前环境是否为企业微信再做 SDK 初始化可以避免普通浏览器里报错。这里收到的签名等参数一般来自后端后端负责计算签名前端只做注入。5.3 用 Vue 开发桌面端和移动端 App 可行吗这个问题几乎每个用过 Vue 的开发都会遇到。桌面端最主流的方式是 Electron。Vue 项目作为渲染进程Electron 负责承载窗口、文件系统和系统能力。Electron 里有“主进程”和“渲染进程”两条运行环境它们之间的通信靠的是 Electron 自己的 IPC 机制这和 Vue 本身没有直接关系。但在 Vue 页面里想调系统能力常规做法是主进程定义 IPC 监听事件preload 脚本通过 contextBridge 暴露接口给页面页面里的 Vue 组件再调用这些接口。所以你在搜索里看到“electron 主渲染进程 ipc 和 vue 有关系吗”答案可以概括成Vue 是界面层通信机制是 Electron 的两者通过桥接配合。移动端打包成原生 App常见路线是用 Capacitor 或 Cordova 这类容器方案。Vue 项目先构建出静态文件再放进 WebView 容器里变成 App。有人问“Vue 前端包如何用 Android Studio 打成 APK”核心不是把 Vue 代码编译成原生 Android 代码而是把打包好的 Web 资源放进 Android 工程用 WebView 加载再通过桥接访问相机、定位这类能力。这类方案的好处是复用同一套 Vue 代码但要注意 WebView 兼容性、首屏加载性能和白屏问题。如果想直接输出跨端小程序或者 App也可以考虑 uni-app它把 Vue 语法映射到不同平台但要注意这本质上是一个自带编译链路的框架和你直接用 Vite 构建 Vue 工程不是同一个规则体系。项目结构、样式限制和生态差异都比较大选型之前需要先了解清楚。6. 写在最后我的几点实操心得把这篇内容从头再看一遍核心就一条学 Vue 基本使用别把重点放在背 API 上而是要抓住“数据驱动视图”和“组件复用”两条主线。我见过太多人一开始就把状态管理、路由守卫、动态组件、插槽这些高级内容全部塞进来结果很快就迷失了。真正有用的路径是先跑通一个最简单的 CDN 页面然后搭建工程化项目再做组件拆分最后才碰路由和状态管理。最后分享一个我自己一直保持的习惯每学一个 Vue 特性我都会写一个最小的复现页面放在本地叫 playground 的文件夹里。比如想搞清楚动态路由就写一个只有两个页面的 demo加几行打印再观察路由变化和组件生命周期。这样遇到新版本变化或者换公司后环境不一致也不用重新查半天文档。这个习惯帮助我快速搭建过 vue3、vue2 等不同场景的项目也很推荐你试试。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进