Vue 3 开发速成手册
Vue 3 开发速成手册
- Vue 3 开发速成手册
- 简介
- 关键特性
- 适用场景
- 一、Vue的安装与配置
- 二、Vue的项目结构
- 三、详细解剖Vue的第一个程序HelloWorld
- 1. 使用CDN引入Vue.js
- 2. 创建你的第一个Vue应用
- 四、声明式渲染(Declarative Rendering)
- 1. 基本声明式渲染
- 2. 指令系统
- 3. 计算属性
- 五、模板语法
- 1. 插值
- 1.1 文本插值
- 1.2 原始 HTML
- 1.3 使用 JavaScript 表达式
- 2. 指令
- 3. 条件语句
- 4. 循环语句
- 4.1 单次循环
- 4.2 嵌套循环
- 5. 属性绑定
- 6. 计算属性和侦听器
- 7. 列表渲染
- 8. 表单输入绑定
- 9. 事件处理
- 9.1 **事件绑定语法**:
- 9.2 **为什么要阻止冒泡?**
- 六、组件
- 1. 组件基础
- 2. 组件 Props
- 3. 组件事件
- 4. 插槽
- 4.1 Vue 组件定义部分
- 4.2 插槽(Slot)的工作原理
- 4.3 最终渲染结果
- 5. 动态组件
- 5.1 组件定义部分
- 5.2 Vue 应用配置
- 5.3 模板部分
- 七、Vue3常用指令速查表
- 1.常用指令
- 2.修饰符速查
- 2.1 v-model 修饰符
- 2.2 v-on 修饰符
- 2.3 按键修饰符
- 八、自定义指令
- 1. 什么是自定义指令?
- 2. 基本语法
- 2.1 全局注册
- 2.2 局部注册
- 3. 钩子函数速查表
- 参数说明
- 生命周期流程图
Vue 3 开发速成手册
简介
Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架,由尤雨溪(Evan You)于 2014 年发布。它以其简洁的 API、灵活的组件化开发和高效的性能,迅速成为最受欢迎的前端框架之一。
Vue 的核心目标是帮助开发者更轻松地构建动态、交互式的 Web 应用。它既适合小型项目(如增强静态页面的交互性),也能胜任复杂的企业级单页应用(SPA)。Vue 的设计哲学强调渐进式采用,开发者可以根据需求逐步引入其功能,而不需要一开始就全面重构现有项目。
关键特性
- 响应式数据绑定:自动同步数据与视图。
- 组件化架构:复用 UI 和逻辑。
- 虚拟 DOM:高效渲染更新。
- 丰富的生态系统:路由(Vue Router)、状态管理(Pinia/Vuex)、构建工具(Vite)等。
适用场景
- 单页应用(SPA)
- 动态内容管理
- 交互式仪表盘
- 渐进式 Web 应用(PWA)
一、Vue的安装与配置
Vue环境的搭建参考这篇博客
Vue的安装与配置: https://blog.csdn.net/R_Feynman_/article/details/150268570?spm=1001.2014.3001.5501
二、Vue的项目结构
当你用 npm init vue@latest初始化项目后,面对生成的十几个文件夹和文件很多新手在使用 Vue 脚手架创建项目后,往往会陷入这样的困惑,下面让我们来了解一下里面生成的到底是一些什么
-
.vscode
VS Code 编辑器配置文件夹,可能包含工作区设置或调试配置 -
node_modules
node_modules目录是由 npm install 命令自动生成的,用于存放项目依赖的所有第三方库和工具包。 -
public
存放静态资源文件(如图片、字体等),这些文件会被直接复制到构建输出目录 -
src
核心源代码目录,通常包含:
main.js(入口文件)
App.vue(根组件)
components/(子组件)
assets/(项目资源)
-
.gitignore
Git 版本控制忽略规则文件 -
index.html
项目主 HTML 文件,Vue 应用的挂载点 -
jsconfig.json
JavaScript 项目配置,用于提供代码智能提示和模块解析 -
package-lock.json
锁定安装时的依赖版本(由 npm 自动生成) -
package.json
项目配置文件,包含:
项目元信息
依赖列表
脚本命令(如 dev/build) -
README.md
项目说明文档 -
vite.config.js
Vite 构建工具配置文件
文件/文件夹 | 类型 | 作用描述 |
---|---|---|
.vscode | 文件夹 | VS Code 编辑器特定配置 |
node_modules | 文件夹 | 存储所有安装的 npm 依赖包 |
public | 文件夹 | 存放无需处理的静态资源文件 |
src | 文件夹 | 项目核心源代码目录(组件、逻辑、资源等) |
.gitignore | 文件 | 指定 Git 版本控制应忽略的文件和目录 |
index.html | 文件 | 应用的主 HTML 文件,Vue 应用的挂载点 |
jsconfig.json | 文件 | 定义 JavaScript 项目的编译器选项 |
package-lock.json | 文件 | 锁定依赖版本,确保安装一致性 |
package.json | 文件 | 项目配置和依赖管理文件 |
README.md | 文件 | 项目说明文档 |
vite.config.js | 文件 | Vite 构建工具的配置文件 |
在我们开发的时候我们可以清空src里面的components/(子组件)和 assets/(项目资源)
保留一个App.vue,这样我们就有了一个纯净的Vue项目
接下来在App.vue里面写入如下程序,在main.js里面删除import './assets/main.css'
此时恭喜你,你的第一个vue程序就诞生了
<template><div class="hello"><h1>{{ msg }}</h1></div>
</template><script>
export default {name: "hello",data() {return {msg: "欢迎来到Vue3!",};},
};
</script>
在运行npm run dev
之后就可以见到欢迎来到Vue3!的字样
三、详细解剖Vue的第一个程序HelloWorld
作为Vue.js的初学者,你可能听说过各种复杂的构建工具和脚手架,但在刚开始学习时,最简单直接的方式就是在HTML文件中引入Vue.js的全局文件。
1. 使用CDN引入Vue.js
- 使用unpkg CDN来引入Vue.js(这会引入Vue 3的最新版本):
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
- 如果你想指定版本,可以这样写:
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.js"></script>
2. 创建你的第一个Vue应用
在第二个<script>
标签中,添加以下代码:
const { createApp } = Vue;createApp({data() {return {message: '你好,Vue!'}},template: `<div>{{ message }}</div>`
}).mount('#app');
const { createApp } = Vue;
作用:解构Vue的工厂函数
从全局Vue对象提取createApp
方法,等价于:const createApp = Vue.createApp;
特点:
Vue 3的创建方式(区别于Vue 2的new Vue()
) ,工厂函数模式更灵活
createApp({...})
功能:创建应用实例
配置对象包含:
data()
- 响应式数据源,template
- 视图模板,其他可选选项(methods/computed等)
执行过程:
接收配置对象,初始化响应式系统,编译模板,返回未挂载的应用实例
data() {...}
核心作用:声明响应式数据
接下来给出完整的示例
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>你好,Vue3</title></head><body><div id="app"></div><script src="https://unpkg.com/vue@3/dist/vue.global.js"></script><script>const { createApp } = Vue;createApp({data() {return {message: "你好,Vue3!",};},template: `<div>{{ message }}</div>`,}).mount("#app");</script></body>
</html>
以下是其工作原理:
①. HTML 结构
<div id="app"></div>
是 Vue 应用的挂载点,Vue 会将渲染的内容插入到这个 div 中
②. Vue 引入
<script src="...vue.global.js">
引入了 Vue 3 的全局构建版本,这会暴露Vue
全局变量
③. Vue 应用创建
const { createApp } = Vue;
从 Vue 对象中解构出createApp
方法createApp()
创建了一个 Vue 应用实例
④. 应用配置对象
data()
函数返回一个包含响应式数据的对象,这里定义了message
属性template
定义了应用的模板,使用双大括号{{ }}
语法绑定message
数据
⑤. 挂载应用
.mount("#app")
将 Vue 应用挂载到 id 为 “app” 的 DOM 元素上
四、声明式渲染(Declarative Rendering)
声明式渲染是 Vue.js 的核心思想之一,贯穿了整个 Vue 的设计理念和开发模式。Vue 通过响应式数据绑定和模板语法,让开发者可以声明式地描述 UI 应该是什么样子,而无需手动操作 DOM。
1. 基本声明式渲染
<div id="app">{{ message }}
</div>`
const app = new Vue({el: '#app',data: {message: 'Hello Vue!'}
})
在这个例子中:
• 双大括号 {{ }}是 Vue 的模板语法
• message
是 Vue 实例的 data
对象中的一个属性
• 当 data
中的 message
改变时,视图会自动更新
2. 指令系统
Vue 提供了多种指令来实现声明式渲染:
v-bind (绑定属性)
<div v-bind:title="message">鼠标悬停查看动态绑定的标题
</div>
简写形式
<div :title="message">简写形式
</div>
v-for (列表渲染)
<ul><li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>
3. 计算属性
对于复杂逻辑,可以使用计算属性:
new Vue({el: '#app',data: {firstName: '张',lastName: '三'},computed: {fullName: function() {return this.firstName + ' ' + this.lastName}}
})
五、模板语法
温馨提示这个框架可以把下面的任何一段代码放进来运行
下面的代码为了方便调试,全部都是在HTML文件中引入Vue.js的全局文件的形式的
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue 3 表单绑定示例</title><script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body><!-- 温馨提示这个框架可以把下面的任何一段代码放进来运行 -->
</body>
</html>
1. 插值
1.1 文本插值
使用双大括号 {{ }}(Mustache 语法)进行文本插值:
<div id="app"><p>{{ message }}</p><button @click="change">更改</button></div><script>Vue.createApp({data() {return { message: "Hello Vue!" }},methods: {change() { this.message = "Changed!" }}}).mount('#app')</script>
1.2 原始 HTML
双大括号会将数据解释为普通文本,使用 v-html
指令可以输出真正的 HTML:
<div id="app"><div :class="{ active: isActive }">红色文字</div><button @click="toggle">切换</button></div><script>Vue.createApp({data() { return { isActive: true } },methods: {toggle() { this.isActive = !this.isActive }}}).mount('#app')</script>
1.3 使用 JavaScript 表达式
Vue 支持在模板中使用 JavaScript 表达式:
1.基本运算:{{ number + 1 }}→ 显示 11
2.三元表达式:{{ ok ? ‘YES’ : ‘NO’ }}→ 显示 YES
3.方法调用:{{ message.split(‘’).reverse().join(‘’) }}→ 显示 “olleH”
4.属性绑定中的表达式::id="‘list-’ + id"→ 生成 id=“list-1”
<div id="app"><!-- 基本运算 --><p>数字加1: {{ number + 1 }}</p><!-- 三元表达式 --><p>状态判断: {{ ok ? 'YES' : 'NO' }}</p><!-- 方法调用 --><p>反转字符串: {{ message.split('').reverse().join('') }}</p><!-- 属性绑定中使用表达式 --><div :id="'list-' + id">这个div的ID是: list-{{ id }}</div><!-- 显示原始数据 --><hr><h3>当前数据状态:</h3><p>number: {{ number }}</p><p>ok: {{ ok }}</p><p>message: {{ message }}</p><p>id: {{ id }}</p></div><script>const { createApp } = Vue;createApp({data() {return {number: 10,ok: true,message: 'Hello',id: 1}}}).mount('#app');</script>
2. 指令
指令是带有 v-前缀的特殊 attribute
:
1.条件渲染 (v-if
):
显示/隐藏文本
添加了切换按钮显示当前状态
2.事件监听:
展示了完整语法(v-on:click
)和简写语法(@click
)
点击按钮会触发alert
3.动态参数:
动态绑定属性名和事件名
显示当前使用的属性名和事件名
4.表单输入绑定 (v-model):
实现输入框和数据的双向绑定
实时显示输入内容
<div id="app"><h2>Vue 3 指令演示</h2><!-- 条件渲染 --><div><h3>1. 条件渲染 (v-if)</h3><p v-if="seen">现在你看到我了</p><button @click="seen = !seen">切换显示状态</button><p>当前seen值: {{ seen }}</p></div><!-- 事件监听 --><div><h3>2. 事件监听</h3><button v-on:click="doSomething">点击我 (完整语法)</button><button @click="doSomething">点击我 (简写语法)</button></div><!-- 动态参数 --><div><h3>3. 动态参数</h3><a v-bind:[attributeName]="url">动态绑定href属性的链接</a><br><a @[eventName]="doSomething">动态绑定click事件的链接</a><p>当前attributeName: "{{ attributeName }}"</p><p>当前eventName: "{{ eventName }}"</p></div><!-- 表单输入绑定 --><div><h3>4. 表单输入绑定 (v-model)</h3><input v-model="message" placeholder="编辑我"><p>Message is: {{ message }}</p></div></div><script>const { createApp } = Vue;createApp({data() {return {seen: true,url: 'https://vuejs.org',attributeName: 'href',eventName: 'click',message: ''}},methods: {doSomething() {alert('按钮被点击了!');}}}).mount('#app');</script>
3. 条件语句
1.v-if / v-else
基本用法:
根据 awesome 的值显示不同的标题,添加了切换按钮和状态显示
2.带 key
的模板切换:
使用 key 属性确保输入框在切换时重新渲染,演示了两种不同的登录表单切换
3.v-show
用法:
通过 display 样式控制元素显示/隐藏,对比 v-if
和 v-show
的区别
<div id="app"><h2>Vue 3 条件渲染演示</h2><div><h3>1. v-if / v-else 基本用法</h3><h1 v-if="awesome">Vue is awesome!</h1><h1 v-else>Oh no 😢</h1><button @click="awesome = !awesome">切换状态</button><p>当前awesome值: {{ awesome }}</p></div><div><h3>2. 带key的模板切换</h3><template v-if="loginType === 'username'"><label>Username</label><input placeholder="Enter your username" key="username-input"></template><template v-else><label>Email</label><input placeholder="Enter your email address" key="email-input"></template><br><button @click="toggleLoginType">切换登录类型</button><p>当前登录类型: {{ loginType }}</p></div><div><h3>3. v-show 用法</h3><div v-show="isShow">这个元素会被显示/隐藏</div><button @click="isShow = !isShow">切换显示状态</button><p>当前isShow值: {{ isShow }}</p><p>注意:v-show只是切换display样式,元素始终存在于DOM中</p></div></div><script>const { createApp } = Vue;createApp({data() {return {awesome: true,loginType: 'username',isShow: true}},methods: {toggleLoginType() {this.loginType = this.loginType === 'username' ? 'email' : 'username';}}}).mount('#app');</script>
4. 循环语句
在元素上使用 v-for
指令,根据源数据的数组或对象进行循环渲染元素。
遍历数组:
v-for="(item, index) in items"
遍历对象:
v-for="(value, key, index) in object"
key 的作用: 使用 v-for
渲染列表时,必须为每个项提供一个唯一的 key 属性,以便 Vue 能够识别每个项的唯一性,从而进行高效的 DOM 更新。
4.1 单次循环
<div id="app"><h2>商品列表</h2><ul><li v-for="(product, index) in products" :key="product.id">{{ index + 1 }}. {{ product.name }} - ¥{{ product.price }}</li></ul><h2>用户信息</h2><div v-for="(value, key) in user" :key="key">{{ key }}: {{ value }}</div></div><script>const { createApp } = VuecreateApp({data() {return {products: [{ id: 1, name: '笔记本电脑', price: 5999 },{ id: 2, name: '智能手机', price: 3999 },{ id: 3, name: '无线耳机', price: 299 }],user: {name: '张三',age: 28,email: 'zhangsan@example.com'}}}}).mount('#app')</script>
4.2 嵌套循环
嵌套循环: 可以嵌套使用多个 v-for
来渲染多维数组或对象结构。
<div id="app"><h2>部门员工列表</h2><div v-for="department in departments" :key="department.name"><h3>{{ department.name }}</h3><ul><li v-for="employee in department.employees" :key="employee.id">{{ employee.name }} - {{ employee.position }}</li></ul></div></div><script>const { createApp } = VuecreateApp({data() {return {departments: [{name: '技术部',employees: [{ id: 1, name: '李四', position: '前端工程师' },{ id: 2, name: '王五', position: '后端工程师' }]},{name: '市场部',employees: [{ id: 3, name: '赵六', position: '市场经理' },{ id: 4, name: '钱七', position: '市场专员' }]}]}}}).mount('#app')</script>
5. 属性绑定
Mustache 语法不能作用在 HTML attribute 上,应该使用 v-bind
指令:
v-bind
在处理 class 和 style 时, 表达式除了可以使用字符串之外,还可以是对象或数组。
v-bind:class
可以简写为 :class
。
<div id="app"><!-- 完整语法 --><div v-bind:id="dynamicId">动态ID元素 (完整语法)</div><!-- 简写 --><div :id="dynamicId">动态ID元素 (简写语法)</div><!-- 绑定布尔值 --><button :disabled="isButtonDisabled">可点击按钮 (当前状态: {{ isButtonDisabled ? '禁用' : '可用' }})</button><button @click="toggleButton">切换按钮状态</button><!-- 动态绑定多个值 --><div v-bind="objectOfAttrs">绑定多个属性的元素</div><!-- 显示当前状态 --><p>当前按钮禁用状态: {{ isButtonDisabled }}</p><p>objectOfAttrs 内容: {{ objectOfAttrs }}</p></div><script>const { createApp } = Vue;createApp({data() {return {dynamicId: 'my-id',isButtonDisabled: false,objectOfAttrs: {id: 'container',class: 'wrapper'}}},methods: {toggleButton() {this.isButtonDisabled = !this.isButtonDisabled;}}}).mount('#app');</script>
6. 计算属性和侦听器
1.计算属性:
reversedMessage
自动反转 message
的内容,添加按钮演示响应式更新
2.侦听器:
监听 question
的变化,当检测到问号时自动触发回答
3.异步方法:
模拟异步获取答案的过程,显示"思考中…"状态和最终回答
<div id="app"><h2>Vue 3 计算属性和侦听器演示</h2><div><h3>1. 计算属性</h3><p>原始消息: "{{ message }}"</p><p>计算后反转消息: "{{ reversedMessage }}"</p><button @click="message = message + '!'">添加感叹号</button></div><div><h3>2. 侦听器和异步方法</h3><input v-model="question" placeholder="输入问题(包含问号时自动回答)"><p>回答: {{ answer }}</p><p>(提示:输入包含问号的问题,如"你好吗?")</p></div></div><script>const { createApp } = Vue;createApp({data() {return {message: 'Hello',question: '',answer: '请先输入问题'}},computed: {// 计算属性reversedMessage() {return this.message.split('').reverse().join('')}},watch: {// 侦听器question(newQuestion) {if (newQuestion.includes('?')) {this.getAnswer()}}},methods: {getAnswer() {this.answer = '思考中...'setTimeout(() => {this.answer = '这是对"' + this.question + '"的回答'}, 1000)}}}).mount('#app');</script>
7. 列表渲染
1.数组渲染:
使用v-for
遍历数组,显示索引和内容
使用:key
提高性能,添加了添加/移除项目的按钮
2.对象渲染:
使用v-for
遍历对象,显示键、值和索引
3.范围渲染:
使用v-for
渲染1-10的数字
4.template渲染:
使用<template>
标签渲染多个元素,为每个项目添加分隔线
<div id="app"><h2>Vue 3 列表渲染演示</h2><div><h3>1. 数组渲染 (带索引和key)</h3><ul><li v-for="(item, index) in items" :key="item.id">{{ index }} - {{ item.message }} (ID: {{ item.id }})</li></ul><button @click="addItem">添加项目</button><button @click="removeItem">移除项目</button></div><div><h3>2. 对象渲染 (键值对)</h3><ul><li v-for="(value, key, index) in myObject">{{ index }}. {{ key }}: {{ value }}</li></ul></div><div><h3>3. 范围渲染 (1-10)</h3><div><span v-for="n in 10">{{ n }} </span></div></div><div><h3>4. 使用template渲染多个元素</h3><ul><template v-for="item in items"><li>{{ item.message }}</li><li class="divider" role="presentation"></li></template></ul></div><div><h3>当前数据状态</h3><pre>{{ JSON.stringify({ items, myObject }, null, 2) }}</pre></div></div><script>const { createApp } = Vue;createApp({data() {return {items: [{ id: 1, message: 'Foo' },{ id: 2, message: 'Bar' }],myObject: {title: 'How to do lists in Vue',author: 'Jane Doe',publishedAt: '2025-01-01'},nextId: 3}},methods: {addItem() {this.items.push({id: this.nextId++,message: 'New Item ' + (this.nextId - 1)});},removeItem() {if (this.items.length > 0) {this.items.pop();}}}}).mount('#app');</script>
8. 表单输入绑定
1.文本输入 - 基本的v-model
绑定
2.多行文本 - textarea
的使用
3.单个复选框
- 绑定布尔值
4.多个复选框
- 绑定数组
5.单选按钮 - 绑定单个值
6.选择框 - select和option的使用
7.修饰符:
.lazy
- 在change时而非input时更新
.number
- 自动转为数字类型
.trim
- 自动去除首尾空格
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue 3 表单绑定示例</title><script src="https://unpkg.com/vue@3/dist/vue.global.js"></script><style>div {margin: 15px 0;padding: 10px;border: 1px solid #eee;border-radius: 5px;}input, textarea, select {margin: 5px;padding: 5px;}</style>
</head>
<body><div id="app"><h2>Vue 3 表单绑定演示</h2><!-- 文本输入 --><div><h3>1. 文本输入</h3><input v-model="text" placeholder="编辑我"><p>文本是: {{ text || '空' }}</p></div><!-- 多行文本 --><div><h3>2. 多行文本</h3><textarea v-model="message" placeholder="添加多行文本"></textarea><p>多行文本是: <pre>{{ message || '空' }}</pre></p></div><!-- 单个复选框 --><div><h3>3. 单个复选框</h3><input type="checkbox" id="checkbox" v-model="checked"><label for="checkbox">状态: {{ checked }}</label></div><!-- 多个复选框 --><div><h3>4. 多个复选框</h3><input type="checkbox" id="jack" value="Jack" v-model="checkedNames"><label for="jack">Jack</label><input type="checkbox" id="john" value="John" v-model="checkedNames"><label for="john">John</label><input type="checkbox" id="mike" value="Mike" v-model="checkedNames"><label for="mike">Mike</label><p>选中的名字: {{ checkedNames.length ? checkedNames : '无' }}</p></div><!-- 单选按钮 --><div><h3>5. 单选按钮</h3><input type="radio" id="one" value="One" v-model="picked"><label for="one">One</label><input type="radio" id="two" value="Two" v-model="picked"><label for="two">Two</label><p>选中的是: {{ picked || '无' }}</p></div><!-- 选择框 --><div><h3>6. 选择框</h3><select v-model="selected"><option disabled value="">请选择</option><option>A</option><option>B</option><option>C</option></select><p>选中的是: {{ selected || '无' }}</p></div><!-- 修饰符 --><div><h3>7. 修饰符</h3><div><label>.lazy修饰符 (change时更新):</label><input v-model.lazy="msg"><p>值: {{ msg || '空' }}</p></div><div><label>.number修饰符 (转为数字):</label><input v-model.number="age" type="number"><p>类型: {{ typeof age }}, 值: {{ age }}</p></div><div><label>.trim修饰符 (去除首尾空格):</label><input v-model.trim="trimmedMsg"><p>值: "{{ trimmedMsg || '空' }}"</p></div></div><div><h3>当前所有数据状态</h3><pre>{{ JSON.stringify({text,message,checked,checkedNames,picked,selected,msg,age,trimmedMsg}, null, 2) }}</pre></div></div><script>const { createApp } = Vue;createApp({data() {return {text: '',message: '',checked: false,checkedNames: [],picked: '',selected: '',msg: '',age: 0,trimmedMsg: ''}}}).mount('#app');</script>
</body>
</html>
9. 事件处理
9.1 事件绑定语法:
完整写法:v-on:click="methodName"
简写形式:@click="methodName"
<div id="app"><h2>Vue3 事件处理</h2><p>计数器: {{ count }}</p><!-- 使用v-on:click --><button v-on:click="increment">增加 (v-on语法)</button><!-- 使用@click简写 --><button @click="decrement">减少 (@简写)</button><!-- 带参数的事件 --><button @click="sayHello('Vue3')">打招呼</button><!-- 访问原生DOM事件 --><button @click="showEventInfo($event)">显示事件信息</button><!-- 事件修饰符示例 --><div @click="parentClick" style="padding:20px; background:#eee; margin-top:20px;"><button @click.stop="childClick">阻止冒泡(.stop)</button></div></div><script>const { createApp } = VuecreateApp({data() {return {count: 0}},methods: {increment() {this.count++console.log('计数器增加')},decrement() {this.count--console.log('计数器减少')},sayHello(name) {alert(`你好,${name}!`)},showEventInfo(event) {console.log('事件对象:', event)console.log('触发元素:', event.target)},parentClick() {console.log('父元素点击')},childClick() {console.log('子元素点击(不会冒泡)')}}}).mount('#app')</script>
当点击按钮时,事件会:
1.先触发按钮的childClick
2.然后向上冒泡触发父元素的parentClick
9.2 为什么要阻止冒泡?
有时我们不希望事件向上传播,例如:
1.点击下拉菜单项时,不希望触发关闭菜单的事件
2.点击模态框内容时,不希望触发关闭模态框的事件
3.嵌套的可点击元素需要独立处理各自的事件
<div id="app"><h2>阻止事件冒泡示例</h2><div @click="logEvent('父元素被点击')"><button @click="logEvent('按钮被点击(会冒泡)')">普通按钮</button><button @click.stop="logEvent('按钮被点击(阻止冒泡)')">阻止冒泡按钮(.stop)</button></div><div><p v-for="(log, index) in logs" :key="index">{{ log }}</p></div></div><script>const { createApp } = VuecreateApp({data() {return {logs: []}},methods: {logEvent(message) {this.logs.push(`${new Date().toLocaleTimeString()}: ${message}`);console.log(message);}}}).mount('#app')</script>
六、组件
1. 组件基础
组件是 Vue 的核心功能之一,它允许我们将 UI 划分为独立、可复用的部分。
1. 组件是可复用的 Vue 实例
2. 使用 components
选项注册组件
3. 组件名在模板中使用时通常使用 kebab-case
(短横线分隔命名)
4. 每个组件必须有单个根元素
<div id="app"><greeting></greeting><greeting></greeting></div><script>const { createApp } = Vue;// 定义一个组件const Greeting = {template: `<p>你好,我是Vue组件!</p>`};const app = createApp({// 注册组件components: {Greeting}});app.mount('#app');</script>
kebab-case 是一种常见的编程命名约定,在计算机科学与软件开发的诸多领域中被广泛应用。 它得名于英文中的 kebab,即烤肉串。 之所以命名为 kebab-case,是因为这种命名方式的形式像一串连在一起的词语,通过短横线(或称为连字符)相连接,形似烤肉串中的各个肉块被串在一起。 例如,字符串hello-world就是 kebab-case 的一个典型例子。
2. 组件 Props
Props
是父组件向子组件传递数据的方式
1. Props
是组件的自定义属性
2. 子组件通过 props
选项声明它接受的属性
3. 父组件通过属性方式传递数据
4. 推荐使用 kebab-case
命名属性(HTML 特性不区分大小写)
<div id="app"><user-card name="张三" age="25"job="前端开发"></user-card><user-card name="李四" age="30"job="UI设计师"></user-card></div><script>const { createApp } = Vue;const UserCard = {props: ['name', 'age', 'job'],template: `<div><h3>{{ name }}</h3><p>年龄: {{ age }}</p><p>职业: {{ job }}</p></div>`};const app = createApp({components: {UserCard}});app.mount('#app');</script>
父组件是 app(根组件),它被挂载到 #app元素上。父组件中注册了 UserCard组件。
子组件是 UserCard,它通过 props接收父组件传递的数据(name、age、job)。
具体关系:
• 父组件(根组件)在模板中使用了<user-card>
标签
• 子组件 UserCard
通过 props
接收父组件传递的数据
• 父组件向子组件传递数据的方式是通过 <user-card>
标签的属性(name=“张三” 等)
总结:
- 父:挂载到 #app的根组件
- 子:UserCard组件
3. 组件事件
子组件可以通过事件与父组件通信
1. 子组件通过 $emit
触发事件
2. 父组件通过 v-on
或 @ 监听子组件事件
3. 事件名推荐使用 kebab-case
4. 可以传递参数:$emit('event-name', arg1, arg2)
<div id="app"><p>计数器: {{ count }}</p><counter-button @increment="count++"></counter-button></div><script>const { createApp } = Vue;const CounterButton = {template: `<button @click="$emit('increment')">增加计数</button>`};const app = createApp({data() {return {count: 0}},components: {CounterButton}});app.mount('#app');</script>
4. 插槽
插槽允许组件接收模板片段作为内容。
1. <slot>
元素作为内容分发的出口
2. 父组件模板中放入组件标签之间的内容会替换子组件的 <slot>
3. 可以有默认内容:<slot>
默认内容</slot>
4. 具名插槽允许有多个插槽
<div id="app"><alert-box>这是一个重要的消息!</alert-box><alert-box><strong>警告!</strong> 系统即将关闭。</alert-box></div><script>const { createApp } = Vue;const AlertBox = {template: `<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;"><slot></slot></div>`};const app = createApp({components: {AlertBox}});app.mount('#app');</script>
接下来让我们仔细看看插槽到底是怎么渲染的
4.1 Vue 组件定义部分
const AlertBox = {template: `<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;"><slot></slot></div>`
};
• 定义了一个名为 AlertBox
的组件
• 组件模板包含一个带有边框样式的 div
• <slot> </slot>
是插槽的位置,这里的内容会被父组件中 <alert-box>
标签内的内容替换
4.2 插槽(Slot)的工作原理
-
父组件(这里就是 #app)在 标签内放置内容
-
子组件(AlertBox)通过 标签定义内容插入的位置
-
Vue 会自动将父组件中的内容插入到子组件的 位置
4.3 最终渲染结果
第一个 <alert-box>
会渲染为:
<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;">这是一个重要的消息!
</div>
第二个 <alert-box>
会渲染为:
<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;"><strong>警告!</strong> 系统即将关闭。
</div>
插槽让组件更加灵活,可以:
- 在组件内部预留位置,由使用者决定插入什么内容
- 创建可复用的布局组件
- 允许组件接收复杂的HTML结构而不仅仅是简单的数据
5. 动态组件
动态组件可以在多个组件间动态切换。
1. 使用 <component :is="...">
实现动态组件
2. is
的值可以是已注册组件的名字或组件选项对象
3. 动态切换组件时,默认会销毁和重新创建组件实例
4. 可以使用 <keep-alive>
缓存不活动的组件实例
<!DOCTYPE html>
<html>
<head><title>Vue3 动态组件</title><script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body><div id="app"><button @click="currentComponent = 'home'">首页</button><button @click="currentComponent = 'about'">关于</button><button @click="currentComponent = 'contact'">联系</button><component :is="currentComponent"></component></div><script>const { createApp } = Vue;const Home = {template: `<div>这是首页内容</div>`};const About = {template: `<div>这是关于我们的内容</div>`};const Contact = {template: `<div>这是联系方式</div>`};const app = createApp({data() {return {currentComponent: 'home'}},components: {Home,About,Contact}});app.mount('#app');</script>
</body>
</html>
5.1 组件定义部分
const Home = {template: `<div>这是首页内容</div>`
};const About = {template: `<div>这是关于我们的内容</div>`
};const Contact = {template: `<div>这是联系方式</div>`
};
定义了三个简单的组件:Home
、About
和 Contact
。
5.2 Vue 应用配置
const app = createApp({data() {return {currentComponent: 'home' // 默认显示Home组件}},components: {Home,About,Contact}
});
• 在 data 中定义了 currentComponent
变量,初始值为 ‘home’
• 注册了三个组件
5.3 模板部分
<button @click="currentComponent = 'home'">首页</button>
<button @click="currentComponent = 'about'">关于</button>
<button @click="currentComponent = 'contact'">联系</button><component :is="currentComponent"></component>
• 三个按钮分别点击时修改 currentComponent
的值
• <component :is="currentComponent">
会根据 currentComponent
的值动态渲染对应的组件
从用户体验角度:推荐组件放在按钮后
即<component :is="currentComponent"></component>
放在前三个按钮的后面
七、Vue3常用指令速查表
1.常用指令
指令 | 缩写 | 说明 | 示例 |
---|---|---|---|
v-text | - | 更新元素的文本内容 | <span v-text="message"></span> |
v-html | - | 更新元素的innerHTML | <div v-html="rawHtml"></div> |
v-show | - | 根据条件显示/隐藏元素(display切换) | <div v-show="isVisible">内容</div> |
v-if | - | 条件渲染元素(销毁/重建) | <div v-if="isActive">内容</div> |
v-else | - | 必须跟在v-if/v-else-if后 | <div v-else>其他内容</div> |
v-else-if | - | v-if的"else if"块 | <div v-else-if="type === 'B'">B</div> |
v-for | - | 循环渲染元素 | <li v-for="item in items" :key="item.id">{{ item.text }}</li> |
v-on | @ | 绑定事件监听器 | <button @click="doThis">点击</button> |
v-bind | : | 动态绑定属性 | <img :src="imageSrc"> |
v-model | - | 表单输入双向绑定 | <input v-model="message"> |
v-slot | # | 定义插槽内容 | <template #header><h1>标题</h1></template> |
v-pre | - | 跳过编译 | <div v-pre>{{ 这里不编译 }}</div> |
v-once | - | 只渲染一次 | <span v-once>{{ 不会改变 }}</span> |
v-memo | - | 缓存模板子树 | <div v-memo="[valueA, valueB]">...</div> |
v-cloak | - | 隐藏未编译的模板 | [v-cloak] { display: none } |
2.修饰符速查
2.1 v-model 修饰符
修饰符 | 说明 | 示例 |
---|---|---|
.lazy | 改为change事件触发 | <input v-model.lazy="msg"> |
.number | 输入转为数字 | <input v-model.number="age"> |
.trim | 去除首尾空格 | <input v-model.trim="text"> |
2.2 v-on 修饰符
修饰符 | 说明 | 示例 |
---|---|---|
.stop | 阻止事件冒泡 | <button @click.stop="doThis"> |
.prevent | 阻止默认行为 | <form @submit.prevent="onSubmit"> |
.capture | 使用捕获模式 | <div @click.capture="doThis"> |
.self | 仅当event.target是元素自身触发 | <div @click.self="doThat"> |
.once | 只触发一次 | <button @click.once="doThis"> |
.passive | 提升滚动性能 | <div @scroll.passive="onScroll"> |
2.3 按键修饰符
修饰符 | 说明 | 示例 |
---|---|---|
.enter | 回车键 | <input @keyup.enter="submit"> |
.tab | Tab键 | <input @keyup.tab="nextField"> |
.delete | 删除/退格键 | <input @keyup.delete="clear"> |
.esc | ESC键 | <input @keyup.esc="cancel"> |
.space | 空格键 | <button @keyup.space="jump"> |
.up | 上箭头 | <input @keyup.up="moveUp"> |
.down | 下箭头 | <input @keyup.down="moveDown"> |
.left | 左箭头 | <input @keyup.left="moveLeft"> |
.right | 右箭头 | <input @keyup.right="moveRight"> |
八、自定义指令
1. 什么是自定义指令?
自定义指令是 Vue 提供的强大功能,允许你直接操作 DOM 元素。当内置指令不够用时,自定义指令可以扩展 Vue 的能力。
2. 基本语法
2.1 全局注册
全局注册可以在任何 Vue 实例的模板中使用
const app = createApp({})// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {mounted(el) {el.focus()}
})
2.2 局部注册
局部注册只能在该组件及其子组件中使用
const MyComponent = {directives: {focus: {mounted(el) {el.focus()}}},template: `<input v-focus>`
}
3. 钩子函数速查表
钩子函数 | 调用时机 | 参数 | 使用场景示例 |
---|---|---|---|
created | 绑定元素的 attribute 或事件监听器应用之前 | el , binding , vnode | 在绑定前修改元素属性 |
beforeMount | 元素被插入到 DOM 前调用 | el , binding , vnode | 准备 DOM 操作前的初始化 |
mounted | 元素被插入到 DOM 后调用 | el , binding , vnode | 最常见的 DOM 操作时机(如添加事件监听) |
beforeUpdate | 在包含组件的 VNode 更新前调用 | el , binding , vnode , prevVnode | 更新前清理旧状态 |
updated | 在包含组件的 VNode 及其子组件的 VNode 更新后调用 | el , binding , vnode , prevVnode | 更新后执行 DOM 操作 |
beforeUnmount | 在绑定元素的父组件卸载前调用 | el , binding , vnode | 清理即将卸载的组件(如定时器) |
unmounted | 在绑定元素的父组件卸载后调用 | el , binding , vnode | 最终清理工作 |
参数说明
el
:指令绑定的 DOM 元素binding
:包含以下属性的对象:value
:指令的绑定值oldValue
:之前的值(仅在update
和componentUpdated
中可用)arg
:指令参数(如v-my-directive:foo
中的foo
)modifiers
:包含修饰符的对象(如v-my-directive.foo.bar
中的{ foo: true, bar: true }
)instance
:使用指令的组件实例
vnode
:Vue 编译生成的虚拟节点prevVnode
:上一个虚拟节点(仅在update
和componentUpdated
钩子中可用)
生命周期流程图
created → beforeMount → mounted → beforeUpdate → updated → beforeUnmount → unmounted
提示:大多数情况下只需使用
mounted
和updated
钩子即可满足需求