

文章目录
- [1. Vue 模板语法基础](#1. Vue 模板语法基础)
-
- [1.1 文本插值](#1.1 文本插值)
- [1.2 原始 HTML](#1.2 原始 HTML)
- [1.3 属性绑定](#1.3 属性绑定)
- [2. Vue 指令系统](#2. Vue 指令系统)
-
- [2.1 条件渲染](#2.1 条件渲染)
- [2.2 列表渲染](#2.2 列表渲染)
- [2.3 事件处理](#2.3 事件处理)
- [2.4 表单输入绑定](#2.4 表单输入绑定)
- [3. 计算属性与侦听器](#3. 计算属性与侦听器)
-
- [3.1 计算属性](#3.1 计算属性)
- [3.2 侦听器](#3.2 侦听器)
- [4. 类与样式绑定](#4. 类与样式绑定)
-
- [4.1 绑定 HTML 类](#4.1 绑定 HTML 类)
- [4.2 绑定内联样式](#4.2 绑定内联样式)
- [5. 条件渲染进阶](#5. 条件渲染进阶)
-
- [5.1 v-if 与模板](#5.1 v-if 与模板)
- [6. 列表渲染进阶](#6. 列表渲染进阶)
-
- [6.1 数组变更检测](#6.1 数组变更检测)
- [6.2 过滤和排序](#6.2 过滤和排序)
- [7. 事件处理进阶](#7. 事件处理进阶)
-
- [7.1 事件修饰符详解](#7.1 事件修饰符详解)
- [7.2 按键修饰符](#7.2 按键修饰符)
正文
1. Vue 模板语法基础
Vue 模板语法允许开发者以声明式的方式将数据渲染到 DOM。
1.1 文本插值
html
<div id="app">
<!-- 基本文本插值 -->
<p>{{ message }}</p>
<!-- 表达式支持 -->
<p>{{ message.split('').reverse().join('') }}</p>
<!-- 数学运算 -->
<p>{{ count + 1 }}</p>
<!-- 三元表达式 -->
<p>{{ isActive ? '激活' : '未激活' }}</p>
</div>
<script>
const app = Vue.createApp({
data() {
return {
message: 'Hello Vue!',
count: 10,
isActive: true
}
}
}).mount('#app')
</script>
1.2 原始 HTML
html
<div id="app">
<!-- 文本插值会将HTML转义 -->
<p>{{ rawHtml }}</p>
<!-- v-html 指令用于输出原始HTML -->
<p v-html="rawHtml"></p>
</div>
<script>
const app = Vue.createApp({
data() {
return {
rawHtml: '<span style="color: red">这是红色文本</span>'
}
}
}).mount('#app')
</script>
1.3 属性绑定
html
<div id="app">
<!-- 绑定HTML属性 -->
<div v-bind:id="dynamicId">动态ID元素</div>
<!-- 简写语法 -->
<img :src="imageSrc" :alt="imageAlt">
<!-- 布尔属性 -->
<button :disabled="isButtonDisabled">按钮</button>
<!-- 动态绑定多个属性 -->
<div v-bind="objectOfAttrs">多属性绑定</div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
dynamicId: 'my-element',
imageSrc: 'https://example.com/image.jpg',
imageAlt: '示例图片',
isButtonDisabled: true,
objectOfAttrs: {
id: 'container',
class: 'wrapper',
style: 'color: blue'
}
}
}
}).mount('#app')
</script>
2. Vue 指令系统
2.1 条件渲染
html
<div id="app">
<!-- v-if 条件渲染 -->
<h1 v-if="awesome">Vue 很棒!</h1>
<h1 v-else>哦不 😢</h1>
<!-- v-if/v-else-if/v-else 链 -->
<div v-if="type === 'A'">A类型</div>
<div v-else-if="type === 'B'">B类型</div>
<div v-else-if="type === 'C'">C类型</div>
<div v-else>未知类型</div>
<!-- v-show 切换元素的显示状态 -->
<h1 v-show="isVisible">使用v-show控制显示</h1>
</div>
<script>
const app = Vue.createApp({
data() {
return {
awesome: true,
type: 'B',
isVisible: true
}
}
}).mount('#app')
</script>
2.2 列表渲染
html
<div id="app">
<!-- 基本列表渲染 -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<!-- 带索引的列表渲染 -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.name }}
</li>
</ul>
<!-- 对象属性遍历 -->
<ul>
<li v-for="(value, key, index) in userObject" :key="key">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>
<!-- 遍历数字范围 -->
<span v-for="n in 10" :key="n">{{ n }} </span>
</div>
<script>
const app = Vue.createApp({
data() {
return {
items: [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
],
userObject: {
name: '张三',
age: 30,
city: '上海'
}
}
}
}).mount('#app')
</script>
2.3 事件处理
html
<div id="app">
<!-- 基本事件绑定 -->
<button v-on:click="counter++">点击计数: {{ counter }}</button>
<!-- 简写语法 -->
<button @click="greet">问候</button>
<!-- 内联处理器 -->
<button @click="say('你好', $event)">说你好</button>
<!-- 事件修饰符 -->
<a @click.stop.prevent="handleLink">阻止默认行为并停止传播</a>
<!-- 按键修饰符 -->
<input @keyup.enter="submitForm">
<!-- 系统修饰键 -->
<div @click.ctrl="handleCtrlClick">Ctrl + 点击</div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
counter: 0
}
},
methods: {
greet() {
alert('你好,Vue!')
},
say(message, event) {
alert(message)
console.log(event)
},
handleLink() {
console.log('链接被点击,但默认行为被阻止')
},
submitForm() {
console.log('表单提交')
},
handleCtrlClick() {
console.log('Ctrl + 点击被触发')
}
}
}).mount('#app')
</script>
2.4 表单输入绑定
html
<div id="app">
<!-- 文本输入 -->
<input v-model="message" placeholder="编辑我">
<p>消息: {{ message }}</p>
<!-- 多行文本 -->
<textarea v-model="description" placeholder="多行输入"></textarea>
<p>描述: {{ description }}</p>
<!-- 复选框 -->
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked ? '已选中' : '未选中' }}</label>
<!-- 多个复选框 -->
<div>
<input type="checkbox" id="apple" value="苹果" v-model="checkedFruits">
<label for="apple">苹果</label>
<input type="checkbox" id="banana" value="香蕉" v-model="checkedFruits">
<label for="banana">香蕉</label>
<input type="checkbox" id="orange" value="橙子" v-model="checkedFruits">
<label for="orange">橙子</label>
</div>
<p>选中的水果: {{ checkedFruits }}</p>
<!-- 单选按钮 -->
<div>
<input type="radio" id="one" value="一" v-model="picked">
<label for="one">一</label>
<input type="radio" id="two" value="二" v-model="picked">
<label for="two">二</label>
</div>
<p>选中的值: {{ picked }}</p>
<!-- 选择框 -->
<select v-model="selected">
<option disabled value="">请选择</option>
<option>北京</option>
<option>上海</option>
<option>广州</option>
</select>
<p>选中的城市: {{ selected }}</p>
<!-- 修饰符 -->
<input v-model.lazy="lazyMessage" placeholder="失焦时才更新">
<p>Lazy消息: {{ lazyMessage }}</p>
<input v-model.number="age" type="number" placeholder="自动转为数字">
<p>年龄: {{ age }} (类型: {{ typeof age }})</p>
<input v-model.trim="trimmedMessage" placeholder="自动去除首尾空格">
<p>去除空格后: "{{ trimmedMessage }}"</p>
</div>
<script>
const app = Vue.createApp({
data() {
return {
message: '',
description: '',
checked: false,
checkedFruits: [],
picked: '',
selected: '',
lazyMessage: '',
age: 0,
trimmedMessage: ''
}
}
}).mount('#app')
</script>
3. 计算属性与侦听器
3.1 计算属性
html
<div id="app">
<!-- 原始数据 -->
<p>原始消息: "{{ message }}"</p>
<!-- 计算属性 -->
<p>反转消息: "{{ reversedMessage }}"</p>
<!-- 方法调用 (每次重新渲染都会调用) -->
<p>方法反转: "{{ reverseMessage() }}"</p>
<!-- 带缓存的计算属性 -->
<p>全名: {{ fullName }}</p>
<button @click="firstName = '李'">修改姓</button>
<button @click="lastName = '四'">修改名</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
message: 'Hello Vue!',
firstName: '张',
lastName: '三'
}
},
computed: {
// 计算属性会基于其依赖进行缓存
reversedMessage() {
console.log('计算属性被执行')
return this.message.split('').reverse().join('')
},
// 带getter和setter的计算属性
fullName: {
get() {
return this.firstName + this.lastName
},
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
},
methods: {
// 方法不会缓存
reverseMessage() {
console.log('方法被调用')
return this.message.split('').reverse().join('')
}
}
}).mount('#app')
</script>
3.2 侦听器
html
<div id="app">
<p>
问一个问题:
<input v-model="question">
</p>
<p>{{ answer }}</p>
<p>
姓: <input v-model="firstName">
名: <input v-model="lastName">
</p>
<p>全名: {{ fullName }}</p>
<!-- 深度监听示例 -->
<button @click="updateUserInfo">更新用户信息</button>
<pre>{{ user }}</pre>
</div>
<script>
const app = Vue.createApp({
data() {
return {
question: '',
answer: '请输入问题',
firstName: '张',
lastName: '三',
fullName: '张三',
user: {
name: '李四',
address: {
city: '北京'
}
}
}
},
watch: {
// 简单侦听器
question(newQuestion, oldQuestion) {
if (newQuestion.includes('?')) {
this.getAnswer()
}
},
// 侦听多个属性变化
firstName(newVal) {
this.fullName = newVal + this.lastName
},
lastName(newVal) {
this.fullName = this.firstName + newVal
},
// 深度侦听对象变化
user: {
handler(newVal) {
console.log('用户信息变化了', newVal)
},
deep: true // 深度监听
},
// 立即执行的侦听器
'user.name': {
handler(newVal) {
console.log('用户名变化了', newVal)
},
immediate: true // 立即执行一次
}
},
methods: {
getAnswer() {
this.answer = '思考中...'
setTimeout(() => {
this.answer = '这是一个很好的问题!'
}, 1000)
},
updateUserInfo() {
this.user.address.city = '上海'
}
}
}).mount('#app')
</script>
4. 类与样式绑定
4.1 绑定 HTML 类
html
<div id="app">
<!-- 对象语法 -->
<div :class="{ active: isActive, 'text-danger': hasError }">
对象语法绑定类
</div>
<!-- 数组语法 -->
<div :class="[activeClass, errorClass]">
数组语法绑定类
</div>
<!-- 数组中使用对象 -->
<div :class="[isActive ? activeClass : '', { error: hasError }]">
混合语法绑定类
</div>
<!-- 组件上使用 -->
<my-component :class="{ active: isActive }"></my-component>
</div>
<script>
const app = Vue.createApp({
data() {
return {
isActive: true,
hasError: false,
activeClass: 'active',
errorClass: 'text-danger'
}
},
components: {
'my-component': {
template: '<p class="base-class">组件</p>'
// 最终渲染: <p class="base-class active">组件</p>
}
}
}).mount('#app')
</script>
4.2 绑定内联样式
html
<div id="app">
<!-- 对象语法 -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">
对象语法绑定样式
</div>
<!-- 直接绑定对象 -->
<div :style="styleObject">
绑定样式对象
</div>
<!-- 数组语法 -->
<div :style="[baseStyles, overridingStyles]">
数组语法绑定多个样式对象
</div>
<!-- 自动添加前缀 -->
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">
自动添加浏览器前缀
</div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
activeColor: 'red',
fontSize: 18,
styleObject: {
color: 'blue',
backgroundColor: '#f0f0f0',
padding: '10px'
},
baseStyles: {
fontWeight: 'bold',
letterSpacing: '1px'
},
overridingStyles: {
color: 'green',
textDecoration: 'underline'
}
}
}
}).mount('#app')
</script>
5. 条件渲染进阶
5.1 v-if 与模板
html
<div id="app">
<!-- 使用template元素包裹多个元素 -->
<template v-if="loginType === 'username'">
<label>用户名</label>
<input placeholder="输入用户名" key="username-input">
</template>
<template v-else>
<label>邮箱</label>
<input placeholder="输入邮箱" key="email-input">
</template>
<button @click="toggleLoginType">切换登录类型</button>
<!-- v-if vs v-show -->
<h1 v-if="showWithIf">v-if: 条件为真时才渲染</h1>
<h1 v-show="showWithShow">v-show: 始终渲染但条件为假时隐藏</h1>
<button @click="toggleShow">切换显示</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
loginType: 'username',
showWithIf: true,
showWithShow: true
}
},
methods: {
toggleLoginType() {
this.loginType = this.loginType === 'username' ? 'email' : 'username'
},
toggleShow() {
this.showWithIf = !this.showWithIf
this.showWithShow = !this.showWithShow
}
}
}).mount('#app')
</script>
6. 列表渲染进阶
6.1 数组变更检测
html
<div id="app">
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ item.name }} - {{ item.price }}元
<button @click="removeItem(index)">删除</button>
</li>
</ul>
<div>
<input v-model="newItemName" placeholder="商品名称">
<input v-model.number="newItemPrice" type="number" placeholder="价格">
<button @click="addItem">添加商品</button>
</div>
<button @click="sortItems">按价格排序</button>
<button @click="reverseItems">反转列表</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
items: [
{ id: 1, name: '苹果', price: 5 },
{ id: 2, name: '香蕉', price: 3 },
{ id: 3, name: '橙子', price: 6 }
],
newItemName: '',
newItemPrice: 0,
nextId: 4
}
},
methods: {
addItem() {
if (this.newItemName && this.newItemPrice > 0) {
this.items.push({
id: this.nextId++,
name: this.newItemName,
price: this.newItemPrice
})
this.newItemName = ''
this.newItemPrice = 0
}
},
removeItem(index) {
this.items.splice(index, 1)
},
sortItems() {
// 变更方法: 会触发视图更新
this.items.sort((a, b) => a.price - b.price)
},
reverseItems() {
// 变更方法: 会触发视图更新
this.items.reverse()
},
updateItem(index) {
// 非变更方法: 需要替换数组才能触发更新
// this.items[index].price += 1 // 不会触发视图更新
// 正确的方式:
this.items[index] = { ...this.items[index], price: this.items[index].price + 1 }
// 或者使用 Vue.set 或 this.$set (Vue 2)
}
}
}).mount('#app')
</script>
6.2 过滤和排序
html
<div id="app">
<input v-model="searchQuery" placeholder="搜索...">
<ul>
<li v-for="item in filteredItems" :key="item.id">
{{ item.name }} - {{ item.price }}元
</li>
</ul>
<div>
<button @click="sortOrder = 'asc'">价格升序</button>
<button @click="sortOrder = 'desc'">价格降序</button>
<button @click="sortOrder = 'none'">原始顺序</button>
</div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
items: [
{ id: 1, name: '苹果', price: 5 },
{ id: 2, name: '香蕉', price: 3 },
{ id: 3, name: '橙子', price: 6 },
{ id: 4, name: '葡萄', price: 8 }
],
searchQuery: '',
sortOrder: 'none'
}
},
computed: {
filteredItems() {
// 先过滤
let result = this.items
if (this.searchQuery) {
const query = this.searchQuery.toLowerCase()
result = result.filter(item =>
item.name.toLowerCase().includes(query)
)
}
// 再排序
if (this.sortOrder !== 'none') {
result = [...result].sort((a, b) => {
return this.sortOrder === 'asc'
? a.price - b.price
: b.price - a.price
})
}
return result
}
}
}).mount('#app')
</script>
7. 事件处理进阶
7.1 事件修饰符详解
html
<div id="app">
<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis">阻止冒泡</a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit">
<input type="text">
<button type="submit">提交</button>
</form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat">阻止默认行为和冒泡</a>
<!-- 只有修饰符 -->
<form @submit.prevent>
<input type="text">
<button type="submit">阻止默认提交</button>
</form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<div @click.capture="doThis">
捕获模式
<button @click="childClick">子元素</button>
</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<div @click.self="doThat">
只有点击这里才触发
<button>点击我不会触发父元素事件</button>
</div>
<!-- 点击事件将只会触发一次 -->
<button @click.once="doOnce">只触发一次</button>
<!-- 滚动事件的默认行为 (滚动) 将立即发生,不等待完成 -->
<div @scroll.passive="onScroll">
被动监听的滚动区域
</div>
</div>
<script>
const app = Vue.createApp({
methods: {
doThis() {
console.log('doThis 被调用')
},
doThat() {
console.log('doThat 被调用')
},
onSubmit() {
console.log('表单提交')
},
childClick() {
console.log('子元素被点击')
},
doOnce() {
console.log('这个处理函数只会触发一次')
},
onScroll() {
console.log('滚动中...')
}
}
}).mount('#app')
</script>
7.2 按键修饰符
html
<div id="app">
<!-- 按下Enter键时调用 -->
<input @keyup.enter="submit">
<!-- 按下Tab键时调用 -->
<input @keyup.tab="handleTab">
<!-- 按下Delete键时调用 -->
<input @keyup.delete="handleDelete">
<!-- 按下Esc键时调用 -->
<input @keyup.esc="handleEscape">
<!-- 按下空格键时调用 -->
<input @keyup.space="handleSpace">
<!-- 按下上方向键时调用 -->
<input @keyup.up="handleUp">
<!-- 按下Page Down键时调用 -->
<input @keyup.page-down="handlePageDown">
<!-- 组合按键 -->
<input @keyup.alt.enter="handleAltEnter">
<!-- Ctrl + 点击 -->
<div @click.ctrl="handleCtrlClick">Ctrl + Click</div>
<!-- 精确的系统修饰符 -->
<button @click.ctrl.exact="onCtrlClick">仅当按下Ctrl时</button>
<button @click.exact="onClickWithoutModifiers">没有任何系统修饰符</button>
</div>
<script>
const app = Vue.createApp({
methods: {
submit() {
console.log('提交表单')
},
handleTab() {
console.log('Tab键被按下')
},
handleDelete() {
console.log('Delete键被按下')
},
handleEscape() {
console.log('Esc键被按下')
},
handleSpace() {
console.log('空格键被按下')
},
handleUp() {
console.log('上方向键被按下')
},
handlePageDown() {
console.log('Page Down键被按下')
},
handleAltEnter() {
console.log('Alt + Enter被按下')
},
handleCtrlClick() {
console.log('Ctrl + Click被触发')
},
onCtrlClick() {
console.log('仅当按下Ctrl时触发')
},
onClickWithoutModifiers() {
console.log('没有任何系统修饰符时触发')
}
}
}).mount('#app')
</script>
结语
感谢您的阅读!期待您的一键三连!欢迎指正!
