Vue 概念 Vue.js 是一款用于构建用户界面的渐进式JavaScript框架。 它允许开发者创建高效的、 可复用的以及模块化的前端组件, 是目前最流行的前端框架之一。 Vue的核心库专注于视图层, 易于学习且集成到现有项目中, 同时也能为复杂的单页应用程序( SPA) 提供强大的功能支持。
Vue.js适合用来构建交互式的Web界面, 无论是简单的嵌入式小部件还是复杂的大型单页应用程序, Vue都能提供相应的解决方案。 由于其灵活性和易用性, Vue在全球范围内得到了广泛的应用和认可。
主要特点
渐进式框架: Vue 是一个渐进式的框架, 意味着你可以根据项目的需要逐步采用它的不同部分。 你可以在一个已有的项目中仅使用 Vue 的视图层, 也可以构建复杂的单页应用程序( SPA) 。
双向数据绑定: Vue 实现了声明式的双向数据绑定机制, 可以自动追踪组件依赖的数据, 在数据变化时自动更新DOM, 简化了用户输入处理和表单验证等操作。
组件化: Vue 强调UI界面的组件化开发, 使得代码更加模块化、 可重用。 每个组件都有自己的视图和数据逻辑, 易于维护和测试。
轻量级: Vue本身体积较小, 性能高效, 非常适合移动设备的应用开发。
指令系统: Vue提供了诸如v-if, v-for, v-bind, v-model等丰富的指令来操作DOM, 让开发变得更加简单直观。
虚拟DOM: 使用虚拟 DOM 技术来提高性能, 通过对比前后两个虚拟 DOM 树的差异, 只对实际需要更新的部分进行最小化的DOM操作, 提升应用的响应速度。
工具链支持: Vue拥有完整的工具链支持, 如Vue CLI( 命令行工具) , Vuex( 状态管理模式) , Vue Router( 路由管理) 等, 帮助开发者更好地进行项目开发。
主要优势
易学易用: Vue 的API设计简洁明了, 文档详尽, 对于初学者来说非常友好, 容易上手。
灵活性: Vue 不强制要求特定的项目结构或工具链, 可以根据项目的具体需求灵活选择合适的架构和技术栈。
强大的生态系统: Vue 拥有一个丰富的插件和工具生态系统, 包括 Vue Router( 路由管理) 、 Vuex( 状态管理) 、 Vue CLI( 脚手架工具) 等, 帮助开发者更高效地构建复杂应用。
社区支持: 随着 Vue 的流行, 其社区也在迅速增长, 提供了大量的学习资源、 教程和支持。
良好的兼容性: Vue 可以很好地与其他库或现有项目集成, 适应不同的项目需求。
应用场景
单页应用程序( SPA) : Vue.js 是构建单页应用程序的理想选择。 通过 Vue Router 可以轻松实现页面的导航和路由管理, 使得用户在不同视图间切换时无需重新加载整个页面, 提供更流畅的用户体验。
渐进式Web应用( PWA) : ue.js 能够很好地支持 PWA 开发。 借助于服务工作者和缓存API等技术, 可以创建出具有离线访问能力、 快速加载速度以及类似原生应用体验的应用程序。
动态内容更新: Vue.js 提供了高效的双向数据绑定机制, 能够实时响应数据的变化并自动更新UI。 这使得它非常适合用于需要频繁更新内容的应用场景, 如社交网络平台、 实时聊天应用等。
移动应用开发: 通过与框架如 Ionic 或 Quasar 结合, Vue.js 可以用来开发跨平台的移动应用。 这些框架利用 Vue.js 的组件化模型来构建界面, 并生成可以在多个平台上运行的代码。
桌面应用开发: Vue.js 还可用于开发桌面应用程序, 例如使用 Electron 框架。 开发者可以使用熟悉的前端技术栈来创建同时适用于 Windows、 Mac 和 Linux 的桌面应用。
企业级应用: 在企业环境中, Vue.js 常被用来构建大型复杂的应用系统。 结合 Vuex 进行状态管理, 可以帮助团队更好地组织和管理代码, 提高开发效率和维护性。
电商网站与管理系统: Vue.js 可用于构建电商网站、 后台管理系统等, 通过组件化开发模式加快开发速度, 同时也便于后续的功能扩展和维护。
插件和库的开发: Vue.js 社区非常活跃, 很多开发者使用 Vue 来创建可复用的插件或库, 为其他开发者提供便捷的功能和服务。
Vue 基础 创建实例 创建 Vue 实例是使用 Vue 的第一步。 每一个 Vue 应用都是从一个 Vue 实例 开始的。 你可以把它看作是一个 Vue 应用的“ 根” , 它控制着页面中的一部分 DOM 元素, 并管理其内部的数据、 方法和生命周期。
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js" > </script > </head > <body > <div id ="app" > {{ message }} </div > <script > new Vue ({ el : '#app' , data : { message : 'Hello, Vue!' } }) </script > </body > </html >
插值表达式 插值表达式是最基础、 最常见的数据绑定方式之一。 它主要用于将数据动态地插入到 HTML 模板中, 实现响应式更新。 Vue 的插值表达式使用双大括号 {{ }}
语法。
基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <div id ="app" > <h1 > {{ title }}</h1 > <p > 欢迎你, {{ user.name }}! </p > <p > 当前时间: {{ new Date() }}</p > </div > <script > new Vue ({ el : '#app' , data : { title : 'Vue 插值表达式示例' , user : { name : '张三' } } }) </script >
主要特性
✅响应式更新: 当数据变化时, 插值内容会自动更新
✅支持任意 JS 表达式: 可以写简单的表达式, 如: 加、 减、 乘、 除、 三目运算符等等...
⚠️仅限文本内容: 插值表达式不能渲染 HTML 内容( 防止 XSS) , 如果需要渲染 HTML, 请使用 v-html 指令
⚠️不支持控制结构: 如 if、 for 等复杂逻辑应在模板外处理或使用计算属性
注意事项 1 2 3 4 5 6 7 8 9 10 11 1. 避免复杂的逻辑 虽然可以在插值中使用表达式, 但建议不要嵌入过于复杂的逻辑, 推荐使用 计算属性( computed) 来代替。 computed: { formattedTime() { return formatDate(this.currentTime); // 自定义格式化函数 } } <p>格式化后的时间: {{ formattedTime }}</p> 2. 不支持 HTML 渲染, 输出的是纯字符串, 不会渲染为加粗效果。 <p>{{ "<strong>加粗文字</strong>" }}</p>
响应式特性 响应式特性是 VUE 核心功能之一, 它使得数据模型与视图之间能够自动同步。 这意味着当数据发生变化时, Vue 会自动更新相关的视图部分, 反之亦然。 这种机制大大简化了前端开发中的状态管理问题。
响应式基础 在 Vue.js 中, 当你创建一个 Vue 实例并定义 data 选项时, Vue 会将 data 对象的所有属性转换为响应式属性。 这意味着: 当这些属性被读取时, Vue 能够追踪到依赖关系; 当这些属性被修改时, Vue 可以通知所有依赖该属性的地方进行更新。
1 2 3 4 5 6 7 8 new Vue ({ el : '#app' , data : { message : 'Hello Vue!' } });
如何实现响应式 Vue 3 中采用了 Proxy 对象来实现响应式系统, 取代了 Vue 2 中使用的 Object.defineProperty 方法。 这使得 Vue 3 在处理响应式数据方面更加灵活和强大。
Proxy: 通过代理对象拦截对目标对象的操作( 如读取、 写入等) , 从而实现在不改变原有对象的基础上对其进行扩展或限制操作的能力。
1 2 3 4 5 6 7 8 import { reactive } from 'vue' ;const state = reactive ({ count : 0 }); state.count ++;
深层次响应式 Vue 的响应式不仅限于顶层属性, 它也支持深层次的数据结构( 如对象和数组) 。 这意味着你可以在嵌套的对象或数组中进行修改, 并且这些修改也会触发相应的视图更新。
1 2 3 4 5 6 7 8 9 10 import { reactive } from 'vue' ;const state = reactive ({ nested : { count : 0 } }); state.nested .count ++;
注意事项
⚠️新增和删除属性: 在 Vue 2 中, 直接向响应式对象添加或删除属性不会触发视图更新。 Vue 3 使用 Proxy 后解决了这个问题, 但如果你使用的是 Vue 2, 则需要使用 Vue.set 或 Vue.delete 来确保响应性。
⚠️循环引用: 对于复杂的嵌套对象结构, 可能会出现循环引用的情况, 这时需要注意避免无限递归的问题。
⚠️异步更新队列: Vue 实现了一个异步更新队列, 用于批量处理 DOM 更新, 提高性能。 因此, 即使多次更改同一个属性, DOM 也可能只更新一次。
开发者工具 在 Vue.js 开发中, 开发者工具是一个非常强大的辅助工具, Vue Devtools 是由 Vue 官方维护的一款浏览器扩展插件, 目前支持 Chrome 和 Firefox 浏览器。 它与 Vue 2.x 和 Vue 3.x 都兼容。 它可以帮助你:
查看组件树结构
检查组件状态( data、 props、 computed 等)
调试 Vuex 状态管理
监控事件和生命周期
时间旅行调试( Time Travel Debugging)
下载、 安装、 使用
下载地址: https://chrome.zzzmh.cn/search/Vue
, 选择 Vue.js Devtools
点击下载。
安装: 解压下载好的安装包 → 打开谷歌浏览器 → 安装扩展程序 → 把安装包中后缀名为 crx
的插件拖入浏览器
配置: 安装完成后 → 点击详情 → 允许访问文件网址
使用: 安装成功后, 打开任意 Vue 项目页面, 按 F12
或 右键点击检查, 打开浏览器开发者工具, 在顶部标签栏中会多出一个 “Vue” 标签页。
Vue 配置 创建 Vue 应用实例时需要传入选项对象。 这个对象包含了应用的各种配置和生命周期钩子, 用于定义组件的行为、 数据、 方法等。
1 2 3 4 5 6 7 8 9 10 11 12 13 配置对象可以包含很多选项, 比如: el: 指定挂载点( 即该 Vue 实例控制的 DOM 容器) data: 数据对象, 用于响应式绑定 methods: 定义可以在模板中调用的方法 computed: 计算属性, 基于已有数据进行逻辑处理后返回新值, 具有缓存机制 watch: 监听数据变化 template: 自定义模板, 提供了 template, 则会替换掉挂载点的内容 生命周期钩子函数 created(): 实例已经创建完成, 但还未挂载到 DOM 上 mounted(): DOM 已经渲染完成 updated(): 数据更新导致视图更新后调用 destroyed(): 实例销毁时调用 等等...
el
el 选项用于指定一个页面上已存在的 DOM 元素, Vue 实例将管理这个元素及其子元素。 它通常用于将 Vue 应用挂载到网页的特定部分。
基本概念
作用: el 是 Vue 实例的一个选项, 用来指定控制的 DOM 元素。 Vue 会把这个元素作为应用的根容器, 并替换其内容。 值类型: 可以是一个 CSS 选择器字符串或者直接是一个实际的 DOM 元素对象。
使用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 <div id ="app" > </div > <script > new Vue ({ el : '#app' }); var element = document .getElementById ('app' );new Vue ({ el : element }); </script >
data
用于定义组件内部的状态( 即响应式数据) 。 Vue 会追踪 data 对象中所有属性的依赖, 并在这些属性发生变化时自动更新视图。
基本概念
作用: data 选项是一个函数( 尤其在组件中) , 它返回一个对象, 这个对象包含了组件的所有响应式数据。 响应性: Vue 自动将 data 函数返回的对象中的所有属性转化为响应式。 这意味着当这些属性的值发生改变时, Vue 能够检测到这些变化, 并相应地更新 DOM。
使用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <div id ="app" > <h1 > {{ title }}</h1 > <p > 欢迎你, {{ user.name }}! </p > <p > 当前时间: {{ new Date() }}</p > </div > <script > new Vue ({ el : '#app' , data : { title : 'Vue 插值表达式示例' , user : { name : '张三' } } }) </script >
methods
methods 用于定义组件内的方法。 这些方法可以包含事件处理逻辑、 计算属性之外的复杂操作以及其他需要执行的函数。 Vue 会自动将 methods 中的方法绑定到 Vue 实例上, 使得它们可以直接通过实例来访问, 并且在模板中也可以直接调用。
基本概念
作用: methods 用于定义可以在 Vue 组件内使用的函数。 这些函数可以响应用户输入( 如点击按钮) 、 执行业务逻辑或与组件的状态交互。 特点: 方法中的 this 关键字指向当前 Vue 实例, 因此可以直接访问和修改组件的数据 (data) 和其他方法。 Vue 自动将 methods 绑定到 Vue 实例, 确保正确的作用域。
使用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <div id ="app" > <p > {{ message }}</p > <button @click ="greet" > Greet</button > <button @click ="increment" > Clicked {{ count }} times.</button > </div > <script > new Vue ({ el : '#app' , data : { message : 'Hello Vue!' , count : 0 }, methods : { greet ( ) { alert (this .message ); }, increment ( ) { this .count ++; } } }) </script >
computed
computed( 计算属性) 是一种非常强大且常用的特性, 用于声明依赖响应式数据的复杂逻辑或派生值。 它本质上是一个带有缓存机制的函数, 当其依赖的数据未发生变化时, 计算属性会直接返回上一次的缓存结果, 而不会重新执行函数。
基本概念 computed 是一个对象, 包含若干个函数。 每个函数的返回值会被当作组件的一个“ 虚拟属性” 使用。 这些属性是响应式的, 并且具有缓存机制。
使用方法 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 <div id ="app" > <p > 全名: {{ fullName }}</p > </div > <script > new Vue ({ el : '#app' , data : { firstName : '张' , lastName : '三' }, computed : { fullName ( ) { return this .firstName + ' ' + this .lastName ; }, fullName1 : { get ( ) { return this .firstName + ' ' + this .lastName ; }, set (newValue ) { const names = newValue.split (' ' ); this .firstName = names[0 ]; this .lastName = names[1 ] || '' ; } } } }) </script >
watch
watch 选项用于监听特定数据的变化, 并在这些数据发生变化时执行相应的回调函数。 它非常适合处理需要对数据变化做出响应的场景, 特别是那些涉及异步操作或开销较大的操作。
基本概念
作用: 监听 Vue 实例中的数据变化( 包括 data, computed 属性等) , 并在侦听到变化时执行自定义逻辑。 适用场景: 当你需要在某个数据改变后执行一些副作用操作( 如网络请求、 DOM 操作等) 时非常有用。
使用方法 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 <script > new Vue ({ el : '#app' , data : { question : '' , answer : 'Questions usually contain a question mark. ;-)' }, methods : { getAnswer ( ) { setTimeout (() => { this .answer = 'Got an answer.' ; }, 1000 ); } }, watch : { question (newQuestion, oldQuestion ) { console .log (`问题从 "${oldQuestion} " 变为 "${newQuestion} "` ); this .answer = 'Waiting for you to stop typing...' ; this .getAnswer (); } } }) </script >
立即触发 有时候你可能希望在监听开始时立即执行一次回调函数, 可以通过设置 immediate: true 来实现。
1 2 3 4 5 6 7 8 9 10 new Vue ({ watch : { question : { handler (newQuestion, oldQuestion ) { console .log ("立即触发" ); }, immediate : true } } })
深度监听 如果要监听的是对象内部的变化, 而不是引用本身的改变, 可以使用深度监听。
1 2 3 4 5 6 7 8 9 10 new Vue ({ watch : { someObject : { handler (newVal, oldVal ) { console .log ("深度监听" ); }, deep : true } } })
生命周期 Vue 组件的生命周期钩子允许你在组件的不同阶段执行自定义逻辑。
钩子函数 Vue 3 中, 生命周期钩子与 Vue 2 基本相同, 但有些钩子名称略有变化。
钩子( Vue 2) 钩子( Vue 3) 描述
beforeCreate
onBeforeMount
(部分功能)
实例初始化之后, 数据观测(data observer) 和 event/watcher 事件配置之前被调用。 在 Vue 3 Composition API 中, 这个阶段通常不需要特别处理。
created
onMounted
(部分功能)
实例创建完成后被调用。 此时实例已完成以下的配置: 数据观测(data observer), 属性和方法的运算, watch/event 事件回调。 然而, 挂载阶段还没开始, $el 属性目前不可见。
beforeMount
onBeforeMount
在挂载开始之前被调用: 相关的 render 函数首次被调用。
mounted
onMounted
实例已挂载后调用, 这时 el 被新创建的 vm.el替换。 如果根实例挂载到了一个文档内的元素上, 当mounted被调用时vm.el替换。 如果根实例挂载到了一个文档内的元素上, 当mounted被调用时vm.el 也在文档内。
beforeUpdate
onBeforeUpdate
数据更新时调用, 发生在虚拟 DOM 打补丁之前。 这里适合在更新之前访问现有的 DOM, 比如手动移除已添加的事件监听器。
updated
onUpdated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁, 在这之后会调用该钩子。 当这个钩子被调用时, 组件 DOM 已经更新, 所以你现在可以执行依赖于 DOM 的操作。
beforeDestroy
onBeforeUnmount
由于数据更改导致的虚拟 DOM 重新渲染和打补丁, 在这之后会调用该钩子。 当这个钩子被调用时, 组件 DOM 已经更新, 所以你现在可以执行依赖于 DOM 的操作。
destroyed
onUnmounted
Vue 实例销毁后调用。 调用后, 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 <div id ="app" > <p > {{ message }}</p > </div > <script > new Vue ({ el : '#app' , data : { message : 'Hello Vue!' }, beforeCreate ( ) { console .log ('实例初始化之后调用' ); }, created ( ) { console .log ('实例创建完成之后调用' ); }, beforeMount ( ) { console .log ('实例挂载之前调用' ); }, mounted ( ) { console .log ('实例挂载之后调用' ); }, beforeUpdate ( ) { console .log ('数据发生改变后, DOM 被更新之前被调用' ); }, updated ( ) { console .log ('更新完毕之后调用' ); }, beforeDestroy ( ) { console .log ('实例销毁之前调用' ); }, destroyed ( ) { console .log ('实例销毁之后调用' ); } }) </script >
Vue 指令 指令系统是 Vue.js 核心特性之一, 通过这些指令可以非常方便地操作 DOM、 绑定数据、 处理用户输入等。 指令通常以 v-
开头, 它们提供了丰富的功能来增强 Vue 应用的能力。
基础渲染指令 v-text
1 2 3 4 <span v-text ="message" > </span > <span > {{ message }}</span >
v-html
1 2 3 4 5 6 7 8 <p v-html ="rawHtml" > </p > data: { rawHtml: '<strong > 加粗文字</strong > ' } <p > <strong > 加粗文字</strong > </p >
v-once
1 2 3 4 5 6 <span v-once > {{ message }}</span > <div v-once > <h1 > {{ title }}</h1 > <p > {{ description }}</p > </div >
属性绑定指令 v-bind
用于动态绑定 HTML 属性值, 如 href、 src、 class、 style 等
⭐⭐ 绑定属性 1 2 3 4 <a v-bind:href ="url" > 链接</a > <a :href ="url" > 链接</a >
⭐⭐ 绑定类名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div :class ="{ active: isActive, 'text-danger': false }" > 动态类名 </div > <div :class ="[activeClass, errorClass]" > 动态类名 </div > <div :class ="[{ active: isActive }, errorClass]" > </div > data: { isActive: true, activeClass: 'active', errorClass: 'text-danger' }
⭐⭐ 绑定样式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div :style ="{ color: activeColor, fontSize: fontSize + 'px' }" > 动态内联样式 </div > <div :style ="{ color: 'blue', fontSize: '14px', marginTop: '20px' }" > </div > <div :style ="{ color: 'blue', 'font-size': '14px', 'margin-top': '20px' }" > </div > <div :style ="[baseStyles, overridingStyles]" > </div > data: { baseStyles: { color: 'black', fontSize: '16px' }, overridingStyles: { color: 'red' } }
⭐⭐ 高级技巧 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div :class ="dynamicClasses" > </div > computed: { dynamicClasses() { return { active: this.isActive, disabled: !this.isAvailable } } } <template > <div :class ="$style.myClass" > 模块化样式</div > </template > <style module > .myClass { color : blue; } </style >
v-model
实现表单输入和应用状态之间的双向数据绑定
⭐⭐ 文本输入框 1 2 3 <input v-model ="message" placeholder ="输入一些文本" > <p > 消息是: {{ message }}</p >
⭐⭐ 多行文本 1 2 3 <textarea v-model ="message" > </textarea > <p > 多行消息是: {{ message }}</p >
⭐⭐ 单选按钮 1 2 3 4 <input type ="radio" id ="one" value ="One" v-model ="picked" > <input type ="radio" id ="two" value ="Two" v-model ="picked" > <span > Picked: {{ picked }}</span >
⭐⭐ 复选框 1 2 3 4 5 6 7 8 <input type ="checkbox" id ="checkbox" v-model ="checked" > <label for ="checkbox" > {{ checked }}</label > <input type ="checkbox" id ="jack" value ="Jack" v-model ="checkedNames" > <input type ="checkbox" id ="john" value ="John" v-model ="checkedNames" > <span > Checked names: {{ checkedNames }}</span >
⭐⭐ 下拉框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select v-model ="selected" > <option value ="A" > A</option > <option value ="B" > B</option > <option value ="C" > C</option > </select > <span > Selected: {{ selected }}</span > <select v-model ="selected" multiple > <option value ="A" > A</option > <option value ="B" > B</option > <option value ="C" > C</option > </select > <span > Selected: {{ selected }}</span >
流程控制指令 v-if
1 2 <p v-if ="seen" > 现在你看到我了</p >
v-else
1 2 3 <p v-if ="seen" > 现在你看到我了</p > <p v-else > 现在你看不到我</p >
v-else-if
1 2 3 4 <div v-if ="type === 'A'" > 优秀</div > <div v-else-if ="type === 'B'" > 良好</div > <div v-else > 一般</div >
v-for
1 2 3 4 5 6 7 8 9 10 <ul > <li v-for ="(item, index) in items" :key ="index" > {{ item }}</li > </ul > <ul > <li v-for ="(value, key, index) in object" > {{ key }}: {{ value }}</li > </ul >
事件处理指令 v-on
1 2 3 4 <button v-on:click ="js函数名" > 点击我</button > <button @click ="js函数名" > 点击我</button >
其他常用指令 v-show
1 2 3 <p v-show ="isVisible" > 现在你看到我了</p >
v-pre
1 2 3 <p v-pre > {{ 这个不会编译 }}</p >
v-cloak
1 2 3 4 5 6 7 8 9 10 11 <style > [v-cloak] { display : none; } </style > <div v-cloak > {{ message }} </div >
自定义指令 除了内置指令外, Vue 还允许开发者自定义指令。
钩子函数 ⭐⭐ 一个自定义指令对象可以包含以下钩子函数( 可选) :
钩子函数 触发时机 函数说明
beforeMount
指令第一次绑定到元素时调用( 只执行一次) 初始化前
mounted
指令绑定的元素被插入到 DOM 时调用 初始化完成
beforeUpdate
在元素更新之前调用( 每次数据变更都会触发) 更新前
updated
在元素更新完成后调用 更新后
beforeUnmount
指令即将从元素上解绑前调用 销毁前
unmounted
指令从元素上解绑后调用 销毁完成
⭐⭐ 每个钩子函数可以接收以下参数( 可选) :
可选参数 参数说明
el
绑定的 DOM 元素
binding
包含指令相关信息的对象( name, value, oldValue, arg, modifiers)
vnode
虚拟节点( VNode)
prevNode
上一个 VNode( 仅在 beforeUpdate 和 updated 中可用)
⭐⭐ binding 对象属性 1 2 3 4 5 6 7 8 { name : 'focus' , value : 'admin' , oldValue : undefined , arg : 'role' , modifiers : { bold : true }, instance : component }
注册指令 ⭐⭐ 全局注册 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createApp } from 'vue' import App from './App.vue' const app = createApp (App )app.directive ('focus' , { mounted (el ) { el.focus () } }) app.mount ('#app' )
⭐⭐ 局部注册 1 2 3 4 5 6 7 8 9 export default { directives : { focus : { mounted (el ) { el.focus () } } } }
使用示例 ⭐⭐ 示例1: 自动聚焦( Focus) 1 2 3 4 5 6 7 app.directive ('focus' , { mounted (el ) { el.focus () } }) <input v-focus />
⭐⭐ 示例2: 权限高亮( 根据用户角色动态设置背景色) 1 2 3 4 5 6 7 8 9 10 app.directive ('highlight' , { mounted (el, binding ) { const roles = ['admin' , 'editor' ] if (roles.includes (binding.value )) { el.style .backgroundColor = '#f0e68c' } } }) <div v-highlight="'admin'" >管理员内容</div>
⭐⭐ 示例3: 节流滚动监听( 防抖/节流优化性能) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 app.directive ('throttle-scroll' , { mounted (el, binding ) { let ticking = false window .addEventListener ('scroll' , () => { if (!ticking) { requestAnimationFrame (() => { binding.value (window .scrollY ) ticking = true }) } setTimeout (() => { ticking = false }, 200 ) }) } }) <div v-throttle-scroll="(y) => scrollPosition = y" ></div>
⭐⭐ 示例4: 拖动指令( 实现简单拖拽功能) 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 app.directive ('draggable' , { mounted (el ) { let offsetX = 0 , offsetY = 0 el.style .position = 'absolute' el.style .cursor = 'move' el.addEventListener ('mousedown' , (e ) => { offsetX = e.clientX - el.offsetLeft offsetY = e.clientY - el.offsetTop function onMouseMove (e ) { el.style .left = `${e.clientX - offsetX} px` el.style .top = `${e.clientY - offsetY} px` } function onMouseUp ( ) { document .removeEventListener ('mousemove' , onMouseMove) document .removeEventListener ('mouseup' , onMouseUp) } document .addEventListener ('mousemove' , onMouseMove) document .addEventListener ('mouseup' , onMouseUp) }) } }) <div v-draggable style="width: 100px; height: 100px; background: lightblue;" >拖我</div>
⭐⭐ 示例5: 权限控制显示( 根据角色决定是否渲染元素) 1 2 3 4 5 6 7 8 9 app.directive ('permission' , { beforeMount (el, binding ) { const userRoles = ['admin' ] if (!userRoles.includes (binding.value )) { el.parentNode .removeChild (el) } } }) <button v-permission="'admin'" >删除</button>
指令修饰符 指令修饰符是对指令功能的增强, 通过在指令后加上特定的后缀来改变其行为。
事件修饰符 v-on 指令用于监听 DOM 事件, 并在触发时执行 JavaScript 代码。 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 <div @click ="doSomething" > <button @click.stop ="doSomethingElse" > 点击我</button > </div > <div @click.capture ="doSomething" > <button @click ="doSomethingElse" > 点击我</button > </div > <div @click.self ="doSomething" > <button @click ="doSomethingElse" > 点击我</button > </div > <button @click.once ="doSomething" > 点击我</button > <form @submit.prevent ="onSubmit" > </form > <div @touchmove.passive ="onTouchMove" > ...</div >
表单修饰符 除了 v-on 指令修饰符外, Vue 还为 v-model 提供了一些修饰符, 用于处理常见的表单输入情况。
1 2 3 4 5 6 7 8 9 <input v-model.lazy ="message" /> <input v-model.number ="age" type ="number" > <input v-model.trim ="message" />
按键修饰符 当你监听键盘事件时, 可以使用按键修饰符来监听特定键的按下。
1 2 3 4 5 6 7 8 9 10 11 <input @keyup.enter ="submit" > <input @keyup.13 ="submit" > Vue.config.keyCodes.f1 = 112 <input @keyup.f1 ="doSomething" >
系统修饰键 可以使用以下系统修饰键来触发鼠标或键盘事件监听器。
1 2 3 4 <div @click.ctrl ="onClick" > 需要同时按Ctrl键点击</div >
Vue 组件 Vue.js 其核心特性之一是组件化开发。 组件允许我们将 UI 拆分为独立的、 可复用的部分, 使代码更易维护和扩展。
创建项目 1 2 3 4 5 6 7 8 npm install -g @vue/cli vue create projectName npm run serve
组成部分 ⭐⭐ 模板 (Template) 模板定义了组件的结构和布局。 它使用 HTML 标记来描述组件的 UI, 并且可以包含 Vue 的指令( 如 v-if, v-for 等) 和插值语法 () 来实现动态内容。
1 2 3 4 5 6 <template > <div class ="example" > <h1 > {{ title }}</h1 > <p > {{ content }}</p > </div > </template >
⭐⭐ 脚本 (Script) 脚本部分包含了组件的逻辑, 包括数据、 方法、 生命周期钩子等。 它是用 JavaScript 编写的, 负责处理组件的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <script > export default { data ( ) { return { title : '我的标题' , content : '这是一个例子内容' } }, methods : { updateContent (newContent ) { this .content = newContent; } }, computed : { fullText ( ) { return `${this .title} - ${this .content} ` ; } }, mounted ( ) { console .log ('组件已挂载' ); } } </script >
⭐⭐ 样式 (Style) 样式部分允许你为组件添加 CSS 样式。 Vue 支持单文件组件中的样式定义, 你可以选择是否将这些样式作用于全局或仅限于当前组件( 通过 scoped 属性) 。
1 2 3 4 5 6 7 <style scoped > .example { font-family : Arial, sans-serif; color : #333 ; } </style >
组件注册 全局组件 全局组件可以在整个应用程序中使用。
1 2 3 4 5 6 7 8 9 10 11 12 import { createApp } from 'vue' import App from './App.vue' const app = createApp (App )app.component ('my-component' , { template : `<div>这是一个全局组件</div>` }) app.mount ('#app' )
局部组件 局部组件只能在定义它的父组件中使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template > <div > <h1 > 父组件</h1 > <ChildComponent /> </div > </template > <script > import ChildComponent from './ChildComponent.vue' export default { components : { ChildComponent } } </script >
组件通信 父子组件通信 父组件通过 props 向子组件传递数据。
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 <template > <div > <p > 来自父组件的消息: {{ message }}</p > </div > </template > <script > export default { props : ['message' ] } </script > <template > <div > <h1 > 父组件</h1 > <ChildComponent :message ="parentMessage" /> </div > </template > <script > import ChildComponent from './ChildComponent.vue' export default { components : { ChildComponent }, data ( ) { return { parentMessage : '你好, 子组件! ' } } } </script >
子父组件通信 子组件通过 $emit 触发事件, 父组件监听该事件。
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 <template > <button @click ="sendToParent" > 点击发送消息给父组件</button > </template > <script > export default { methods : { sendToParent ( ) { this .$emit('child-event' , '这是来自子组件的消息' ) } } } </script > <template > <div > <h1 > 父组件</h1 > <ChildComponent @child-event ="handleChildEvent" /> <p > 收到子组件的消息: {{ childMessage }}</p > </div > </template > <script > import ChildComponent from './ChildComponent.vue' export default { components : { ChildComponent }, data ( ) { return { childMessage : '' } }, methods : { handleChildEvent (message ) { this .childMessage = message } } } </script >
非父子组件通信 非父子组件, 使用事件总线( Event Bus) 通信。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createApp } from 'vue' export const EventBus = createApp ({})import { EventBus } from './event-bus.js' EventBus .config .globalProperties .$emit('custom-event' , data)import { EventBus } from './event-bus.js' EventBus .config .globalProperties .$on('custom-event' , (data ) => { console .log ('接收到数据:' , data) })
跨层级通信 provide / inject
用于跨层级传递数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script > export default { provide ( ) { return { theme : 'dark' } } } </script > <script > export default { inject : ['theme' ] } </script >
组件分类 根组件 根组件是整个 Vue 应用的入口点, 也是其他所有组件的父组件。 它通常与一个 DOM 元素绑定, 并且作为应用的起点, 包含整个应用的主要逻辑、 状态管理等。
⭐⭐ 特性
唯一性: 每个 Vue 应用都有且仅有一个根组件。 挂载点: 根组件通过 app.mount('#app')
与 HTML 中的一个元素( 如 <div id="app"></div>
) 绑定。 全局资源注册: 可以在根组件创建后注册全局组件、 指令、 过滤器等。 状态管理: 如果使用 Vuex 或 Pinia 等状态管理库, 通常会在根组件或其附近初始化这些库。
⭐⭐ main.js 1 2 3 4 5 6 7 8 9 10 11 12 import { createApp } from 'vue' import App from './App.vue' const app = createApp (App )app.component ('global-component' , { template : '<div>这是一个全局组件</div>' }) app.mount ('#app' )
⭐⭐ 根组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template > <div id ="app" > <h1 > {{ message }}</h1 > <global-component /> </div > </template > <script > export default { data ( ) { return { message : '欢迎来到我的 Vue 应用' } } } </script >
普通组件 普通组件是指除根组件之外的所有组件。 它们可以嵌套使用, 形成组件树结构。 普通组件有助于实现代码复用、 提高开发效率和维护性。
⭐⭐ 特性
可复用性: 设计良好的普通组件可以在不同的地方重复使用, 减少代码冗余。 局部注册: 普通组件可以通过局部注册的方式, 在需要使用的父组件中注册和使用。 Props 和 Events: 普通组件之间以及普通组件与父组件之间主要通过 Props 和自定义事件进行通信。
⭐⭐ 子组件 1 2 3 4 5 6 7 8 9 10 11 12 13 <template > <div > <p > {{ message }}</p > <button @click ="$emit('reply', '这是来自子组件的回复')" > 回复</button > </div > </template > <script > export default { props : ['message' ] } </script >
⭐⭐ 父组件 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 <template > <div > <h1 > 父组件</h1 > <child-component :message ="parentMessage" @reply ="handleReply" /> </div > </template > <script > import ChildComponent from './ChildComponent.vue' export default { components : { ChildComponent }, data ( ) { return { parentMessage : '你好, 子组件! ' } }, methods : { handleReply (reply ) { console .log (reply) } } } </script >
动态组件 动态组件允许你在同一个挂载点动态切换不同的组件, 而不必每次都销毁再重新创建它们。 这通过使用 <component>
元素和 is
属性来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template > <div > <button @click ="currentComponent = 'ComponentA'" > 加载组件 A</button > <button @click ="currentComponent = 'ComponentB'" > 加载组件 B</button > <component :is ="currentComponent" > </component > </div > </template > <script > import ComponentA from './ComponentA.vue' import ComponentB from './ComponentB.vue' export default { components : { ComponentA , ComponentB }, data ( ) { return { currentComponent : 'ComponentA' } } } </script >
异步组件 异步组件允许你按需加载组件, 有助于提高应用的初始加载性能。
⭐⭐ 组件注册 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Vue .component ('async-component' , () => import ('./myAsyncComponent.vue' ))<template> <div > <h1 > 父组件</h1 > <async-component > </async-component > </div > </template> <script > export default { components : { 'async-component' : () => import ('./myAsyncComponent.vue' ) } } </script >
⭐⭐ 错误处理 1 2 3 4 5 6 7 8 9 10 11 12 const AsyncComponent = ( ) => ({ component : import ('./MyComponent.vue' ), loading : LoadingComponent , error : ErrorComponent , delay : 200 , timeout : 3000 });
组件缓存 在 Vue 中, 组件默认每次切换时都会销毁并重新创建。 但有时候我们需要保留组件状态, 比如在 Tab 切换、 路由切换等场景中避免重复渲染和数据加载。 Vue 提供了一个内置组件 <keep-alive>
来实现组件的缓存功能。 <keep-alive>
是一个抽象组件, 它包裹动态组件( 如通过 component is
或 <router-view>
渲染的组件) , 可以缓存这些组件的状态, 防止其被频繁销毁和重建。
标签属性 ⭐⭐ include 只有匹配的组件会被缓存。 可以是字符串( 指定组件名) 、 正则表达式或者数组形式( 当为数组时, 元素可以是组件名或正则表达式) 。
1 2 3 4 5 6 7 8 9 <keep-alive include ="a,b" > <component :is ="view" > </component > </keep-alive > <keep-alive :include ="['a', 'b']" > <component :is ="view" > </component > </keep-alive >
⭐⭐ exclude 匹配的组件不会被缓存。 可以是字符串( 指定组件名) 、 正则表达式或者数组形式( 当为数组时, 元素可以是组件名或正则表达式) 。
1 2 3 4 5 6 7 8 9 <keep-alive exclude ="a,b" > <component :is ="view" > </component > </keep-alive > <keep-alive :exclude ="['a', 'b']" > <component :is ="view" > </component > </keep-alive >
⭐⭐ max 设置缓存组中的最大组件数。 如果超出这个数量, 最早缓存的组件将被销毁。
1 2 3 <keep-alive :max ="10" > <component :is ="view" > </component > </keep-alive >
控制缓存 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 <template > <keep-alive > <component :is ="currentComponent" v-if ="shouldRender" /> </keep-alive > </template > <script > export default { data ( ) { return { currentComponent : 'TabA' , shouldRender : true , }; }, }; </script > <keep-alive > <component :is ="currentComponent" v-if ="currentComponent === 'TabA'" /> <component :is ="currentComponent" v-if ="currentComponent === 'TabB'" /> </keep-alive >
生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <template > <div > 我是缓存组件 {{ count }}</div > </template > <script > export default { data ( ) { return { count : 0 , }; }, activated ( ) { console .log ('当组件被 <keep-alive> 缓存 > 激活时调用' ); }, deactivated ( ) { console .log ('当组件被 <keep-alive> 缓存 > 停用时调用' ); }, mounted ( ) { this .count ++; }, }; </script >
完整示例 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 <template > <div > <button @click ="currentTab = 'TabA'" > Tab A</button > <button @click ="currentTab = 'TabB'" > Tab B</button > <keep-alive > <component :is ="currentTab" v-if ="currentTab === 'TabA'" /> </keep-alive > <component :is ="currentTab" v-if ="currentTab === 'TabB'" /> </div > </template > <script > export default { data ( ) { return { currentTab : 'TabA' , }; }, components : { TabA : { name : 'TabA' , template : `<div>这是 Tab A, 计数: {{ count }} <button @click="count++">+1</button></div>` , data ( ) { return { count : 0 , }; }, activated ( ) { console .log ('TabA 被激活' ); }, deactivated ( ) { console .log ('TabA 被缓存' ); }, }, TabB : { template : `<div>这是 Tab B</div>` , }, }, }; </script >
组件进阶 v-model
在 Vue 中, v-model 是一种语法糖, 用于实现双向绑定。 它本质上是结合了 props
和 $emit
来实现父子组件之间的数据同步。
⭐⭐ 语法糖 1 2 3 <input type ="text" v-model ="inputValue" > <input type ="text" :value ="inputValue" @input ="inputValue = $event.target.value" >
⭐⭐ 表单通信 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 <template > <input type ="text" :value ="value" @input ="$emit('input', $event.target.value)" /> </template > <script > export default { props : ['value' ] } </script > <template > <div > <CustomInput v-model ="inputValue" /> </div > </template > <script > import CustomInput from './CustomInput.vue' export default { components : { CustomInput }, data ( ) { return { inputValue : '' } } } </script >
⭐⭐ sync 修饰符 1 2 3 4 5 6 7 8 9 10 11 12 13 <template > <input :value ="text" @input ="$emit('update:text', $event.target.value)" /> </template > <script > export default { props : ['text' ] } </script > <CustomInput :text.sync ="inputValue" />
自定义 默认情况下, v-model 使用的 prop 名为 value, 触发的事件名为 input。 但在某些场景下, 我们可能需要自定义这个行为, 比如使用 modelValue 和 update:modelValue, 或者其它任意命名的 prop 和事件。
Mixins
混合( Mixins) 提供了一种灵活的方式, 用于分发 Vue 组件中的可复用功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script > export default { created ( ) { console .log ('混合对象的 created 钩子' ) } } </script > <script > import myMixin from './myMixin.js' export default { mixins : [myMixin], created ( ) { console .log ('组件自身的 created 钩子' ) } } </script >
ref / $refs
在 Vue 中, ref
和 $refs
是用于直接访问和操作 DOM 元素或组件实例的重要机制。 虽然 Vue 鼓励使用数据驱动的方式处理视图, 但在某些场景下( 如聚焦输入框、 调用子组件方法等) , 需要直接访问元素或组件实例, 这时就非常有用了。
⭐⭐ 概念
ref 是一个特殊的属性, 可以定义在普通的 HTML 元素或 Vue 组件上。
它允许你为该元素或组件注册一个引用标识。 在渲染完成后, 可以通过 $refs 对象来访问这些引用。
$refs 是 Vue 实例上的一个对象, 它持有所有通过 ref 注册的 DOM 元素或组件实例。
⭐⭐ 普通 DOM 元素示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template > <div > <input ref ="myInput" type ="text" /> <button @click ="focusInput" > 聚焦输入框</button > </div > </template > <script > export default { methods : { focusInput ( ) { this .$refs .myInput .focus (); } } } </script >
⭐⭐ 子组件引用示例 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 <template > <div > 我是子组件</div > </template > <script > export default { methods : { sayHello ( ) { console .log ('Hello from child component!' ); } } } </script > <template > <div > <ChildComponent ref ="childRef" /> <button @click ="callChildMethod" > 调用子组件方法</button > </div > </template > <script > import ChildComponent from './ChildComponent.vue' ;export default { components : { ChildComponent }, methods : { callChildMethod ( ) { this .$refs .childRef .sayHello (); } } } </script >
$nextTick
由于 Vue 的响应式更新是异步的( 即数据变化后不会立即更新 DOM) , 而是将更新放入一个队列中, 在下一个事件循环“ tick” 中批量更新, 因此我们不能直接在数据修改后立即访问更新后的 DOM。 为了解决这个问题, Vue 提供了 $nextTick()
方法, 用于在DOM 更新之后执行某些操作。
⭐⭐ 概念
作用: 在下一次 DOM 更新循环结束后执行回调函数。 使用场景: 当你修改了数据, 并希望在 DOM 更新完成后进行一些操作( 如获取 DOM 尺寸、 手动聚焦、 调用第三方插件等) 时使用。
⭐⭐ 基本语法 1 2 3 4 5 6 7 8 this .$nextTick(() => { }); await this .$nextTick();
⭐⭐ 示例一: 数据更新后聚焦输入框 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 <template > <div > <input v-if ="showInput" ref ="myInput" type ="text" /> <button @click ="showAndFocus" > 显示并聚焦输入框</button > </div > </template > <script > export default { data ( ) { return { showInput : false }; }, methods : { showAndFocus ( ) { this .showInput = true ; this .$nextTick(() => { this .$refs .myInput .focus (); }); } } }; </script >
⭐⭐ 示例 2: 修改数据后获取 DOM 内容 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 <template > <div > <p ref ="textContent" > {{ message }}</p > <button @click ="changeMessage" > 更改消息并读取内容</button > </div > </template > <script > export default { data ( ) { return { message : '原始信息' }; }, methods : { changeMessage ( ) { this .message = '新信息' ; this .$nextTick(() => { console .log (this .$refs .textContent .innerText ); }); } } }; </script >
Vue 插槽 在 Vue 中, 插槽是一种内容分发机制, 允许你在组件中插入任意的 HTML 或子组件, 并由父组件决定这些内容的具体形式。 插槽的本质是: 父组件向子组件传递内容的方式。 它不像 props 那样只能传递数据, 而是可以传递完整的结构( HTML + 组件) , 从而实现更复杂的 UI 组合。
默认插槽 最简单的插槽形式, 也称为“ 匿名插槽” , 用于插入未命名的内容。 父组件可以在子组件标签内部放置任何 HTML 或 Vue 模板代码, 这些内容会替换子组件中的 <slot>
标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template > <div class ="card" > <slot > 这里是默认内容, 如果父组件没有传入内容, 则显示这个</slot > </div > </template > <template > <ChildComponent > <h2 > 这是插入到插槽中的内容</h2 > <p > 来自父组件的自定义内容</p > </ChildComponent > </template >
具名插槽 当需要在组件中插入多个不同位置的内容时, 可以使用具名插槽。 通过给 <slot>
标签添加 name 属性来定义具名插槽。 这样, 父组件可以通过 <template v-slot:插槽名>
或简写的 #插槽名来为目标插槽提供特定内容。
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 <template > <div class ="layout" > <header > <slot name ="header" > </slot > </header > <main > <slot > </slot > </main > <footer > <slot name ="footer" > </slot > </footer > </div > </template > <template > <LayoutComponent > <template v-slot:header > <h1 > 页面标题</h1 > </template > <p > 主要内容区域</p > <template #footer > <p > © 2025 我的网站. 版权所有.</p > </template > </LayoutComponent > </template >
作用域插槽 作用域插槽允许父组件访问子组件中的数据。 通过这种方式, 父组件可以根据子组件提供的数据自定义渲染内容, 同时保持逻辑和模板的分离。
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 <template > <ul > <li v-for ="item in items" :key ="item.id" > <slot :item ="item" > {{ item.name }}</slot > </li > </ul > </template > <script > export default { data ( ) { return { items : [ { id : 1 , name : '苹果' }, { id : 2 , name : '香蕉' }, { id : 3 , name : '橙子' } ] }; } }; </script > <template > <ListComponent > <template v-slot ="{ item }" > <span style ="color: red;" > {{ item.name }}</span > </template > </ListComponent > </template >
插槽解构 1 2 3 4 5 <template > <ListComponent v-slot ="{ item: { id, name }, index }" > <div > ID: {{ id }}, 名称: {{ name }}, 序号: {{ index }}</div > </ListComponent > </template >
Vue Router Vue 路由主要指的是 Vue Router, 它是 Vue.js 官方的路由管理器。 它与 Vue.js 核心深度集成, 让构建单页面应用( SPA) 变得非常容易。
Route( 路由配置) : 定义了 URL 和组件之间的映射关系。 Router( 路由器实例) : 负责解析 URL, 并将其匹配到相应的路由配置, 显示对应的组件。 Link( 导航链接) : 通过 <router-link>
组件来创建导航链接, 点击时不会重新加载页面。
基本使用 安装路由 1 2 3 npm install vue-router@3.6.5
引入使用 ⭐⭐ main.js 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 import Vue from 'vue' import App from './App.vue' import Home from './components/XXHome.vue' import About from './components/XXAbout.vue' import VueRouter from 'vue-router' Vue .use (VueRouter )const routes = [ { path : '/' , name : 'Home' , component : Home }, { path : '/home' , name : 'Home' , component : Home }, { path : '/about' , name : 'About' , component : About } ] const router = new VueRouter ({ routes : routes }) Vue .config .productionTip = false new Vue ({ render : h => h (App ), router : router }).$mount('#app' )
⭐⭐ app.vue 1 2 3 4 5 6 7 8 9 10 11 12 13 <template > <div id ="app" > <router-view > </router-view > </div > </template > <script > export default { name : 'App' } </script > <style > </style >
⭐⭐ 访问
http://localhost:8080/#/home
高亮导航 简单使用 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 <template > <div id ="app" > <nav > <router-link to ="/" > 首页</router-link > <router-link to ="/home" > home</router-link > <router-link to ="/about" > about</router-link > </nav > <router-view > </router-view > </div > </template > <script > export default { name : 'App' } </script > <style > .router-link-active { font-weight : bold; color : red; } </style >
自定义类名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <nav > <router-link to ="/" active-class ="active" > 首页</router-link > <router-link to ="/home" active-class ="active" > home</router-link > <router-link to ="/about" active-class ="active" > about</router-link > </nav > <style > .active { font-weight : bold; color : red; } </style >
精确匹配 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <nav > <router-link to ="/" active-class ="active" exact > 首页</router-link > <router-link to ="/home" active-class ="active" > home</router-link > <router-link to ="/about" active-class ="active" > about</router-link > </nav > <style > .active { font-weight : bold; color : red; } </style >
模块拆分 在 Vue 项目中, 随着应用功能的增多, 路由配置( routes) 会变得越来越复杂。 为了保持代码结构清晰、 易于维护, 将不同种类的文件拆分到多个独立文件中是一种常见的做法。
模块拆分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 src/ ├── components/ // 可复用组件文件夹 │ ├── button.vue // 按钮组件 │ └── alert.vue // 弹窗组件 ├── router/ // 路由相关文件夹 │ ├── index.js // 主路由入口 │ ├── home.js // 首页相关路由 │ ├── user.js // 用户相关路由 │ └── settings.js // 设置相关路由 ├── views/ // 视图相关组件文件夹 │ ├── Home.vue // 首页 │ ├── UserProfile.vue // 用户详情页 │ ├── UserList.vue // 用户列表页 │ └── Settings.vue // 系统设置页 ├── app.vue └── main.js
路径简写 在 Vue 项目中, 默认会将 @ 设置为 src 目录的别名。
⭐⭐ 使用示例 1 2 3 import HelloWorld from '@/components/button.vue' import HelloWorld from '../src/components/button.vue'
⭐⭐ 修改目录 可以通过 webpack.config.js
或 vue.config.js
文件中 resolve.alias 属性设置
1 2 3 4 5 6 7 8 9 10 module .exports = { configureWebpack : { resolve : { alias : { '@' : path.resolve (__dirname, 'src' ) } } } }
路由配置 ⭐⭐ 创建子路由文件 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 export default [ { path : '/' , name : 'Home' , component : () => import ('../views/Home.vue' ), meta : { title : '首页' } } ] export default [ { path : '/user/list' , name : 'UserList' , component : () => import ('../views/UserList.vue' ), meta : { title : '用户列表' } }, { path : '/user/profile/:id' , name : 'UserProfile' , component : () => import ('../views/UserProfile.vue' ), meta : { title : '用户详情' } } ] export default [ { path : '/settings' , name : 'Settings' , component : () => import ('../views/Settings.vue' ), meta : { title : '系统设置' } } ]
⭐⭐ 主路由合并所有子路由 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 import Vue from 'vue' import VueRouter from 'vue-router' Vue .use (VueRouter )import homeRoutes from './home' import userRoutes from './user' import settingsRoutes from './settings' const routes = [ ...homeRoutes, ...userRoutes, ...settingsRoutes, { path : '*' , redirect : '/' } ] const router = new VueRouter ({ routes : routes }) export default router
⭐⭐ 在 main.js 中使用路由 1 2 3 4 5 6 7 8 import Vue from 'vue' import App from './App.vue' import router from './router' new Vue ({ render : h => h (App ), router : router }).$mount('#app' )
路由模式 Vue Router 支持两种主要的路由模式: hash
模式和 history
模式。 这两种模式决定了应用如何处理 URL 以及如何在浏览器历史记录中进行导航。
Hash 模式
工作原理: hash 模式利用了 URL 中的 # 号( 即 hash) 来模拟一个完整的 URL, 当 # 后面的部分发生变化时, 页面不会重新加载。 Vue Router 默认使用的就是这种模式。 优点: 兼容性好, 几乎所有的浏览器都支持。 不需要服务器端做任何配置, 因为 # 及其后面的内容不会被发送到服务器。 缺点: URL 看起来不太美观, 包含了一个 # 号。
1 2 3 4 const router = new VueRouter ({ mode : 'hash' , routes : routes })
History 模式
工作原理: history 模式依赖于 HTML5 History API
来实现 URL 导航而无需重新加载页面。 它允许你使用正常的路径格式, 如 /user/id
, 而不是带 # 的路径。 优点: 提供更传统且美观的 URL, 没有 #。 缺点: 需要服务器端的支持, 因为如果用户直接访问或刷新页面, 服务器需要返回 index.html 文件, 否则会返回 404 错误。
⭐⭐ 配置路由 1 2 3 4 const router = new VueRouter ({ mode : 'history' , routes : routes })
⭐⭐ 配置服务器端: Apache 1 2 3 4 5 6 7 8 <IfModule mod_rewrite.c > RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule >
⭐⭐ 配置服务器端: Nginx 1 2 3 location / { try_files $uri $uri / /index.html; }
域名根目录 在 Vue Router 中, base 配置项用于指定应用的基础 URL。 这个选项对于那些不是部署在域名根目录的应用特别有用, 例如, 当你的应用被部署在一个子路径( 如 https://example.com/my-app/
) 时, 你就可以使用 base 来配置这个基础路径。
⭐⭐ 配置文件 1 2 3 4 5 module .exports = { publicPath : '/my-app/' }
⭐⭐ 路由配置 1 2 3 4 5 6 7 8 const router = new VueRouter ({ mode : 'history' , base : process.env .BASE_URL , routes : routes })
⭐⭐ 服务端配置 1 2 3 location /my-app/ { try_files $uri $uri / /my-app/index.html; }
路由重定向 路由重定向 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const routes = [ { path : '/' , name : 'Home' , component : Home }, { path : '/about' , name : 'About' , component : About }, { path : '/home' , redirect : '/' }, { path : '/redirect-me' , redirect : to => { return { path : '/about' } }}, { path : '/' , alias : '/the-home-page' , component : Home } ]
配置404页面 ⭐⭐ 创建 404 页面组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template > <div class ="not-found" > <h1 > 404 - 页面未找到</h1 > <p > 对不起, 您访问的页面不存在。 </p > </div > </template > <script > export default { name : "NotFound" } </script > <style scoped > .not-found { text-align : center; margin-top : 50px ; } </style >
⭐⭐ 配置路由 1 2 3 4 5 6 7 8 9 10 11 import Home from '../views/Home.vue' import About from '../views/About.vue' import NotFound from '../views/NotFound.vue' const routes = [ { path : '/' , component : Home , name : 'Home' }, { path : '/about' , component : About , name : 'About' }, { path : '*' , component : NotFound } ]
前进 / 后退 返回上一页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template > <button @click ="goBack" > 返回上一页</button > </template > <script > export default { methods : { goBack ( ) { } } } </script >
前进一页
自定义返回 1 2 3 4 5 6 7 8 goBack ( ) { if (window .history .length > 1 ) { this .$router .back (); } else { this .$router .push ('/' ); } }
路由传参 路径参数 路径参数是 URL 中的一部分, 用于向目标路由传递参数。 例如, /user/:id
中的 :id
就是一个路径参数。
⭐⭐ 配置路由 1 2 3 4 5 6 7 const routes = [ { path : '/user/:id' , name : 'UserDetail' , component : UserDetail } ]
⭐⭐ 传递参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <router-link :to ="{ name: 'UserDetail', params: { id: 123 }}" > 查看用户123</router-link > <router-link to ="/user/456" > 查看用户456</router-link > <button @click ="goToUser(789)" > 跳转到用户789</button > <script > export default { methods : { goToUser (id ) { this .$router .push ({ name : 'UserDetail' , params : { id : id } }) } } } </script >
⭐⭐ 访问参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <template > <div > <h1 > User Detail</h1 > <p > User ID: {{ $route.params.id }}</p > </div > </template > <script > export default { name : "UserDetail" , created ( ) { console .log (this .$route .params .id ); } } </script >
查询参数 ⭐⭐ 传递参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <router-link :to ="{ name: 'UserList', query: { search: 'vue', page: 2 } }" > 搜索 vue 用户, 第2页</router-link > <router-link to ="/user/list?search=react&page=1" > 搜索 react 用户, 第1页</router-link > <button @click ="goToPage(1)" > 跳转到第一页</button > <script > export default { methods : { goToPage (page ) { this .$router .push ({ name : 'UserList' , query : { search : 'vue' , page : page } }) } } } </script >
⭐⭐ 访问参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template > <div > <h2 > 用户列表</h2 > <p > 搜索关键词: {{ $route.query.search }}</p > <p > 当前页码: {{ $route.query.page }}</p > </div > </template > <script > export default { name : 'UserList' , created ( ) { console .log ('搜索词:' , this .$route .query .search ) console .log ('页码:' , this .$route .query .page ) } } </script >
嵌套路由 嵌套路由是一种用于构建复杂用户界面的强大功能。 它允许你在一个父级路由下定义子路由, 从而创建多层次的页面布局。
父级路由: 包含一个或多个子路由的路由。 子路由: 依赖于父级路由的路由, 通常用于展示更详细的信息或者特定的功能模块。
创建组件 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 <template > <div > <h1 > 产品列表</h1 > <ul > <li v-for ="product in products" :key ="product.id" > <router-link :to ="{ name: 'ProductDetail', params: { id: product.id }}" > {{ product.name }} </router-link > </li > </ul > <router-view > </router-view > </div > </template > <script > export default { data ( ) { return { products : [ { id : 1 , name : '产品A' }, { id : 2 , name : '产品B' }, { id : 3 , name : '产品C' } ] } } } </script > <template > <div > <h2 > 产品详情</h2 > <p > ID: {{ $route.params.id }}</p > </div > </template > <script > export default { mounted ( ) { console .log ('当前产品ID:' , this .$route .params .id ) } } </script >
路由配置
children 数组: 用于定义子路由。 每个子路由对象都有自己的 path、 name 和 component 属性。 默认子路由: 如果你希望在访问父级路由路径时自动加载一个特定的子路由, 可以为空字符串定义一个子路由, 如 path: ‘’。 懒加载组件: 为了提高性能, 可以对不常用的组件采用懒加载的方式, 例如 component: () => import(‘../views/…’)。
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 import Vue from 'vue' import VueRouter from 'vue-router' import ProductList from '../views/ProductList.vue' import ProductDetail from '../views/ProductDetail.vue' Vue .use (VueRouter )const routes = [ { path : '/products' , component : ProductList , children : [ { path : '' , name : 'ProductList' , component : () => import ('../views/ProductList.vue' ) }, { path : ':id' , name : 'ProductDetail' , component : ProductDetail } ] } ] const router = new VueRouter ({ mode : 'history' , base : process.env .BASE_URL , routes }) export default router
主应用组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <template > <div id ="app" > <nav > <router-link to ="/" > 首页</router-link > <router-link to ="/products" > 产品列表</router-link > </nav > <router-view > </router-view > </div > </template > <script > export default { name : 'App' } </script >
命名视图 命名视图允许你在同一层级渲染多个视图, 而不是嵌套它们。 这对于构建复杂的布局特别有帮助, 比如在一个页面上同时展示一个侧边栏和一个主内容区域。 要使用命名视图, 你需要在<router-view>
组件上指定一个 name
属性。 默认情况下, 没有指定 name
的 <router-view>
会被认为是“ 默认” 视图。
创建组件 1 2 3 4 5 6 7 8 9 10 11 12 <template > <header > <h1 > 这是头部</h1 > </header > </template > <script > export default { name : "Header" } </script >
1 2 3 4 5 6 7 8 9 10 11 12 <template > <footer > <p > 这是底部信息。 </p > </footer > </template > <script > export default { name : "Footer" } </script >
⭐⭐ Home.vue 1 2 3 4 5 6 7 8 9 10 11 12 <template > <div > <p > 这是首页内容。 </p > </div > </template > <script > export default { name : "Home" } </script >
配置路由 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import Vue from 'vue' ;import Router from 'vue-router' ;Vue .use (Router );import Header from '@/components/Header.vue' ;import Footer from '@/components/Footer.vue' ;import Home from '@/views/Home.vue' ;export default new Router ({ routes : [ { path : '/' , components : { default : Home , header : Header , footer : Footer } } ] });
主应用组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template > <div id ="app" > <router-view name ="header" > </router-view > <router-view > </router-view > <router-view name ="footer" > </router-view > </div > </template > <script > export default { name : "App" } </script >
前置守卫 Vue 全局前置守卫主要用于路由跳转前的拦截处理, 常用于登录验证等场景。 它允许你定义一个函数, 在每次导航时都会先调用这个函数, 以决定是否允许导航继续进行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 router.beforeEach ((to, from , next ) => { const isAuthenticated = false ; if (to.path !== '/login' && !isAuthenticated) { next ('/login' ); } else { next (); } });
Vuex Vuex 是 Vue.js 的官方状态管理库, 它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化。
基本使用 安装Vuex 1 2 3 npm install vuex@3.6.2
创建仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex )const store = new Vuex .Store ({ state : { title : "Hello Vue" } }) export default store
引入仓库 1 2 3 4 5 6 7 8 9 import Vue from 'vue' ;import App from './App.vue' ;import store from './store' ;new Vue ({ render : h => h (App ), store : store }).$mount('#app' )
使用仓库 1 2 3 4 5 <template > <div id ="app" > <h1 > {{ $store.state.title }}</h1 > </div > </template >
核心概念
State: 驱动应用的数据源。
Getter: 从 state 中派生出一些状态, 类似于 Vue 组件中的计算属性。
Mutation: 唯一可以改变 store 中状态的方法, 必须是同步函数。
Action: 类似于 mutation, 不同在于它可以包含任意异步操作。
Module: 将 store 分割成模块( module) 。 每个模块拥有自己的 state、 mutation、 action、 getter。
State 在 Vuex 中, State 是存储应用层级状态的核心概念。 它就像一个全局的变量容器, 但与普通的全局变量不同的是, Vuex 的 state 通过定义明确的规则来管理状态的变化, 这使得状态变化更加透明和可追踪。 State 是整个应用的“ 数据源” , 它是驱动应用行为的核心。 Vuex 将这些共享的状态集中存储在一个单一的 store 中, 并通过 Vue 的响应式系统使其具有响应性。
单一状态树: Vuex 使用单一状态树, 即用一个对象就包含了全部的应用层级状态。 这也意味着每个应用将仅仅包含一个 store 实例。 响应式: Vuex 中的状态是响应式的, 这意味着当 Vue 组件从 store 中读取状态的时候, 若 store 中的状态发生变化, 那么相应的组件也会相应地得到高效更新。 访问状态: 可以在任何 Vue 组件中使用 this.$store.state 来访问 Vuex store 中的状态。 为了更好地组织代码, 通常会使用计算属性来映射 state。
⭐⭐ 定义数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 export default new Vuex .Store ({ state : { count : 0 , user : { name : 'Alice' , age : 25 }, todos : [ { id : 1 , text : '学习 Vuex' , done : true }, { id : 2 , text : '写项目文档' , done : false } ] } })
⭐⭐ 使用示例 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 <template > <div > <h2 > 计数器: {{ count }}</h2 > <p > 用户名称: {{ user.name }}</p > <ul > <li v-for ="todo in todos" :key ="todo.id" > {{ todo.text }} - {{ todo.done ? '已完成' : '未完成' }} </li > </ul > </div > </template > <script > export default { computed : { count ( ) { return this .$store .state .count }, user ( ) { return this .$store .state .user }, todos ( ) { return this .$store .state .todos } } } </script >
Getter 在 Vuex 中, Getter 类似于 Vue 组件中的计算属性。 它用于从 state 中派生出一些状态, 这些状态可能需要基于现有的 state 进行一些转换或过滤。 Getter 的返回值会根据它的依赖被缓存起来, 并且只有当它的依赖值发生了变化时才会重新计算。
缓存: Getter 会基于它们的依赖进行缓存, 只有当依赖的状态发生变化时才会重新计算。 接收其他 getter 作为第二个参数: 这意味着你可以将 getter 链接起来, 一个 getter 可以使用另一个 getter 的结果。 访问方式: 可以通过 store.getters 访问到所有的 getter 函数, 也可以通过组件中的 this.$store.getters 来访问。
⭐⭐ 定义数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 export default new Vuex .Store ({ state : { todos : [ { id : 1 , text : '学习 Vuex' , done : true }, { id : 2 , text : '编写 Vuex 应用' , done : false } ] }, getters : { doneTodos : state => { return state.todos .filter (todo => todo.done ); }, doneTodosCount : (state, getters ) => { return getters.doneTodos .length ; }, getTodoById : (state ) => (id ) => { return state.todos .find (todo => todo.id === id); } } });
⭐⭐ 使用示例 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 <template > <div > <p > {{ doneTodos }}</p > <p > {{ doneTodosCount }}</p > <p > {{ getTodoById(2) }}</p > </div > </template > <script > export default { computed : { doneTodos ( ) { return this .$store .getters .doneTodos ; }, doneTodosCount ( ) { return this .$store .getters .doneTodosCount ; }, getTodoById ( ) { return id => this .$store .getters .getTodoById (id) } } } </script >
Mutation 在 Vuex 中, Mutation 是唯一可以修改 state( 状态) 的地方。 它是一个同步函数, 接收 state 作为第一个参数, 并通过更改 state 来改变应用的状态。
特性 描述
同步操作
Mutation 必须是同步的, 这样可以确保状态变化可追踪、 易于调试。
提交方式
使用 store.commit('mutationName', payload) 提交 mutation。
命名规范
推荐使用常量命名( 如 INCREMENT) , 便于维护和查找。
不能异步
如果需要执行异步操作, 请使用 Action, 而不是直接在 Mutation 中写异步代码。
⭐⭐ 定义数据 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 export default new Vuex .Store ({ state : { count : 0 , todos : [ { id : 1 , text : '学习 Vuex' , done : true }, { id : 2 , text : '编写 Vuex 应用' , done : false } ] }, mutations : { INCREMENT (state ) { state.count ++ }, INCREMENT_BY (state, payload ) { state.count += payload.amount }, TOGGLE_TODO_DONE (state, id ) { const todo = state.todos .find (todo => todo.id === id) if (todo) { todo.done = !todo.done } } } })
⭐⭐ 使用示例 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 <template > <div > <p > 当前计数: {{ count }}</p > <button @click ="increment" > +1</button > <button @click ="incrementBy(5)" > +5</button > <ul > <li v-for ="todo in todos" :key ="todo.id" > {{ todo.text }} - {{ todo.done ? '已完成' : '未完成' }} </li > </ul > <button @click ="toggleTodoDone(1)" > 切换 ID=1 的任务状态</button > </div > </template > <script > export default { computed : { count ( ) { return this .$store .state .count }, todos ( ) { return this .$store .state .todos } }, methods : { increment ( ) { this .$store .commit ('INCREMENT' ) }, incrementBy (amount ) { this .$store .commit ('INCREMENT_BY' , { amount }) }, toggleTodoDone (id ) { this .$store .commit ('TOGGLE_TODO_DONE' , id) } } } </script >
Action 在 Vuex 中, Action 类似于 Mutation, 不同之处在于: Action 提交的是 Mutation, 而不是直接变更状态; Action 可以包含任意异步操作, 而 Mutation 必须是同步的。 通过 Action, 可以处理复杂的逻辑, 比如异步请求、 条件判断等, 并最终通过提交 Mutation 来改变状态。
⭐⭐ 定义数据 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 export default new Vuex .Store ({ state : { count : 0 }, mutations : { increment (state ) { state.count ++ }, decrement (state ) { state.count -- } }, actions : { incrementAsync ({ commit } ) { setTimeout (() => { commit ('increment' ) }, 1000 ) }, decrementBy ({ commit }, amount ) { setTimeout (() => { commit ('decrement' ) if (amount > 1 ) { console .log (`已减少 ${amount} 次` ) } }, 1000 ) } } })
⭐⭐ 使用示例 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 <template > <div > <h2 > 当前计数: {{ count }}</h2 > <button @click ="increment" > 同步 +1</button > <button @click ="incrementAsync" > 异步 +1</button > <button @click ="decrementBy(1)" > 异步 -1</button > </div > </template > <script > export default { computed : { count ( ) { return this .$store .state .count } }, methods : { increment ( ) { this .$store .commit ('increment' ) }, incrementAsync ( ) { this .$store .dispatch ('incrementAsync' ) }, decrementBy (amount ) { this .$store .dispatch ('decrementBy' , amount) } } } </script >
Modules Vuex 的模块化( Modules) 功能允许我们将 store 分割成模块( module) 。 每个模块拥有自己的 state、 mutation、 action 和 getter, 甚至还可以嵌套子模块。 这对于大型应用特别有用, 因为它帮助我们更好地组织和管理状态。
独立的状态空间: 每个模块都有自己的状态。 命名空间: 默认情况下, 模块内的 action、 mutation 和 getter 是注册在全局命名空间下的。 这意味着如果你有两个不同模块定义了相同的 mutation 类型, 它们会冲突。 为了解决这个问题, 可以开启命名空间。 嵌套模块: 模块可以嵌套, 形成更复杂的结构。
⭐⭐ 定义数据 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 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex )const userModule = { namespaced : true , state : { username : '张三' , age : 25 }, mutations : { setUsername (state, newUsername ) { state.username = newUsername } }, actions : { updateUsername ({ commit }, newUsername ) { setTimeout (() => { commit ('setUsername' , newUsername) }, 1000 ) } }, getters : { userInfo : (state ) => { return `${state.username} - ${state.age} 岁` } } } const cartModule = { namespaced : true , state : { items : [ { id : 1 , name : '商品A' , price : 99 }, { id : 2 , name : '商品B' , price : 199 } ] }, mutations : { removeItem (state, id ) { state.items = state.items .filter (item => item.id !== id) } } } export default new Vuex .Store ({ modules : { user : userModule, cart : cartModule } })
⭐⭐ 使用示例 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 <template > <div > <h2 > 用户信息</h2 > <p > {{ userInfo }}</p > <input v-model ="newUsername" placeholder ="输入新用户名" > <button @click ="updateUsername" > 更新用户名( 异步) </button > <h2 > 购物车内容</h2 > <ul > <li v-for ="item in cartItems" :key ="item.id" > {{ item.name }} - ¥{{ item.price }} <button @click ="removeItem(item.id)" > 移除</button > </li > </ul > </div > </template > <script > export default { data ( ) { return { newUsername : '' } }, computed : { userInfo ( ) { return this .$store .getters ['user/userInfo' ] }, cartItems ( ) { return this .$store .state .cart .items } }, methods : { updateUsername ( ) { if (this .newUsername .trim ()) { this .$store .dispatch ('user/updateUsername' , this .newUsername ) } }, removeItem (id ) { this .$store .commit ('cart/removeItem' , id) } } } </script >
辅助函数 mapState mapState 是 Vuex 中的一个辅助函数, 用于帮助我们更方便地在计算属性中使用 state。 当一个组件需要获取多个状态时, 如果每次都从 this.$store.state 中读取, 会显得非常繁琐。 这时可以利用 mapState 辅助函数生成计算属性, 简化代码。
⭐⭐ 定义数据 1 2 3 4 5 6 Vuex .Store ({ state : { count : 0 , message : 'Hello Vuex!' } });
⭐⭐ 使用函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <template > <div id ="app" > <p > Count is: {{ count }}</p > <p > Message is: {{ message }}</p > </div > </template > <script > import { mapState } from 'vuex' ;export default { computed : { ...mapState ([ 'count' , 'message' ]) } } </script >
mapGetters mapGetters 是 Vuex 提供的一个 辅助函数, 用于将 store 中的 getters 映射为组件的计算属性( computed properties) , 从而在模板中更方便地使用这些值。
⭐⭐ 定义数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 export default new Vuex .Store ({ state : { todos : [ { id : 1 , text : 'Learn Vue' , done : true }, { id : 2 , text : 'Build an app' , done : false }, { id : 3 , text : 'Deploy it' , done : true } ] }, getters : { doneTodos (state ) { return state.todos .filter (todo => todo.done ); }, undoneTodosCount (state ) { return state.todos .filter (todo => !todo.done ).length ; } } });
⭐⭐ 使用示例 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 <template > <div > <h2 > Done Todos:</h2 > <ul > <li v-for ="todo in doneTodos" :key ="todo.id" > {{ todo.text }}</li > </ul > <p > Undone Todos Count: {{ undoneTodosCount }}</p > </div > </template > <script > import { mapGetters } from 'vuex' ;export default { computed : { ...mapGetters (['doneTodos' , 'undoneTodosCount' ]) ...mapGetters ({ done : 'doneTodos' , totalUndone : 'undoneTodosCount' }) } }; </script >
mapMutations mapMutations 是 Vuex 提供的一个 辅助函数, 用于将 store 中的 mutations 映射为组件中的方法( methods) , 从而在组件中更方便地调用这些 mutation 来修改 state。
⭐⭐ 定义数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 export default new Vuex .Store ({ state : { count : 0 }, mutations : { increment (state ) { state.count ++; }, decrement (state ) { state.count --; }, addCount (state, payload ) { state.count += payload.amount ; } } });
⭐⭐ 使用示例 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 <template > <div > <p > 当前计数: {{ count }}</p > <button @click ="increment" > +1</button > <button @click ="decrement" > -1</button > <button @click ="addCount({ amount: 5 })" > +5</button > </div > </template > <script > import { mapMutations } from 'vuex' ;export default { methods : { ...mapMutations (['increment' , 'decrement' , 'addCount' ]) ...mapMutations ({ increase : 'increment' , decrease : 'decrement' , add : 'addCount' }) } }; </script >
mapActions mapActions 是 Vuex 提供的一个 辅助函数, 用于将 store 中的 actions 映射为组件中的方法( methods) , 从而在组件中更方便地调用这些 actions 来执行异步操作或提交 mutations。
⭐⭐ 定义数据 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 export default new Vuex .Store ({ state : { count : 0 }, mutations : { increment (state ) { state.count ++; } }, actions : { incrementAsync ({ commit } ) { setTimeout (() => { commit ('increment' ); }, 1000 ); }, addCount (context, payload ) { setTimeout (() => { context.commit ('increment' ); console .log ('Added:' , payload.amount ); }, 500 ); } } });
⭐⭐ 使用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template > <div > <p > 当前计数: {{ count }}</p > <button @click ="incrementAsync" > +1 (异步)</button > <button @click ="addCount({ amount: 5 })" > +5</button > </div > </template > <script > import { mapActions } from 'vuex' ;export default { methods : { ...mapActions (['incrementAsync' , 'addCount' ]) ...mapActions ({ increase : 'incrementAsync' , addFive : 'addCount' }) } }; </script >
模块化使用 当我们使用模块化时, 为了更方便地访问模块中的 state、 getters、 mutations 和 actions, 我们需要配合使用 mapState、 mapGetters、 mapMutations 和 mapActions, 并指定模块路径。
⭐⭐ 定义数据 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 export default { namespaced : true , state : { username : '张三' , age : 25 }, getters : { userInfo (state ) { return `姓名: ${state.username} , 年龄: ${state.age} ` ; } }, mutations : { setUsername (state, newUsername ) { state.username = newUsername; }, setAge (state, newAge ) { state.age = newAge; } }, actions : { updateUserInfo ({ commit }, payload ) { commit ('setUsername' , payload.username ); commit ('setAge' , payload.age ); } } }; import Vue from 'vue' ;import Vuex from 'vuex' ;import userModule from './modules/userModule' ;Vue .use (Vuex );export default new Vuex .Store ({ modules : { user : userModule } });
⭐⭐ 使用示例 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 <template > <div > <h2 > 用户信息</h2 > <p > 用户名: {{ username }}</p > <p > 年龄: {{ age }}</p > <p > 用户详情: {{ userInfo }}</p > <button @click ="setUsername('李四')" > 修改用户名</button > <button @click ="setAge(30)" > 修改年龄</button > <button @click ="updateUserInfo({ username: '王五', age: 40 })" > 更新用户信息</button > </div > </template > <script > import { mapState, mapGetters, mapMutations, mapActions } from 'vuex' ;export default { computed : { ...mapState ('user' , ['username' , 'age' ]), ...mapGetters ('user' , ['userInfo' ]) }, methods : { ...mapMutations ('user' , ['setUsername' , 'setAge' ]), ...mapActions ('user' , ['updateUserInfo' ]) } }; </script >
Vue 3 Vue 3 相对于 Vue 2 有许多重要的改进和新特性。
Composition API: Vue 3 引入了 Composition API, 允许更灵活地组织组件逻辑, 并解决了 Options API 中逻辑复用的问题。
性能提升: Vue 3 的渲染性能提高了约 40%, 并且在初始加载时间和更新性能上都有显著的提升。
更好的 TypeScript 支持: Vue 3 对 TypeScript 的集成更加紧密, 提供了更好的类型推导和错误检查。
更小的打包体积: Vue 3 使用 Tree Shaking 技术和更高效的代码分割, 使得最终打包文件体积更小。
响应式系统: Vue 3 使用 Proxy 替代 Vue 2 的 Object.defineProperty 来实现响应式系统, 这使得它能够侦测到更多类型的变更, 并且性能更好。
Fragment 支持: Vue 3 允许组件返回多个根元素( 即支持 Fragment) , 而 Vue 2 需要一个单一的根元素。
v-model 语法变化: Vue 3 增强了 v-model 的工作机制, 支持自定义绑定的 prop 和事件, 并且可以同时使用多个 v-model 绑定。
新的生命周期钩子: Vue 3 提供了一些新的生命周期钩子函数, 如 onMounted、 onUpdated 和 onUnmounted, 这些是为 Composition API 设计的。
Teleport 和 Suspense: Vue 3 引入了 Teleport 组件用于跨 DOM 边界渲染内容, 以及 Suspense 用于异步依赖的占位加载。
Router 和 Vuex 更新: Vue Router 和 Vuex 都有更新版本来更好地与 Vue 3 结合使用, 提供了基于 Composition API 的新功能。
其他改进: Vue 3 支持自定义渲染器, 提供对 SSR 更好的支持
初始化项目 创建命令 1 2 3 4 5 6 7 8 9 10 11 node -v npm init vue@latest npm install npm run dev
目录介绍 1 2 3 4 5 6 7 8 9 10 11 12 my-vue-app/ ├── public/ # 静态资源目录, 不会经过 webpack/vite 处理 │ └── favicon.ico # 网站图标 ├── src/ │ ├── assets/ # 静态资源( 如图片、 字体等) , 会被 vite 构建处理 │ ├── components/ # 可复用的 Vue 组件 │ ├── App.vue # 根组件, 整个应用的入口 │ └── main.js # 应用入口文件, 创建 Vue 实例并挂载 App.vue ├── index.html # Vite 默认入口 HTML 文件, 自动注入打包后的 JS ├── package.json # 项目配置和依赖信息 ├── vite.config.js # Vite 构建工具的核心配置文件 └── vitest.config.js # Vitest 测试框架的配置文件( 如果启用了测试功能)
组合式 API Vue 3 引入了组合式 API( Composition API) , 这是一个基于函数的 API, 用于在组件内部逻辑复用和代码组织。 它旨在解决 Vue 2 中选项型 API( Options API) 在处理复杂组件时的一些局限性。
setup 在 Vue 3 中, setup 函数是一个新的组件选项, 它作为组合式 API( Composition API) 的入口点。 会在组件实例创建之前执行, 这意味着你无法在这个阶段访问到 this, 因为此时组件实例尚未创建。
⭐⭐ 使用示例 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 <template > <div > <p > {{ count }}</p > <button @click ="increment" > Increment</button > </div > </template > <script > import { ref, onMounted } from 'vue' ;export default { setup ( ) { const count = ref (0 ); const increment = ( ) => { count.value ++; }; onMounted (() => { console .log ('Component is mounted!' ); }); return { count, increment }; } }; </script >
⭐⭐ 语法糖 使用 <script setup>
语法可以极大地简化组合式 API 的写法。 它是 Vue 3.2 引入的一种编译时语法糖, 使得代码更简洁、 直观, 特别适合使用组合式 API 的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <template > <div > <p > Count: {{ count }}</p > <button @click ="increment" > Increment</button > </div > </template > <script setup > import { ref } from 'vue' const count = ref (0 )function increment ( ) { count.value ++ } </script >
reactive 在 Vue 3 的组合式 API 中, reactive 是一个非常重要的函数, 用于创建响应式对象。 它通过 Proxy( 或在不支持 Proxy 的环境中使用 Object.defineProperty) 来追踪对象的属性变化, 并在数据变化时自动更新视图。
⭐⭐ 使用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <template > <div > <p > 姓名: {{ user.name }}</p > <p > 年龄: {{ user.age }}</p > <button @click ="increaseAge" > 增加年龄</button > </div > </template > <script setup > import { reactive } from 'vue' const user = reactive ({ name : '张三' , age : 25 }) function increaseAge ( ) { user.age ++ } </script >
⭐⭐ 深层响应式对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { reactive } from 'vue' const state = reactive ({ user : { name : '李四' , info : { age : 30 , job : '工程师' } } }) state.user .info .job = '前端开发'
ref 在 Vue 3 的组合式 API 中, ref 是一个非常基础且常用的功能, 用于创建响应式的引用( reference) 。 它特别适用于管理基本类型( 如数字、 字符串、 布尔值等) , 也可以用于包装对象。
⭐⭐ ref 与 reactive 对比
特性 ref reactive
数据类型
任意类型( 包括基本类型)
对象或数组
是否需要 .value
需要
不需要
是否可重新赋值
是
否( 重新赋值会丢失响应性)
解构后是否保持响应性
是
否( 需配合 toRefs)
⭐⭐ 使用示例 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 <template > <div > <p > 计数器: {{ count }}</p > <button @click ="increment" > 加一</button > </div > </template > <script setup > import { ref } from 'vue' const count = ref (0 )function increment ( ) { count.value ++ } const user = ref ({ name : '张三' , age : 25 }) user.value .age = 26 </script >
计算/监听 computed computed 用于创建计算属性。 它基于其依赖的响应式数据进行缓存, 并仅在其依赖项发生变化时才重新计算, 从而提高性能。
⭐⭐ 只读计算属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template > <div > <p > 姓氏: {{ firstName }}</p > <p > 名字: {{ lastName }}</p > <p > 全名: {{ fullName }}</p > </div > </template > <script setup > import { ref, computed } from 'vue' const firstName = ref ('张' )const lastName = ref ('三' )const fullName = computed (() => { return `${firstName.value} ${lastName.value} ` }) </script >
⭐⭐ 可写的计算属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <script setup > import { ref, computed } from 'vue' const firstName = ref ('' )const lastName = ref ('' )const fullName = computed ({ get ( ) { return `${firstName.value} ${lastName.value} ` .trim () }, set (newValue ) { const names = newValue.split (' ' ) firstName.value = names[0 ] || '' lastName.value = names[1 ] || '' } }) </script > <template > <input v-model ="fullName" placeholder ="输入全名" > <p > 姓氏: {{ firstName }}</p > <p > 名字: {{ lastName }}</p > </template >
watch watch 用于监听响应式数据的变化, 并在数据变化时执行副作用逻辑。 它与 Vue 2 中的选项 API 中的 watch 功能类似, 但在组合式 API 中提供了更灵活和强大的功能。
⭐⭐ 监听单个属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script setup > import { ref, watch } from 'vue' const count = ref (0 )watch (count, (newVal, oldVal ) => { console .log (`count changed from ${oldVal} to ${newVal} ` ) }) function increment ( ) { count.value ++ } </script > <template > <button @click ="increment" > Increment</button > </template >
⭐⭐ 监听多个来源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script setup > import { ref, watch } from 'vue' const firstName = ref ('张' )const lastName = ref ('三' )watch ([firstName, lastName], ([newFirstName, newLastName], [oldFirstName, oldLastName] ) => { console .log (`姓名从 "${oldFirstName} ${oldLastName} " 更改为 "${newFirstName} ${newLastName} "` ) }) </script > <template > <input v-model ="firstName" placeholder ="输入姓氏" > <input v-model ="lastName" placeholder ="输入名字" > </template >
⭐⭐ 使用 getter 函数监听 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script setup > import { ref, watch } from 'vue' const firstName = ref ('李' )const lastName = ref ('四' )watch (() => `${firstName.value} ${lastName.value} ` , (newName, oldName ) => { console .log (`姓名从 "${oldName} " 更改为 "${newName} "` ) }) </script > <template > <input v-model ="firstName" placeholder ="输入姓氏" > <input v-model ="lastName" placeholder ="输入名字" > </template >
⭐⭐ 立即执行监听器 1 2 3 4 5 6 7 8 9 10 11 12 13 <script setup > import { ref, watch } from 'vue' const message = ref ('Hello World' )watch (message, (newVal, oldVal ) => { console .log (`message changed from ${oldVal} to ${newVal} ` ) }, { immediate : true }) </script > <template > <input v-model ="message" placeholder ="修改消息" > </template >
⭐⭐ 监听深层对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <script setup > import { reactive, watch } from 'vue' const user = reactive ({ profile : { name : '王五' , age : 28 , address : { city : '北京' } } }) watch (() => user.profile , (newVal, oldVal ) => { console .log ('profile changed:' , newVal, oldVal) }, { deep : true }) </script > <template > <input v-model ="user.profile.name" placeholder ="输入姓名" > <input v-model ="user.profile.address.city" placeholder ="输入城市" > </template >
生命周期函数 生命周期钩子函数以函数形式提供, 不再使用选项式 API 中的 mounted、 created 等写法。 这些钩子函数必须在组件的 setup()
函数或 <script setup>
中调用。
函数概述
钩子函数 触发时机
onBeforeMount 组件挂载之前
onMounted 组件挂载完成后
onBeforeUpdate 数据更新前, 模板尚未重新渲染
onUpdated 数据更新后, 模板已重新渲染
onBeforeUnmount 组件卸载前
onUnmounted 组件卸载后
onActivated 被 缓存的组件激活时调用
onDeactivated 被 缓存的组件停用时调用
onErrorCaptured 捕获子孙组件错误
使用示例 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 <script setup > import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured } from 'vue' onBeforeMount (() => { console .log ('组件即将挂载' ) }) onMounted (() => { console .log ('组件已挂载' ) }) onBeforeUpdate (() => { console .log ('数据已更新, DOM 尚未更新' ) }) onUpdated (() => { console .log ('DOM 已更新' ) }) onBeforeUnmount (() => { console .log ('组件即将卸载, 清理定时器' ) }) onUnmounted (() => { console .log ('组件已卸载' ) }) onActivated (() => { console .log ('组件被激活' ) }) onDeactivated (() => { console .log ('组件被停用' ) }) onErrorCaptured ((err, instance, info ) => { console .error ('捕获到错误:' , err, info) }) </script >
组件通信 父传子 props
是父子组件之间传递数据的标准方式。
⭐⭐ 父组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template > <div > <h2 > 父组件</h2 > <ChildComponent :message ="parentMessage" :count ="parentCount" /> </div > </template > <script setup > import { ref } from 'vue' import ChildComponent from './ChildComponent.vue' const parentMessage = ref ('你好, 子组件! ' )const parentCount = ref (10 )</script >
⭐⭐ 子组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template > <div > <p > 来自父组件的消息: {{ message }}</p > <p > 来自父组件的数字: {{ count }}</p > </div > </template > <script setup > import { defineProps } from 'vue' const props = defineProps ({ message : { type : String , required : true }, count : { type : Number , default : 0 } }) </script >
子传父 子组件通过 $emit
触发自定义事件, 父组件监听该事件并接收数据。
⭐⭐ 子组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <template > <button @click ="sendToParent" > 点击发送消息给父组件</button > </template > <script setup > import { defineEmits } from 'vue' const emit = defineEmits (['update' ])function sendToParent ( ) { emit ('update' , '这是来自子组件的消息' ) } </script >
⭐⭐ 父组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template > <div > <h2 > 父组件</h2 > <ChildComponent @update ="handleUpdate" /> <p > 接收到子组件的消息: {{ childMessage }}</p > </div > </template > <script setup > import { ref } from 'vue' import ChildComponent from './ChildComponent.vue' const childMessage = ref ('' )function handleUpdate (msg ) { childMessage.value = msg } </script >
跨层级通信 provide
和 inject
用于跨越多个组件层级传递数据, 适合全局状态共享( 如主题、 用户信息等) 。
⭐⭐ 根组件 1 2 3 4 5 6 7 8 9 10 11 12 13 <template > <div id ="app" > <LevelOne /> </div > </template > <script setup > import { provide, ref } from 'vue' import LevelOne from './LevelOne.vue' const theme = ref ('dark' )provide ('theme' , theme)</script >
⭐⭐ 一层组件 1 2 3 4 5 6 7 8 <template > <LevelTwo /> </template > <script setup > import LevelTwo from './LevelTwo.vue' </script >
⭐⭐ 二层组件 1 2 3 4 5 6 7 8 9 10 11 12 13 <template > <div :class ="themeClass" > 当前主题: {{ theme }} </div > </template > <script setup > import { inject } from 'vue' const theme = inject ('theme' )const themeClass = computed (() => `theme-${theme.value} ` )</script >
模版引用 模板引用( Template Refs) 提供了一种直接访问 DOM 元素或子组件实例的方法。 通过 ref 属性标记目标元素或组件, 并在组合式 API 中使用 ref 函数来声明对应的引用变量。
引用DOM元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template > <button @click ="focusInput" > 聚焦输入框</button > <input ref ="inputField" placeholder ="点击按钮聚焦我" > </template > <script setup > import { ref } from 'vue' const inputField = ref (null )function focusInput ( ) { if (inputField.value ) { inputField.value .focus () } } </script >
引用子组件 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 <template > <p > {{ message }}</p > </template > <script setup > import { ref } from 'vue' const message = ref ('Hello from Child Component' )defineExpose ({ message }) </script > <template > <ChildComponent ref ="childComponent" /> <button @click ="logMessage" > 查看子组件消息</button > </template > <script setup > import { ref } from 'vue' import ChildComponent from './ChildComponent.vue' const childComponent = ref (null )function logMessage ( ) { console .log (childComponent.value .message ) } </script >
结合生命周期函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template > <div ref ="contentBox" > 内容盒子</div > </template > <script setup > import { ref, onMounted } from 'vue' const contentBox = ref (null )onMounted (() => { if (contentBox.value ) { console .log ('Content box 宽度:' , contentBox.value .offsetWidth ) } }) </script >
编译器宏 Vue 3 的 组合式 API( Composition API) 中, 尤其是在使用 <script setup>
语法时, 我们可以通过一些内置的编译器宏来定义组件的行为, 比如 props、 emits、 expose 等。
defineProps 用于声明组件接收的 props。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script setup > const props = defineProps ({ title : String , count : { type : Number , default : 0 } }) </script > <template > <h1 > {{ title }}</h1 > <p > Count: {{ count }}</p > </template >
defineEmits 用于声明组件会触发的事件。
1 2 3 4 5 6 7 8 9 10 11 <script setup > const emit = defineEmits (['update:title' , 'submit' ])function onSubmit ( ) { emit ('submit' , { data : 'Form submitted' }) } </script > <template > <button @click ="onSubmit" > Submit</button > </template >
defineExpose 暴露组件内部方法或变量供父组件通过 ref 调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <script setup > function sayHello ( ) { console .log ('Hello from child' ) } defineExpose ({ sayHello })</script > <template > <ChildComponent ref ="childRef" /> <button @click ="callChildMethod" > Call Child Method</button > </template > <script setup > import { ref } from 'vue' const childRef = ref ()function callChildMethod ( ) { childRef.value ?.sayHello () } </script >
defineOptions 用于设置组件的配置选项, 如 name、 props、 emits 等。 如果使用了 defineOptions 来声明 props 或 emits, 就不能再使用 defineProps 和 defineEmits。
1 2 3 4 5 6 7 <script setup > defineOptions ({ name : 'MyComponent' , props : ['title' ], emits : ['submit' ] }) </script >
defineModel 简化 v-model 的双向绑定定义( Vue 3.4+ 引入) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script setup > const firstName = defineModel ('firstName' )const lastName = defineModel ('lastName' )</script > <template > <input v-model ="firstName" > <input v-model ="lastName" > </template > <template > <ChildComponent v-model:firstName ="first" v-model:lastName ="last" /> </template >
withDefaults 为 defineProps 提供默认值。
1 2 3 4 5 6 7 8 9 10 11 <script setup > const props = withDefaults ( defineProps<{ title : string count?: number }>(), { count : 0 } ) </script >
defineSlots 获取插槽信息( 不推荐手动使用, 一般通过 <slot>
使用即可) 。
1 2 3 4 <script setup > const slots = defineSlots ()console .log (slots.default ? 'Default slot exists' : 'No default slot' )</script >
路由管理 安装路由 1 npm install vue-router@4
路由配置 ⭐⭐ 创建路由 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 import { createApp } from 'vue' import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import About from '../views/About.vue' const router = createRouter ({ history : createWebHistory (), routes : [ { path : '/' , name : 'Home' , component : Home }, { path : '/about' , name : 'About' , component : About } ] }) export default router
⭐⭐ 注册路由 1 2 3 4 5 6 7 import { createApp } from 'vue' import App from './App.vue' import router from './router' const app = createApp (App )app.use (router) app.mount ('#app' )
⭐⭐ App.vue 1 2 3 4 5 6 7 8 9 10 11 <template > <div id ="app" > <router-link to ="/" > 首页</router-link > <router-link to ="/about" > 关于我们</router-link > <router-view > </router-view > </div > </template >
路由模式 history 配置决定了路由如何管理 URL。
⭐⭐ HTML5 History 模式 它使用浏览器的 history API 来实现 URL 导航而不重新加载页面。 适用于现代 Web 应用, 尤其是单页应用( SPA) 。
1 2 3 4 5 6 7 8 import { createRouter, createWebHistory } from 'vue-router' const router = createRouter ({ history : createWebHistory (), routes : [ ] })
⭐⭐ Hash 模式 使用 URL 的 hash 部分( 即 # 后面的部分) 来模拟一个完整的 URL, 以便在不支持 HTML5 History API 的浏览器中进行兼容。
1 2 3 4 5 6 7 8 import { createRouter, createWebHashHistory } from 'vue-router' const router = createRouter ({ history : createWebHashHistory (), routes : [ ] })
基路径配置 base 配置项定义了应用的基础路径。 如果你的应用被部署在一个子路径下, 则需要通过这个选项指定该子路径。 例如, 如果你的应用部署在 http://example.com/my-app/
, 那么 base 应该设置为 /my-app/
。
⭐⭐ 直接配置 1 2 3 4 5 6 7 8 import { createRouter, createWebHistory } from 'vue-router' const router = createRouter ({ history : createWebHistory ('/my-app/' ), routes : [ ] })
⭐⭐ 环境变量配置 1 2 3 4 5 6 7 import { defineConfig } from 'vite' ;import vue from '@vitejs/plugin-vue' ;export default defineConfig ({ base : '/my-app/' , });
1 2 3 4 5 6 7 8 9 import { createRouter, createWebHistory } from 'vue-router' const router = createRouter ({ history : createWebHistory (import .meta .env .BASE_UR ), routes : [ ] })
导航守卫 ⭐⭐ 全局前置守卫 这是最常用的全局守卫, 适用于整个应用的每个路由跳转。 可以在路由配置文件中通过 router.beforeEach 方法注册一个全局前置守卫。
1 2 3 4 5 6 7 router.beforeEach ((to, from ) => { return true ; });
⭐⭐ 全局解析守卫 类似于 beforeEach, 但是它确保在导航被确认之前, 所有组件内守卫和异步路由组件都已经被解析。
1 2 3 4 5 router.beforeResolve (async (to) => { if (to.meta .requiresCamera ) { await askForCameraPermission (); } });
⭐⭐ 全局后置钩子 这些钩子不会改变导航本身, 通常用于分析、 更改页面标题等辅助功能。
1 2 3 router.afterEach ((to, from ) => { document .title = to.meta .title || 'Default Title' ; });
⭐⭐ 路由独享守卫 可以在路由配置中为特定路由定义 beforeEnter 守卫, 只对该路由生效。
1 2 3 4 5 6 7 8 9 const routes = [ { path : '/users/:id' , component : UserDetails , beforeEnter : (to, from ) => { }, }, ];
⭐⭐ 组件内守卫 这些守卫是定义在组件内部的, 允许你控制组件自身的进入和离开行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 export default { name : 'MyComponent' , beforeRouteEnter (to, from , next ) { next (); }, beforeRouteUpdate (to, from , next ) { next (); }, beforeRouteLeave (to, from , next ) { next (); }, };
状态管理 Vue 3 推荐使用 Pinia 状态管理库, 它提供了一个更简单、 更直观的方式来管理应用的状态。
基本使用 ⭐⭐ 安装 pinia 组件库
⭐⭐ 定义 Store 1 2 3 4 5 6 7 8 9 10 11 12 13 import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const app = createApp (App )const pinia = createPinia ()app.use (pinia) app.mount ('#app' )
⭐⭐ 定义 Store 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { defineStore } from 'pinia' export const useMainStore = defineStore ('main' , { state : () => ({ count : 0 , name : 'Pinia' }), getters : { doubleCount : (state ) => state.count * 2 , fullName : (state ) => `${state.name} Store` }, actions : { increment ( ) { this .count ++ }, updateName (newName ) { this .name = newName } } })
⭐⭐ 定义 Store 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template > <div > <p > {{ store.count }}</p > <p > {{ store.doubleCount }}</p > <button @click ="store.increment" > Increment</button > </div > </template > <script > import { useMainStore } from './stores/main' import { onMounted } from 'vue' export default { setup ( ) { const store = useMainStore () onMounted (() => { console .log (store.fullName ) }) return { store } } } </script >
Store Store 是 Pinia 中最基本的概念, 用来保存状态( state) 、 计算属性( getters) 和修改状态的方法( actions) 。 天然模块化, 每个 store 是一个模块, 不需要手动配置命名空间。
⭐⭐ 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import { defineStore } from 'pinia' export const useCounterStore = defineStore ('counter' , { state : () => ({ count : 0 , }), getters : { doubleCount : (state ) => state.count * 2 , }, actions : { increment ( ) { this .count ++ }, decrement ( ) { this .count -- } } })
⭐⭐ 组合式API写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { defineStore } from 'pinia' import { ref, computed } from 'vue' export const useCounterStore = defineStore ('counter' , () => { const count = ref (0 ) const doubleCount = computed (() => count.value * 2 ) function increment ( ) { count.value ++ } function decrement ( ) { count.value -- } return { count, doubleCount, increment, decrement } })
⭐⭐ 在组件中使用 1 2 3 4 5 6 7 8 9 10 11 12 13 <template > <div > <p > 当前计数: {{ counter.count }}</p > <p > 双倍计数: {{ counter.doubleCount }}</p > <button @click ="counter.increment()" > +1</button > <button @click ="counter.decrement()" > -1</button > </div > </template > <script setup > import { useCounterStore } from '@/stores/counterStore' const counter = useCounterStore ()</script >
⭐⭐ 重置 Store 1 2 const userStore = useUserStore ()userStore.$reset()
⭐⭐ 订阅状态变化 1 2 3 store.$subscribe((mutation, state ) => { console .log ('状态发生了变化:' , state) })
统一导出 ⭐⭐ 目录结构 1 2 3 4 5 6 7 8 src/ ├── stores/ │ ├── index.js // 入口文件( 可选) │ ├── counterStore.js │ ├── userStore.js │ └── todoStore.js ├── views/ └── components/
⭐⭐ 统一导出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 export * from './counterStore' export * from './userStore' export * from './todoStore' import { useCounterStore } from './counterStore' import { useUserStore } from './userStore' export default { useCounterStore, useUserStore }
⭐⭐ 在组件中使用 1 2 3 4 5 6 7 8 9 10 11 12 13 import { useCounterStore, useUserStore } from '@/stores' import * as stores from '@/stores' const counter = stores.useCounterStore ()const user = stores.useUserStore ()import stores from '@/stores' const counter = stores.useCounterStore ()const user = stores.useUserStore ()
数据解构 在 Pinia 中, 解构 Store 的数据( state、 getters、 actions) 是一个常见的操作, 尤其是在组件中使用时。 但如果不注意, 可能会导致 响应性丢失。
⭐⭐ 安全解构 1 2 3 4 5 6 7 8 9 10 11 <script setup > import { storeToRefs } from 'pinia' import { useCounterStore } from '@/stores/counterStore' const counter = useCounterStore ()const { count } = storeToRefs (counter)const { doubleCount, increment } = counter</script >
⭐⭐ 直接调用 1 2 3 4 5 6 7 8 9 10 11 <script setup > import { useCounterStore } from '@/stores/counterStore' const counter = useCounterStore ()</script > <template > <p > 当前计数: {{ counter.count }}</p > <p > 双倍计数: {{ counter.doubleCount }}</p > <button @click ="counter.increment()" > +1</button > </template >
⭐⭐ 组合式 API 写法中的解构 如果使用的是 Composition API 风格定义的 store( 即使用 ref, computed 等返回值) , 可以直接解构, 因为这些值本身就是响应式的。
1 2 3 4 5 6 7 8 9 10 11 export const useCounterStore = defineStore ('counter' , () => { const count = ref (0 ) const doubleCount = computed (() => count.value * 2 ) function increment ( ) { count.value ++ } return { count, doubleCount, increment } })
1 2 3 4 5 6 7 8 9 10 11 <script setup > import { useCounterStore } from '@/stores/counterStore' const { count, doubleCount, increment } = useCounterStore ()</script > <template > <p > {{ count }}</p > <p > {{ doubleCount }}</p > <button @click ="increment" > +1</button > </template >
数据持久化 默认情况下, 刷新页面后状态会丢失。 我们可以使用 pinia-plugin-persistedstate
插件实现持久化。
⭐⭐ 安装插件 1 npm install pinia-plugin-persistedstate
⭐⭐ 配置插件 1 2 3 4 5 6 7 8 9 10 11 import { createApp } from 'vue' import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia ()pinia.use (piniaPluginPersistedstate) const app = createApp (App )app.use (pinia) app.mount ('#app' )
⭐⭐ 在 Store 中启用持久化 1 2 3 4 5 6 7 export const useUserStore = defineStore ('user' , { state : () => ({ username : '' , token : null }), persist : true , })
⭐⭐ 更换持久化储存仓库 1 2 3 4 5 6 7 8 9 export const useUserStore = defineStore ('user' , { state : () => ({ username : '' , token : null }), persist : { storage : localStorage , } })
监听状态变化 Pinia 的状态本质上是响应式的, 因此可以像监听普通响应式数据一样监听它们。
1 2 3 4 5 6 7 8 9 10 11 import { watch } from 'vue' import { useCounterStore } from '@/stores/counterStore' const counter = useCounterStore ()watch ( () => counter.count , (newVal ) => { console .log ('count 发生了变化:' , newVal) } )
插件系统 Pinia 提供了插件机制, 允许你在创建 store 之前或之后执行一些操作。
⭐⭐ 日志插件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export const loggerPlugin = (context ) => { console .log ('Pinia 初始化插件...' ) context.pinia .use (() => { return ({ store } ) => { store.$onAction(({ name, store, args } ) => { console .log (`[ACTION] ${store.$id} .${name} 被调用, 参数:` , args) }) store.$subscribe((mutation, state ) => { console .log (`[STATE CHANGE] ${mutation.storeId} ` , state) }) } }) }
⭐⭐ 使用插件 1 2 3 4 5 6 7 8 import { createPinia } from 'pinia' import { loggerPlugin } from './plugins/loggerPlugin' const pinia = createPinia ()pinia.use (loggerPlugin) app.use (pinia)
组件库 Axios Axios 是一个基于 Promise 的 HTTP 客户端, 用于浏览器和 Node.js。 它能够发出请求到 REST endpoints 并且可以与 Vue.js 等框架很好地集成。
中文文档
地址: http://www.axios-js.com/zh-cn/docs
安装组件
基本使用 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 <template > <div > <p > {{ info }}</p > </div > </template > <script > import axios from 'axios' ;export default { data ( ) { return { info : null , }; }, created ( ) { axios .get ('https://api.example.com/data' ) .then (response => (this .info = response.data )) .catch (error => console .error (error)); } }; </script >
创建实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import axios from 'axios' ;const instance = axios.create ({ baseURL : 'https://api.example.com' , timeout : 1000 , headers : {'X-Custom-Header' : 'foobar' } }); instance.get ('/data' ) .then (function (response ) { console .log (response); }) .catch (function (error ) { console .error (error); }); export default instance
添加拦截器 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 import axios from 'axios' ;const instance = axios.create ({ baseURL : 'https://api.example.com' , timeout : 1000 , headers : {'X-Custom-Header' : 'foobar' } }); instance.get ('/data' ) .then (function (response ) { console .log (response); }) .catch (function (error ) { console .error (error); }); instance.interceptors .request .use (function (config ) { return config; }, function (error ) { return Promise .reject (error); }); instance.interceptors .response .use (function (response ) { return response; }, function (error ) { return Promise .reject (error); }); export default instance
Vant-UI Vant 是一个轻量、 可定制的移动端组件库。
中文文档
地址: https://vant-ui.github.io/vant/#/zh-CN
安装组件 1 2 3 4 npm i vant@latest-v2 npm install babel-plugin-import --save-dev
配置插件 1 2 3 4 5 6 7 8 9 10 { "plugins" : [ ["import" , { "libraryName" : "vant" , "libraryDirectory" : "es" , "style" : true }, "vant" ] ] }
全局引入 1 2 3 4 5 6 7 import Vant from 'vant' ;import 'vant/lib/index.css' ;const app = createApp (App );app.use (Vant ); app.mount ('#app' );
使用组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template > <van-button type ="primary" > 主要按钮</van-button > <van-button type ="info" > 信息按钮</van-button > <van-button type ="default" > 默认按钮</van-button > <van-button type ="warning" > 警告按钮</van-button > <van-button type ="danger" > 危险按钮</van-button > </template > <script > import { Button } from 'vant' ;export default { name : 'App' , components : { [Button .name ]: Button , }, }; </script >
Element Plus Element Plus 是一款基于 Vue 3 的 UI 组件库, 专为中后台管理系统设计, 提供丰富的组件、 响应式布局、 主题定制和国际化支持。
中文文档
地址: https://cn.element-plus.org/zh-CN/guide/design.html
安装组件 1 npm install element-plus --save
导入组件 ⭐⭐ 完整引入 如果你对打包后的文件大小不是很在乎, 那么使用完整导入会更方便。
1 2 3 4 5 6 7 8 9 import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp (App )app.use (ElementPlus ) app.mount ('#app' )
⭐⭐ 按需自动导入 需要使用额外的插件来导入要使用的组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 export default defineConfig ({ plugins : [ AutoImport ({ resolvers : [ElementPlusResolver ()], }), Components ({ resolvers : [ElementPlusResolver ()], }), ], })
⭐⭐ 按需手动导入 需要安装 unplugin-element-plus 插件来导入样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export default defineConfig ({ plugins : [ElementPlus ()], }) import { ElButton } from 'element-plus'
使用组件 1 2 3 4 <template > <el-button type ="primary" > 主要按钮</el-button > <el-input v-model ="input" placeholder ="请输入内容" > </el-input > </template >
全局配置 在引入 ElementPlus 时, 可以传入一个包含 size 和 zIndex 属性的全局配置对象。 size 用于设置表单组件的默认尺寸, zIndex 用于设置弹出组件的层级, zIndex 的默认值为 2000。
1 2 3 4 5 6 import { createApp } from 'vue' import ElementPlus from 'element-plus' import App from './App.vue' const app = createApp (App )app.use (ElementPlus , { size : 'small' , zIndex : 3000 })
构建工具 打包部署 ⭐⭐ 配置 1 2 3 4 5 module .exports = { publicPath : './' , outputDir : 'dist' , };
⭐⭐ 打包
⭐⭐ 部署
将打包好的dist文件夹直接部署服务器即可。
Webpack Webpack 是一个强大的模块打包工具, 主要用于现代 JavaScript 应用程序。 它不仅可以处理 JavaScript 文件, 还能通过插件和加载器( loaders) 来转换和管理其他类型的文件, 如 CSS、 图片等。 使用 Vue CLI 构建 VUE 项目, Vue CLI 就是基于 Webpack 构建的, 它为 Vue.js 应用提供了一套开箱即用的标准构建流程。 其底层使用了 Webpack 来处理模块打包、 热更新、 代码分割等核心功能。 并封装了默认配置, 开发者无需手动编写 webpack.config.js 文件即可进行项目构建和开发。
中文文档
地址: https://www.webpackjs.com/concepts
安装 Webpack 1 2 3 4 5 npm init -y npm install --save-dev webpack webpack-cli
基本配置 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 const path = require ('path' );module .exports = { entry : './src/index.js' , output : { filename : 'bundle.js' , path : path.resolve (__dirname, 'dist' ) }, module : { rules : [ { test : /\.js$/ , exclude : /node_modules/ , use : { loader : 'babel-loader' , } } ] } };
加载器 为了能够处理不同类型的文件, 我们需要使用相应的加载器。 比如处理CSS文件可以使用style-loader和css-loader。
1 2 3 4 5 6 7 npm install --save-dev style-loader css-loader { test : /\.css$/, use: ['style-loader' , 'css-loader' ] }
插件 插件用于执行更广泛的任务, 如优化、 资源管理和环境变量注入。 例如, HtmlWebpackPlugin可以帮助我们自动创建HTML文件并将打包好的JS文件自动引入。
1 2 3 4 5 6 7 8 9 10 11 12 npm install --save-dev html-webpack-plugin const HtmlWebpackPlugin = require('html-webpack-plugin' ); module.exports = { // ... 省略其他配置 plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' // 指定模板文件 }) ] };
打包 1 2 3 4 5 6 "scripts" : { "build" : "webpack" }
Vite Vue 3 开始, Vue 官方推荐使用 Vite 作为默认开发工具, Vite 是一个新型的前端构建工具, 它通过原生 ES 模块( ESM) 提供闪电般的冷启动速度, 并支持多种框架( 如 Vue、 React、 Preact 等) 。 Vite 利用了现代浏览器对 ES 模块的原生支持, 避免了传统打包工具( 如 Webpack 或 Rollup) 需要进行的完整打包过程。
中文文档
地址: https://cn.vitejs.dev/guide
核心特性
极速冷启动: 无需打包编译, 直接通过 ESM 加载模块
即时热更新: 在开发模式下, 修改代码后可以实现毫秒级的更新
开箱即用: 内置 TypeScript、 JSX、 CSS 预处理器等支持
插件系统: 可以通过插件扩展功能
生产优化: 在生产环境使用 Rollup 进行打包优化
插件系统 插件是 Vite 的核心扩展机制, 用于处理各种类型的文件、 优化依赖等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import react from '@vitejs/plugin-react' import legacy from '@vitejs/plugin-legacy' export default defineConfig ({ plugins : [ vue (), react (), legacy ({ targets : { chrome : '64' }, additionalLegacyPolyfills : ['regenerator-runtime/runtime' ] }) ] })
CSS 相关配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 export default defineConfig ({ css : { modules : { localsConvention : 'camelCaseOnly' }, postcss : { plugins : { autoprefixer : {} } }, preprocessorOptions : { scss : { additionalData : `@import "./src/variables.scss";` } } } })
全局常量替换 1 2 3 4 5 export default defineConfig ({ define : { __APP_ENV__ : JSON .stringify ('production' ) } })
开发服务器配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 export default defineConfig ({ server : { port : 3000 , open : true , host : '0.0.0.0' , https : false , proxy : { '/api' : { target : 'http://localhost:8080' , changeOrigin : true , rewrite : (path ) => path.replace (/^\/api/ , '' ) } }, hmr : { protocol : 'ws' , port : 24678 } } })
生产构建配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 export default defineConfig ({ build : { target : 'modules' , outDir : 'dist' , assetsDir : 'assets' , assetsInlineLimit : 4096 , cssCodeSplit : true , sourcemap : false , rollupOptions : { input : { main : path.resolve (__dirname, './index.html' ), about : path.resolve (__dirname, './about.html' ) } }, lib : { entry : './lib/main.js' , name : 'MyLib' , fileName : format => `my-lib.${format} .js` } } })
模块解析配置 1 2 3 4 5 6 7 8 export default defineConfig ({ resolve : { alias : { '@' : path.resolve (__dirname, './src' ) }, extensions : ['.js' , '.vue' , '.json' , '.wasm' ] } })
依赖预构建配置 1 2 3 4 5 6 export default defineConfig ({ optimizeDeps : { include : ['lodash-es' , 'axios' ], exclude : ['moment' ] } })
日志级别配置 1 2 3 4 export default defineConfig ({ logLevel : 'info' , clearScreen : false })
环境变量前缀配置 1 2 3 export default defineConfig ({ envPrefix : 'VITE_' })
公共基础路径配置 1 2 3 4 5 export default defineConfig ({ base : '/my-app/' , base : process.env .VITE_MYAPP_BASE_PATH || '/' })
VSCode插件 Vetur Vetur 专为 Vue.js 开发者设计。 它提供了对 .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 { "vetur.experimental.template" : true , "vetur.useWorkspaceDependencies" : false , "vetur.format.defaultFormatter.html" : "prettier" , "vetur.format.defaultFormatter.js" : "prettier" , "vetur.format.defaultFormatter.css" : "prettier" , "vetur.format.defaultFormatter.scss" : "prettier" , "vetur.format.defaultFormatter.less" : "prettier" , "vetur.format.defaultFormatter.postcss" : "prettier" , "vetur.format.options" : { "semi" : false , "singleQuote" : true } , "vetur.validation.template" : true , "vetur.validation.script" : true , "vetur.validation.style" : true }
Vue - Official Vue - Official插件 是 Vue 官方团队为 Visual Studio Code 推出的新一代官方插件, 专为 Vue 3( 特别是使用 TypeScript 和组合式 API) 开发提供全面支持。 这个插件是对旧版 Vetur 的替代方案, 旨在解决 Vetur 在 Vue 3 + TypeScript 支持上的局限性。
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 { "vetur.enabled" : false } { "vue.enable.officialPlugin" : true , "vue.format.defaultFormatter.html" : "prettier" , "vue.format.defaultFormatter.css" : "prettier" , "vue.format.defaultFormatter.js" : "prettier" , "vue.format.defaultFormatter.ts" : "prettier" , "editor.codeActionsOnSave" : { "source.fixAll" : true , "source.fixAll.eslint" : true } , "editor.formatOnSave" : false , "vue.validate.template" : true , "vue.validate.script" : true , "vue.validate.style" : true }
ESLint ESLint 是一个 JavaScript/TypeScript 代码检查工具, 广泛用于 VS Code 中帮助开发者遵循最佳实践、 保持代码风格一致并避免常见错误。
1 2 3 4 5 6 7 8 9 { "editor.codeActionsOnSave" : { "source.fixAll" : true , "source.fixAll.eslint" : true , "source.organizeImports" : true } , "editor.formatOnSave" : false }
学习资源
视频
【 Vue2+Vue3基础入门到实战项目全套教程】 : https://www.bilibili.com/video/BV1HV4y1a7n4
文档
菜鸟教程 VUE2: https://www.runoob.com/vue2/vue-tutorial.html
菜鸟教程 VUE3: https://www.runoob.com/vue3/vue3-tutorial.html
官方文档 VUE2: https://v2.cn.vuejs.org/v2/guide
官方文档 VUE3: https://cn.vuejs.org/guide/introduction.html
官方文档 路由: https://v3.router.vuejs.org/zh