实战总结:Vue 工程化
本文是对《15-前端Web实战(Vue工程化+ElementPlus)》讲义中 Vue 工程化部分 的总结,并补充了自行了解的”侧边栏 + 动态组件加载”实现。
这一天是前端开发从”脚本式引入 Vue”迈向”工程化开发”的转折点,目标:掌握 Vue 脚手架项目结构、组合式 API 写法,理解单文件组件开发流程。
上篇:Vue 工程化
一、为什么需要前端工程化
1. 传统开发模式的问题
之前在 HTML 中直接用 <script src="vue.js"> 引入 Vue / Axios,存在三大痛点:
| 问题 |
说明 |
| 不规范 |
每次开发从零开始,无统一标准 |
| 难复用 |
多个页面中的组件共用性差 |
| 难维护 |
js、图片等资源无规范化存储目录 |
2. 工程化的 4 个特点
1 2 3 4
| 模块化 → 将 js/css 做成可复用模块 组件化 → 将 UI + 样式 + 行为封装成组件 规范化 → 统一目录结构与编码规范 自动化 → 构建/测试/部署自动完成
|
一句话:前端工程化就是把开发所需的工具、技术、流程、经验进行规范化和标准化,从而统一规范、提效、降难、提复用。
二、环境准备
1. create-vue 脚手架介绍
- create-vue 是 Vue 官方最新的脚手架工具,用于快速生成工程化 Vue 项目。
- 提供功能:统一目录结构、本地调试、热部署、单元测试、集成打包上线。
- 依赖环境:必须先安装 NodeJS。
2. NodeJS 安装要点
| 步骤 |
关键点 |
| 安装目录 |
没有中文、没有空格(如 D:\develop\NodeJS) |
| 验证安装 |
命令行执行 node -v |
| 配置全局路径 |
npm config set prefix "D:\develop\NodeJS"(用管理员身份运行) |
| 切换淘宝镜像 |
npm config set registry https://registry.npmmirror.com |
⚠️ 重点提醒:prefix 后的路径必须是 NodeJS 的安装目录,配置错误会导致全局包安装位置错乱。
3. npm 介绍
- npm = Node Package Manager,NodeJS 的软件包管理器。
- 开发中需要的依赖通过
npm install xxx 从远程仓库下载到本地。
三、Vue 项目创建与结构
1. 创建命令
执行后的交互选项(默认值基本都是 No,按需开启):
| 选项 |
含义 |
默认 |
| Project name |
项目名称 |
vue-project |
| Add TypeScript |
是否加入 TS |
No |
| Add JSX Support |
是否加入 JSX 支持 |
No |
| Add Vue Router |
是否添加路由(单页应用) |
No |
| Add Pinia |
是否添加状态管理 |
No |
| Add Vitest |
是否添加单元测试 |
No |
| Add E2E testing |
是否添加端到端测试 |
No |
| Add ESLint |
是否添加代码质量检查 |
No |
2. 项目结构
1 2 3 4 5 6 7 8 9 10
| vue-project/ ├── index.html # 页面入口,包含 #app 挂载点 ├── package.json # 项目依赖和脚本 ├── vite.config.js # Vite 配置(插件、别名等) └── src/ # ← 核心开发目录 ├── main.js # 应用入口:创建并挂载根组件 ├── App.vue # 根组件 ├── views/ # 页面级组件 ├── components/ # 可复用组件 └── assets/ # 静态资源(CSS、图片)
|
📌 重点:以后操作最多的就是 src 目录,前端代码都在这里写。
3. 启动项目
| 方式 |
命令/操作 |
| 命令行 |
npm run dev |
| VSCode 图形化 |
点击 NPM 脚本中 dev 后的运行按钮 |
启动后访问:http://localhost:5173
四、Vue 项目开发流程(SFC)
1. 什么是 SFC
SFC(Single-File Components,单文件组件):Vue 将一个组件的逻辑 (JS)、模板 (HTML)、样式 (CSS) 封装在同一个 *.vue 文件里。
2. .vue 文件三段式结构
1 2 3 4 5 6 7 8 9 10 11
| <script setup> </script>
<template> </template>
<style scoped> </style>
|
五、API 风格(重点对比)
Vue 组件有两种写法:组合式 API 和 选项式 API。
1. 组合式 API(Vue3 推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script setup> import { ref, onMounted } from 'vue';
const count = ref(0);
function increment() { count.value++; }
onMounted(() => { console.log('Vue Mounted....'); }) </script>
<template> <input type="button" @click="increment"> Count : {{ count }} </template>
|
2. 选项式 API(Vue2 风格)
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> export default { data() { return { count: 0 } }, methods: { increment: function() { this.count++ } }, mounted() { console.log('vue mounted.....'); } } </script>
|
3. 两种风格对比
| 对比项 |
组合式 API |
选项式 API |
| 标识 |
<script setup> |
<script> + export default |
| 数据定义 |
const x = ref(值) |
data() { return {...} } |
| 方法定义 |
普通函数 function fn() {} |
写在 methods 选项里 |
| 钩子函数 |
onMounted(() => {}) |
mounted() {} |
this 指向 |
没有 this(undefined) |
指向当前组件实例 |
| 访问响应式值 |
count.value |
this.count |
| 推荐度 |
✅ Vue3 主推 |
兼容 Vue2 老项目 |
⚠️ 易混淆:组合式 API 中没有 this!访问 ref 变量必须用 .value(模板里除外,模板自动解包)。
六、案例:UserList.vue(组合式 API + Axios)
1. 安装 axios
2. views/UserList.vue 完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| <script setup> import { ref, onMounted } from 'vue' import axios from 'axios'
const userList = ref([]) const name = ref('') const gender = ref('') const job = ref('')
const search = () => { axios.get(`https://web-server.itheima.net/emps/list?name=${name.value}&gender=${gender.value}&job=${job.value}`) .then(res => { userList.value = res.data.data }) }
onMounted(() => { search() }) </script>
<template> <div id="center"> 姓名: <input type="text" v-model="name"> 性别: <select v-model="gender"> <option value="1">男</option> <option value="2">女</option> </select> 职位: <select v-model="job"> <option value="1">班主任</option> <option value="2">讲师</option> <option value="3">其他</option> </select> <input class="btn" type="button" value="查询" @click="search"> </div>
<table> <tr> <th>序号</th><th>姓名</th><th>头像</th><th>性别</th> <th>职位</th><th>入职时间</th><th>更新时间</th> </tr> <tr v-for="(user, index) in userList" :key="user.id"> <td>{{ index + 1 }}</td> <td>{{ user.name }}</td> <td><img :src="user.image"></td> <td> <span v-if="user.gender == 1">男</span> <span v-else-if="user.gender == 2">女</span> <span v-else>其他</span> </td> <td> <span v-show="user.job == 1">班主任</span> <span v-show="user.job == 2">讲师</span> <span v-show="user.job != 1 && user.job != 2">其他</span> </td> <td>{{ user.entrydate }}</td> <td>{{ user.updatetime }}</td> </tr> </table> </template>
<style scoped> table, th, td { border: 1px solid #000; border-collapse: collapse; line-height: 50px; text-align: center; } #center, table { width: 60%; margin: auto; } #center { margin-bottom: 20px; } img { width: 50px; } input, select { width: 17%; padding: 10px; margin-right: 30px; border: 1px solid #ccc; border-radius: 4px; } .btn { background-color: #ccc; } </style>
|
3. App.vue 引入组件
1 2 3 4 5 6 7
| <script setup> import UserList from './views/UserList.vue' </script>
<template> <UserList></UserList> </template>
|
4. 数据驱动流程
1 2 3 4 5 6 7
| 声明数据 (ref) ↓ onMounted 自动调用 search() ↓ axios 异步请求 → 赋值 userList.value ↓ Vue 响应式更新 → v-for 重新渲染表格
|
扩展篇:侧边栏 + 动态组件加载(自行了解)
这部分是讲义之外,基于”侧边栏菜单切换不同页面”需求自己研究的实现方式。
一、需求场景
很多后台管理系统都有这种布局:
1 2 3 4 5 6
| ┌──────────┬──────────────────────┐ │ 侧边栏 │ 内容区 │ │ ─主页 │ │ │ ─热门 │ (点击菜单切换内容) │ │ ─我的 │ │ └──────────┴──────────────────────┘
|
核心难点:点击左侧菜单,右侧内容区切换成不同组件,但整个页面不刷新。
二、动态组件 <component :is="...">
1. 是什么
<component> 是 Vue 内置的特殊组件,不是 HTML 原生标签。它通过 :is 属性决定渲染哪个组件。
2. 基本写法
1
| <component :is="当前要显示的组件" />
|
:is 后面跟的是组件对象(不是字符串)。
- 当
:is 的值变化时,右侧渲染的组件自动切换。
3. 配合菜单数据使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { ref } from 'vue' import HomeView from './views/HomeView.vue' import HotView from './views/HotView.vue' import MineView from './views/MineView.vue'
const menus = [ { name: '主页', component: HomeView }, { name: '热门', component: HotView }, { name: '我的', component: MineView }, ]
const activeIndex = ref(0)
|
1 2
| <component :is="menus[activeIndex].component" />
|
📌 component 是自定义字段名,可改成 comp、view 等,但模板中要保持一致。
三、侧边栏布局实现
1. App.vue 完整结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| <script setup> import { ref } from 'vue' import HomeView from './views/HomeView.vue' import HotView from './views/HotView.vue' import MineView from './views/MineView.vue'
const menus = [ { name: '主页', component: HomeView }, { name: '热门', component: HotView }, { name: '我的', component: MineView }, ]
const activeIndex = ref(0) </script>
<template> <div class="layout"> <aside class="sidebar"> <ul> <li v-for="(item, index) in menus" :key="item.name" :class="{ active: index === activeIndex }" @click="activeIndex = index" > {{ item.name }} </li> </ul> </aside>
<main class="content"> <component :is="menus[activeIndex].component" /> </main> </div> </template>
<style scoped> .layout { display: flex; }
.sidebar { width: 180px; background: #f5f5f5; }
.sidebar li { padding: 15px; cursor: pointer; list-style: none; }
.sidebar li.active { background: #409eff; color: #fff; }
.content { flex: 1; padding: 20px; } </style>
|
2. 关键语法点拆解
| 语法 |
作用 |
本例 |
v-for="(item, index) in menus" |
循环渲染菜单 |
生成每个 <li> |
:key="item.name" |
列表项唯一标识(优化性能) |
用 name 作 key |
:class="{ active: index === activeIndex }" |
动态绑定 class |
选中项加 active 类 |
@click="activeIndex = index" |
点击修改索引 |
切换高亮 + 切换组件 |
<component :is="..."> |
动态组件 |
渲染对应页面 |
四、数据驱动流程图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 数据定义 menus (菜单数组) + activeIndex (选中索引, ref(0)) ↓ 模板渲染 v-for 生成菜单 <li> :class 根据 activeIndex 高亮当前项 <component :is> 渲染 menus[0].component ↓ 用户点击菜单 @click="activeIndex = index" ↓ Vue 响应式触发 activeIndex 变化 → :class 重新计算 → 高亮切换 → :is 重新计算 → 组件切换 ↓ 视图更新 右侧内容区换成新组件
|
五、进阶:keep-alive 缓存组件状态
问题:默认情况下,<component :is> 切换组件时,旧组件会被销毁,新组件重新创建。如果组件内有输入数据(如表单),切换回来会丢失。
解决:用 <keep-alive> 包裹动态组件,缓存不活动的组件实例。
1 2 3 4 5
| <main class="content"> <keep-alive> <component :is="menus[activeIndex].component" /> </keep-alive> </main>
|
| 对比 |
不用 keep-alive |
用 keep-alive |
| 切换时 |
销毁旧组件 → 创建新组件 |
缓存旧组件 → 复用 |
| 状态保留 |
❌ 丢失(如表单输入) |
✅ 保留 |
| 适用场景 |
切换频繁、状态不重要 |
需保留状态(如表单、滚动位置) |
六、如何新增菜单项
1 2 3 4 5
| 1. 在 src/views/ 下新建 NewView.vue 2. 在 App.vue 中 import NewView from './views/NewView.vue' 3. 在 menus 数组中追加: { name: '新菜单', component: NewView } 4. 无需改其他代码,菜单自动出现
|
常见问题 & 易混淆点
| 问题 |
答案 |
import 何时用 {}? |
具名导出用 {}(如 import { ref } from 'vue');默认导出不用(如 import App from './App.vue') |
.vue 文件是默认还是具名导出? |
默认导出 |
<component> 是 HTML 原生标签吗? |
❌ 不是,是 Vue 内置特殊组件 |
:is 后面跟字符串还是组件对象? |
组件对象(导入的变量) |
组合式 API 里有 this 吗? |
❌ 没有,this 是 undefined |
ref 变量在 JS 中怎么访问值? |
用 .value(如 count.value);模板中自动解包 |
scoped 的作用? |
样式只作用于当前组件,不污染全局 |
| 侧边栏宽度在哪改? |
.sidebar 的 width 属性 |
| 内容区如何占满剩余宽度? |
flex: 1 |
| 动态组件切换后状态丢失? |
用 <keep-alive> 包裹 |
v-if 和 v-show 区别? |
v-if 销毁/创建 DOM;v-show 只切换 display |
常用命令速查
1 2 3 4 5 6 7 8 9 10 11 12 13
| npm config set prefix "NodeJS安装目录" npm config set registry https://registry.npmmirror.com
npm create vue@3.3.4 npm install npm run dev npm run build npm run preview
npm install axios
|
Vue 核心模板语法速查表
| 语法 |
作用 |
示例 |
{{ 表达式 }} |
文本插值 |
{{ user.name }} |
v-model |
双向数据绑定 |
<input v-model="name"> |
v-for |
列表循环 |
v-for="(item, index) in menus" |
:key |
列表项唯一标识 |
:key="item.id" |
:class |
动态绑定类名 |
:class="{ active: isActive }" |
:src |
动态绑定属性 |
<img :src="user.image"> |
@click |
监听点击 |
@click="activeIndex = index" |
v-if / v-else-if / v-else |
条件渲染(销毁/创建) |
v-if="user.gender == 1" |
v-show |
条件显示(display 切换) |
v-show="user.job == 1" |
<component :is> |
动态组件 |
<component :is="menus[i].component" /> |
📌 复习建议:
- 对照本文,理解 Vue 工程化项目每个文件的作用(
main.js / App.vue / views/)。
- 组合式 API 是后续开发主力,重点掌握
ref、onMounted、<script setup> 写法。
- 侧边栏 + 动态组件是后台管理系统的经典布局,建议手敲一遍加深理解。
- 动手试试:新增一个菜单项、改侧边栏宽度、用 keep-alive 缓存组件状态。