Vue 项目开发必备知识点
本文档系统整理了Vue开发中的核心概念、技术要点和最佳实践,涵盖从基础到高级的各个方面,帮助开发者全面掌握Vue技术栈。
目录导航
-
1. Vue 核心概念
- Vue 实例
- 模板语法
- 组件系统
- 响应式原理
-
2. Vue 组件通信
- 父组件向子组件传递数据
- 子组件向父组件传递数据
- 兄弟组件间通信
- 跨多层组件通信
- 非父子组件通信的其他方式
-
3. Vue 生命周期
- 生命周期概述
- 创建阶段
- 挂载阶段
- 更新阶段
- 销毁阶段
- Vue 3 生命周期变化
-
4. Vue Router
- 路由配置与注册
- 动态路由
- 嵌套路由
- 路由参数传递与获取
- 导航守卫
- 路由懒加载
- Vue Router高级特性
-
5. Vuex/Pinia 状态管理
- Vuex 核心概念
- Pinia 核心概念
- Vuex 与 Pinia 的区别与选择
- 状态持久化
- 高级特性与最佳实践
-
6. Vue 生态系统
- UI组件库
- 构建工具
- HTTP请求
- 测试工具
- 其他生态工具
-
7. Vue 3 新特性
- 组合式API (***position API)
- Teleport
- Suspense
- ***position API vs Options API
- 其他Vue 3新特性
-
8. 性能优化
- 组件懒加载
- 虚拟列表
- 缓存策略
- 图片优化
- 渲染优化
- 打包优化
- 网络优化
- 运行时优化
- Vue 3特有优化
-
9. Vue移动端开发特别注意事项
- 触摸事件处理
- 响应式布局
- 性能优化
- 用户体验优化
- 适配问题
- 常用库推荐
-
10. Vue 最佳实践
- 项目结构组织
- 组件设计原则
- 代码规范与质量
- 性能最佳实践
- 测试与调试技巧
- 可维护性考虑
- 部署与发布策略
-
11. Vue 表单处理
- 表单绑定
- 表单验证
- 自定义表单控件
- 表单状态管理
-
12. Vue 动画与过渡
- 过渡类与效果
- 动画钩子函数
- 列表过渡
- 第三方动画库集成
-
13. Vue SSR 与 SSG
- SSR 基本原理
- Nuxt.js 框架
- SSG 静态站点生成
- SSR/SSG 性能优化
-
14. Vue 微前端方案
- 微前端架构
- 常用微前端框架
- Vue 与微前端集成
- 微前端部署策略
-
15. Vue 国际化 (i18n)
- Vue I18n 插件
- 多语言资源管理
- 国际化最佳实践
- 日期、数字与货币格式化
1. Vue 核心概念
-
Vue 实例:
- 实例创建:
new Vue({/* 配置选项 */}) - 配置选项:data、methods、***puted、watch、props、***ponents、生命周期钩子等
- 实例属性与方法:
$data、$props、$el、$mount、$nextTick、$forceUpdate等 - 实例与组件的关系
- 实例创建:
-
模板语法:
- 插值表达式:
{ { message }},支持JavaScript表达式 - 指令系统:
v-bind、v-model、v-for、v-if/v-else、v-on、v-show等 - 指令修饰符:
.stop、.prevent、.once、.lazy、.number、.trim等 - 计算属性:带缓存的响应式属性,基于依赖自动计算
- 监听器:对数据变化做出响应的方法
代码示例:模板语法与指令
<!-- 插值表达式 --> <p>{ { message }}</p> <p>{ { count + 1 }}</p> <p>{ { isActive ? 'Active' : 'Inactive' }}</p> <!-- 指令示例 --> <div v-if="isVisible">Visible content</div> <div v-else>Hidden content</div> <ul> <li v-for="item in items" :key="item.id"> { { item.name }} </li> </ul> <input v-model="username" placeholder="Enter your name"> <button v-on:click="submitForm">Submit</button> <!-- 指令简写 --> <img :src="imageUrl" alt="Product"> <button @click="handleClick">Click me</button> <!-- 计算属性与监听器 --> <div> <p>Original price: { { price }}</p> <p>Discounted price: { { discountedPrice }}</p> <!-- 计算属性 --> </div>// 计算属性与监听器示例 const vm = new Vue({ data() { return { price: 100, discount: 0.2, username: '' } }, ***puted: { // 计算属性 - 有缓存,只有依赖变化时才会重新计算 discountedPrice() { return this.price * (1 - this.discount); } }, watch: { // 监听器 - 响应数据变化 username(newValue, oldValue) { console.log(`Username changed from ${ oldValue} to ${ newValue}`); } } }); - 插值表达式:
-
组件系统:
- 组件注册:全局注册(
Vue.***ponent)和局部注册 - Props:父子组件间数据传递,支持类型校验和默认值
- 自定义事件:子组件通过
$emit触发事件,父组件通过v-on监听 - 插槽:默认插槽、具名插槽、作用域插槽
- 动态组件:
***ponent元素和:is属性实现组件动态切换
代码示例:组件创建与注册
// 全局组件注册 Vue.***ponent('global-button', { props: { type: { type: String, default: 'primary' }, disabled: { type: Boolean, default: false } }, template: ` <button :class="['btn', 'btn-' + type]" :disabled="disabled" @click="handleClick" > <slot>Button</slot> </button> `, methods: { handleClick() { if (!this.disabled) { this.$emit('click'); } } } }); // 局部组件定义 const Local***ponent = { template: ` <div class="local-***ponent"> <h3>{ { title }}</h3> <p>{ { content }}</p> </div> `, props: ['title', 'content'] }; // 在父组件中使用局部组件 const Parent***ponent = { ***ponents: { 'local-***ponent': Local***ponent }, template: ` <div class="parent"> <h2>Parent ***ponent</h2> <local-***ponent title="Local ***ponent Title" content="Local ***ponent content goes here" /> <global-button @click="handleButtonClick">Global Button</global-button> </div> `, methods: { handleButtonClick() { console.log('Button clicked!'); } } };代码示例:Vue 单文件组件 (SFC)
<!-- My***ponent.vue --> <template> <div class="my-***ponent"> <h2>{ { title }}</h2> <p>{ { description }}</p> <slot name="content"></slot> <slot name="footer" :count="items.length"></slot> </div> </template> <script> export default { name: 'My***ponent', props: { title: { type: String, required: true }, description: { type: String, default: '' }, items: { type: Array, default: () => [] } } }; </script> <style scoped> .my-***ponent { padding: 20px; border: 1px solid #eee; } h2 { color: #333; } </style>代码示例:使用插槽
<template> <div> <my-***ponent title="***ponent with Slots" description="This ***ponent demonstrates slot usage" :items="listItems" > <!-- 默认插槽内容 --> <p>This is content for the default slot.</p> <!-- 具名插槽内容 --> <template v-slot:content> <ul> <li v-for="item in listItems" :key="item.id">{ { item.name }}</li> </ul> </template> <!-- 作用域插槽内容 --> <template v-slot:footer="slotProps"> <p>Total items: { { slotProps.count }}</p> </template> </my-***ponent> </div> </template> <script> import My***ponent from './My***ponent.vue'; export default { ***ponents: { My***ponent }, data() { return { listItems: [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' } ] }; } }; </script> - 组件注册:全局注册(
-
响应式原理:
- 数据绑定:单向数据流和双向绑定
- 虚拟DOM:Vue的渲染机制和diff算法
- 响应式更新:依赖收集与派发更新机制
- Vue 2与Vue 3响应式系统的区别
代码示例:Vue 2 vs Vue 3 响应式实现对比
// Vue 2 响应式实现原理简化示例 function observe(data) { if (!data || typeof data !== 'object') { return; } // 遍历数据对象的所有属性 Object.keys(data).forEach(key => { let internalValue = data[key]; // 创建依赖收集器 const dep = new Dep(); // 使用Object.defineProperty劫持属性 Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { // 收集依赖(Watcher) if (Dep.target) { dep.addSub(Dep.target); } return internalValue; }, set(newValue) { if (newValue === internalValue) return; internalValue = newValue; // 通知所有依赖更新 dep.notify(); } }); // 递归观察嵌套对象 observe(internalValue); }); } // Vue 3 响应式实现原理简化示例 function reactive(target) { return new Proxy(target, { get(target, key, receiver) { const result = Reflect.get(target, key, receiver); // 收集依赖 track(target, key); // 递归代理嵌套对象 if (typeof result === 'object' && result !== null) { return reactive(result); } return result; }, set(target, key, value, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, value, receiver); // 只有值发生变化时才触发更新 if (oldValue !== value) { // 触发更新 trigger(target, key); } return result; }, deleteProperty(target, key) { const hasKey = key in target; const result = Reflect.deleteProperty(target, key); if (hasKey) { // 触发更新 trigger(target, key, 'delete'); } return result; } }); } // Vue 3 中的响应式API使用示例 const { ref, reactive, ***puted, watch, watchEffect } = Vue; const count = ref(0); console.log(count.value); // 0 count.value++; // 触发更新 const user = reactive({ name: 'John', age: 30 }); // 计算属性 const fullName = ***puted(() => { return `${ user.name} Doe`; }); // 监听单个值 watch(count, (newValue, oldValue) => { console.log(`Count changed from ${ oldValue} to ${ newValue}`); }); // 自动追踪依赖的监听 watchEffect(() => { console.log(`User name is: ${ user.name}`); });Vue 2 与 Vue 3 响应式系统的主要区别
特性 Vue 2 Vue 3 核心实现 Object.defineProperty Proxy 数组响应式 重写数组方法 原生支持 检测属性添加/删除 不支持,需使用Vue.set/Vue.delete 原生支持 检测数组索引/长度变化 不支持 部分支持 嵌套对象响应式 递归遍历到底 按需递归(懒代理) TypeScript支持 有限 原生支持 性能 较大对象初始化较慢 更高效,特别是大型对象 响应式API 无专用API,通过Vue实例实现 提供ref、reactive等组合式API
2. Vue 组件通信
-
父组件向子组件传递数据:
- Props传递:静态props和动态props(
v-bind:prop) - Props类型校验:String、Number、Boolean、Array、Object、Function等
- Props默认值和必填项设置
- 单向数据流原则:子组件不应该修改父组件传递的props
代码示例:父组件向子组件传递数据
<!-- Parent***ponent.vue --> <template> <div class="parent"> <h2>Parent ***ponent</h2> <!-- 静态props --> <child-***ponent title="Static Title" /> <!-- 动态props --> <child-***ponent :message="parentMessage" :user-data="userInfo" :is-active="is***ponentActive" /> <button @click="updateMessage">Update Message</button> </div> </template> <script> import Child***ponent from './Child***ponent.vue'; export default { ***ponents: { Child***ponent }, data() { return { parentMessage: 'Hello from parent', userInfo: { name: 'John', age: 30 }, is***ponentActive: true }; }, methods: { updateMessage() { this.parentMessage = 'Updated message from parent'; this.is***ponentActive = !this.is***ponentActive; } } }; </script> <!-- Child***ponent.vue --> <template> <div class="child"> <h3>{ { title || 'Default Title' }}</h3> <p v-if="message">{ { message }}</p> <p v-if="userData">User: { { userData.name }}, Age: { { userData.age }}</p> <p>Status: { { isActive ? 'Active' : 'Inactive' }}</p> </div> </template> <script> export default { name: 'Child***ponent', props: { // 基础类型检查 title: String, // 带默认值的props message: { type: String, default: 'Default message' }, // 复杂类型的props userData: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: () => ({ }), required: false }, // 布尔类型props isActive: { type: Boolean, default: false }, // 自定义验证函数 items: { type: Array, validator: (value) => { // 这个值必须匹配下列字符串中的一个 return value.length > 0; }, default: () => [] } } }; </script> - Props传递:静态props和动态props(
-
子组件向父组件传递数据:
- 自定义事件:子组件通过
$emit('eventName', data)触发事件 - 父组件通过
v-on:eventName="handleEvent"监听事件 - 事件修饰符在组件上的应用
- .native修饰符的使用与注意事项
代码示例:子组件向父组件传递数据
<!-- Parent***ponent.vue --> <template> <div class="parent"> <h2>Parent ***ponent</h2> <p>Child message: { { childMessage }}</p> <p>Counter: { { counter }}</p> <!-- 监听子组件事件 --> <child-***ponent @message-sent="handleMessage" @increment="handleIncrement" @decrement="handleDecrement" /> <!-- 带参数的事件处理 --> <child-***ponent @update-count="(value) => { counter += value }" /> </div> </template> <script> import Child***ponent from './Child***ponent.vue'; export default { ***ponents: { Child***ponent }, data() { return { childMessage: '', counter: 0 }; }, methods: { handleMessage(message) { this.childMessage = message; }, handleIncrement() { this.counter++; }, handleDecrement() { this.counter--; } } }; </script> <!-- Child***ponent.vue --> <template> <div class="child"> <h3>Child ***ponent</h3> <!-- 基本事件触发 --> <button @click="sendMessage">Send Message</button> <!-- 带参数的事件触发 --> <button @click="incrementParentCounter">+1</button> <button @click="decrementParentCounter">-1</button> <!-- 带动态参数的事件触发 --> <button @click="updateCountBy(5)">+5 to Parent</button> <button @click="updateCountBy(-3)">-3 to Parent</button> <!-- 使用表单输入触发事件 --> <input type="text" v-model="inputValue" @input="onInputChange" placeholder="Type something..." > </div> </template> <script> export default { name: 'Child***ponent', data() { return { inputValue: '' }; }, methods: { sendMessage() { // 触发自定义事件,可携带数据 this.$emit('message-sent', 'Hello from child ***ponent!'); }, incrementParentCounter() { this.$emit('increment'); }, decrementParentCounter() { this.$emit('decrement'); }, updateCountBy(value) { this.$emit('update-count', value); }, onInputChange() { this.$emit('input-change', this.inputValue); } }, // Vue 3中可以显式声明组件发出的事件 emits: ['message-sent', 'increment', 'decrement', 'update-count', 'input-change'] }; </script> - 自定义事件:子组件通过
-
兄弟组件间通信:
- EventBus:创建全局事件总线
Vue.prototype.$bus = new Vue() - 通过 e m i t 发送事件, emit发送事件, emit发送事件,on监听事件
- 使用Vuex/Pinia进行状态共享
- 兄弟组件通信的最佳实践
代码示例:兄弟组件间通信
1. 使用EventBus进行兄弟组件通信
// 在main.js中初始化EventBus import Vue from 'vue'; // 方法1: 全局注册EventBus Vue.prototype.$bus = new Vue(); // 方法2: 创建独立的EventBus模块 // event-bus.js export const bus = new Vue(); // ***ponentA.vue (发送方组件) <template> <div class="***ponent-a"> <h3>***ponent A</h3> <button @click="sendMessage">Send Message to B</button> </div> </template> <script> // 如果使用独立的EventBus模块 import { bus } from './event-bus'; export default { methods: { sendMessage() { // 方法1: 使用全局EventBus this.$bus.$emit('message-from-a', 'Hello from ***ponent A!'); // 方法2: 使用独立EventBus模块 // bus.$emit('message-from-a', 'Hello from ***ponent A!'); } } }; </script> // ***ponentB.vue (接收方组件) <template> <div class="***ponent-b"> <h3>***ponent B</h3> <p>Message from A: { { messageFromA }}</p> </div> </template> <script> // 如果使用独立的EventBus模块 import { bus } from './event-bus'; export default { data() { return { messageFromA: '' }; }, mounted() { // 方法1: 使用全局EventBus this.$bus.$on('message-from-a', (message) => { this.messageFromA = message; }); // 方法2: 使用独立EventBus模块 // bus.$on('message-from-a', (message) => { // this.messageFromA = message; // }); }, beforeDestroy() { // 组件销毁前移除事件监听,避免内存泄漏 this.$bus.$off('message-from-a'); // 或 bus.$off('message-from-a'); } }; </script>2. 使用Vuex进行组件通信
// store.js import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { sharedData: '', counter: 0 }, mutations: { setSharedData(state, data) { state.sharedData = data; }, incrementCounter(state) { state.counter++; }, decrementCounter(state) { state.counter--; } }, actions: { setSharedDataAction({ ***mit }, data) { // 可以在这里处理异步操作 ***mit('setSharedData', data); } }, getters: { getSharedData: state => state.sharedData, getCounter: state => state.counter } }); // 在main.js中注册store import store from './store'; new Vue({ store, // ...其他配置 }).$mount('#app'); // Sender***ponent.vue <template> <div class="sender"> <h3>Sender ***ponent</h3> <input v-model="message" placeholder="Type a message"> <button @click="updateSharedData">Update Shared Data</button> <button @click="increment">Increment Counter</button> </div> </template> <script> export default { data() { return { message: '' }; }, methods: { updateSharedData() { // 直接提交mutation // this.$store.***mit('setSharedData', this.message); // 或通过dispatch action(适合处理异步操作) this.$store.dispatch('setSharedDataAction', this.message); }, increment() { this.$store.***mit('incrementCounter'); } } }; </script> // Receiver***ponent.vue <template> <div class="receiver"> <h3>Receiver ***ponent</h3> <p>Shared Data: { { sharedData }}</p> <p>Counter: { { counter }}</p> <button @click="decrement">Decrement Counter</button> </div> </template> <script> export default { ***puted: { // 方法1: 直接从store获取 // sharedData() { // return this.$store.state.sharedData; // }, // counter() { // return this.$store.state.counter; // } // 方法2: 使用mapState辅助函数 // ...Vuex.mapState(['sharedData', 'counter']) // 方法3: 使用mapGetters辅助函数 ...Vuex.mapGetters(['getSharedData', 'getCounter']), sharedData() { return this.getSharedData; }, counter() { return this.getCounter; } }, methods: - EventBus:创建全局事件总线