Vue2 指令
内置指令总览
| 指令 | 作用 | 简写 |
|---|---|---|
v-text | 更新元素文本内容 | - |
v-html | 更新元素 HTML 内容 | - |
v-show | 切换 display 控制显隐 | - |
v-if / v-else-if / v-else | 条件渲染(惰性) | - |
v-for | 列表渲染 | - |
v-on | 绑定事件监听 | @ |
v-bind | 动态绑定属性 | : |
v-model | 表单双向绑定 | - |
v-cloak | 解决插值表达式闪烁 | - |
v-once | 只渲染一次 | - |
v-pre | 跳过编译,提升性能 | - |
条件渲染
v-if / v-else-if / v-else
vue
<template>
<div>
<h1 v-if="score >= 90">优秀</h1>
<h1 v-else-if="score >= 60">及格</h1>
<h1 v-else>不及格</h1>
</div>
</template><template>
<div>
<h1 v-if="score >= 90">优秀</h1>
<h1 v-else-if="score >= 60">及格</h1>
<h1 v-else>不及格</h1>
</div>
</template>v-if是"真正"的条件渲染,条件为假时元素不会被渲染v-else-if和v-else必须紧跟在v-if之后
v-show
vue
<template>
<h1 v-show="isVisible">我可能显示也可能隐藏</h1>
</template><template>
<h1 v-show="isVisible">我可能显示也可能隐藏</h1>
</template>v-show只是切换display属性,元素始终渲染- 不适合 template 标签使用
v-if vs v-show
| 特性 | v-if | v-show |
|---|---|---|
| 渲染方式 | 条件为假时不渲染 DOM | 始终渲染,切换 display |
| 切换开销 | 高(销毁/重建) | 低(切换 CSS) |
| 初始开销 | 低(条件为假时不渲染) | 高(始终渲染) |
| 适用场景 | 运行时条件变化少的场景 | 频繁切换显隐的场景 |
列表渲染
v-for 基本用法
vue
<template>
<div>
<!-- 遍历数组 -->
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.name }}
</li>
<!-- 遍历对象 -->
<li v-for="(value, key, index) in obj" :key="key">
{{ index }} - {{ key }}: {{ value }}
</li>
<!-- 遍历数字 -->
<li v-for="n in 10" :key="n">{{ n }}</li>
</div>
</template><template>
<div>
<!-- 遍历数组 -->
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.name }}
</li>
<!-- 遍历对象 -->
<li v-for="(value, key, index) in obj" :key="key">
{{ index }} - {{ key }}: {{ value }}
</li>
<!-- 遍历数字 -->
<li v-for="n in 10" :key="n">{{ n }}</li>
</div>
</template>key 的重要性
Vue 通过 key 追踪每个节点的身份,用于高效的 DOM 复用和重新排序。
vue
<template>
<div>
<!-- 建议使用唯一 id,而非 index -->
<li v-for="(item, index) in items" :key="item.id">
{{ item.name }}
</li>
</div>
</template><template>
<div>
<!-- 建议使用唯一 id,而非 index -->
<li v-for="(item, index) in items" :key="item.id">
{{ item.name }}
</li>
</div>
</template>不使用 index 作为 key 的原因: 当列表顺序变化或被删除时,index 会变化,导致 Vue 无法正确复用或重排元素。
数组更新检测
Vue2 只能检测到以下数组变更方法:
js
// 会触发视图更新
this.items.push(item)
this.items.pop()
this.items.shift()
this.items.unshift(item)
this.items.splice(index, 1, newItem)
this.items.sort((a, b) => a - b)
this.items.reverse()
// 不会触发视图更新
this.items[index] = newValue // 需要用 this.$set
this.items.length = newLength // 需要用 this.items.splice(newLength)// 会触发视图更新
this.items.push(item)
this.items.pop()
this.items.shift()
this.items.unshift(item)
this.items.splice(index, 1, newItem)
this.items.sort((a, b) => a - b)
this.items.reverse()
// 不会触发视图更新
this.items[index] = newValue // 需要用 this.$set
this.items.length = newLength // 需要用 this.items.splice(newLength)事件处理
v-on 基础用法
vue
<template>
<div>
<!-- 绑定事件 -->
<button v-on:click="handleClick">点击</button>
<button @click="handleClick">点击(简写)</button>
<!-- 传参 -->
<button @click="say('hello', $event)">传参</button>
<!-- 内联语句 -->
<button @click="count++">+1</button>
<button @click="show = !show">切换</button>
<!-- 动态事件名 -->
<button @[eventName]="handleClick">动态事件</button>
</div>
</template>
<script>
export default {
data() {
return { eventName: 'click', count: 0, show: false }
},
methods: {
handleClick(event) {
console.log('点击了', event.target)
},
say(msg, event) {
console.log(msg, event)
}
}
}
</script><template>
<div>
<!-- 绑定事件 -->
<button v-on:click="handleClick">点击</button>
<button @click="handleClick">点击(简写)</button>
<!-- 传参 -->
<button @click="say('hello', $event)">传参</button>
<!-- 内联语句 -->
<button @click="count++">+1</button>
<button @click="show = !show">切换</button>
<!-- 动态事件名 -->
<button @[eventName]="handleClick">动态事件</button>
</div>
</template>
<script>
export default {
data() {
return { eventName: 'click', count: 0, show: false }
},
methods: {
handleClick(event) {
console.log('点击了', event.target)
},
say(msg, event) {
console.log(msg, event)
}
}
}
</script>事件修饰符
vue
<template>
<div>
<!-- 阻止冒泡 -->
<button @click.stop="doThis">停止冒泡</button>
<!-- 阻止默认行为 -->
<form @submit.prevent="onSubmit">阻止提交</form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis">串联</button>
<!-- 只当点击元素本身触发 -->
<div @click.self="doThat">self</div>
<!-- 事件捕获模式 -->
<div @click.capture="doThis">捕获</div>
<!-- 只触发一次 -->
<button @click.once="doThis">只触发一次</button>
<!-- 组件上监听原生事件 -->
<custom-component @click.native="handleClick">原生事件</custom-component>
</div>
</template><template>
<div>
<!-- 阻止冒泡 -->
<button @click.stop="doThis">停止冒泡</button>
<!-- 阻止默认行为 -->
<form @submit.prevent="onSubmit">阻止提交</form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis">串联</button>
<!-- 只当点击元素本身触发 -->
<div @click.self="doThat">self</div>
<!-- 事件捕获模式 -->
<div @click.capture="doThis">捕获</div>
<!-- 只触发一次 -->
<button @click.once="doThis">只触发一次</button>
<!-- 组件上监听原生事件 -->
<custom-component @click.native="handleClick">原生事件</custom-component>
</div>
</template>按键修饰符
vue
<template>
<div>
<!-- 回车键 -->
<input @keyup.enter="submit">
<!-- 组合键 -->
<input @keyup.ctrl.enter="submitWithCtrl">
<!-- 常用按键别名 -->
<!-- .enter / .tab / .delete / .esc / .space / .up / .down / .left / .right -->
</div>
</template><template>
<div>
<!-- 回车键 -->
<input @keyup.enter="submit">
<!-- 组合键 -->
<input @keyup.ctrl.enter="submitWithCtrl">
<!-- 常用按键别名 -->
<!-- .enter / .tab / .delete / .esc / .space / .up / .down / .left / .right -->
</div>
</template>系统修饰键
vue
<template>
<div>
<!-- 按住 ctrl 点击 -->
<button @click.ctrl="doSomething">Ctrl + Click</button>
<!-- 同时按下 alt + c -->
<input @keyup.alt.67="clear">
<!-- 修饰键:ctrl / alt / shift / meta -->
</div>
</template><template>
<div>
<!-- 按住 ctrl 点击 -->
<button @click.ctrl="doSomething">Ctrl + Click</button>
<!-- 同时按下 alt + c -->
<input @keyup.alt.67="clear">
<!-- 修饰键:ctrl / alt / shift / meta -->
</div>
</template>鼠标修饰符
vue
<template>
<button @click.left="leftClick">鼠标左键</button>
<button @click.right="rightClick">鼠标右键</button>
<button @click.middle="middleClick">鼠标中键</button>
</template><template>
<button @click.left="leftClick">鼠标左键</button>
<button @click.right="rightClick">鼠标右键</button>
<button @click.middle="middleClick">鼠标中键</button>
</template>属性绑定
v-bind 基础
vue
<template>
<div>
<!-- 绑定属性 -->
<img v-bind:src="imageSrc" :alt="altText">
<!-- 动态属性名 -->
<button :[key]="value">动态属性</button>
<!-- 绑定对象 -->
<div v-bind="{ id: someId, 'data-type': type }">对象绑定</div>
</div>
</template><template>
<div>
<!-- 绑定属性 -->
<img v-bind:src="imageSrc" :alt="altText">
<!-- 动态属性名 -->
<button :[key]="value">动态属性</button>
<!-- 绑定对象 -->
<div v-bind="{ id: someId, 'data-type': type }">对象绑定</div>
</div>
</template>动态样式
动态 class
vue
<template>
<div>
<!-- 对象语法 -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
<!-- 数组语法 -->
<div :class="[activeClass, errorClass]"></div>
<!-- 三元表达式 -->
<div :class="[isActive ? 'active' : '']"></div>
<!-- 数组+对象混合 -->
<div :class="[{ active: isActive }, 'static-class']"></div>
<!-- 绑定到组件的根元素上 -->
<custom-component class="custom-class"></custom-component>
</div>
</template>
<script>
export default {
data() {
return {
isActive: true,
hasError: false,
activeClass: 'active',
errorClass: 'text-danger'
}
}
}
</script><template>
<div>
<!-- 对象语法 -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
<!-- 数组语法 -->
<div :class="[activeClass, errorClass]"></div>
<!-- 三元表达式 -->
<div :class="[isActive ? 'active' : '']"></div>
<!-- 数组+对象混合 -->
<div :class="[{ active: isActive }, 'static-class']"></div>
<!-- 绑定到组件的根元素上 -->
<custom-component class="custom-class"></custom-component>
</div>
</template>
<script>
export default {
data() {
return {
isActive: true,
hasError: false,
activeClass: 'active',
errorClass: 'text-danger'
}
}
}
</script>动态 style
vue
<template>
<div>
<!-- 对象语法 -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<!-- 数组语法(多个样式对象) -->
<div :style="[baseStyles, overridingStyles]"></div>
</div>
</template>
<script>
export default {
data() {
return {
activeColor: 'red',
fontSize: 16,
baseStyles: { color: 'red', fontSize: '14px' },
overridingStyles: { fontWeight: 'bold' }
}
}
}
</script><template>
<div>
<!-- 对象语法 -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<!-- 数组语法(多个样式对象) -->
<div :style="[baseStyles, overridingStyles]"></div>
</div>
</template>
<script>
export default {
data() {
return {
activeColor: 'red',
fontSize: 16,
baseStyles: { color: 'red', fontSize: '14px' },
overridingStyles: { fontWeight: 'bold' }
}
}
}
</script>表单绑定
v-model 基础
vue
<template>
<div>
<!-- 文本输入 -->
<input v-model="message" placeholder="输入文字">
<p>{{ message }}</p>
<!-- 多行文本 -->
<textarea v-model="content"></textarea>
<!-- 复选框 -->
<input type="checkbox" v-model="checked">
<div>选中:{{ checked }}</div>
<!-- 多选复选框 -->
<input type="checkbox" value="A" v-model="hobbies">
<input type="checkbox" value="B" v-model="hobbies">
<input type="checkbox" value="C" v-model="hobbies">
<!-- 单选框 -->
<input type="radio" value="男" v-model="gender">
<input type="radio" value="女" v-model="gender">
<!-- 下拉选择 -->
<select v-model="selected">
<option disabled value="">请选择</option>
<option value="A">A</option>
<option value="B">B</option>
</select>
<!-- 多选下拉 -->
<select v-model="selectedList" multiple>
<option v-for="item in options" :key="item.value" :value="item.value">
{{ item.text }}
</option>
</select>
</div>
</template>
<script>
export default {
data() {
return {
message: '',
content: '',
checked: false,
hobbies: [],
gender: '',
selected: '',
selectedList: [],
options: [
{ text: '选项A', value: 'A' },
{ text: '选项B', value: 'B' }
]
}
}
}
</script><template>
<div>
<!-- 文本输入 -->
<input v-model="message" placeholder="输入文字">
<p>{{ message }}</p>
<!-- 多行文本 -->
<textarea v-model="content"></textarea>
<!-- 复选框 -->
<input type="checkbox" v-model="checked">
<div>选中:{{ checked }}</div>
<!-- 多选复选框 -->
<input type="checkbox" value="A" v-model="hobbies">
<input type="checkbox" value="B" v-model="hobbies">
<input type="checkbox" value="C" v-model="hobbies">
<!-- 单选框 -->
<input type="radio" value="男" v-model="gender">
<input type="radio" value="女" v-model="gender">
<!-- 下拉选择 -->
<select v-model="selected">
<option disabled value="">请选择</option>
<option value="A">A</option>
<option value="B">B</option>
</select>
<!-- 多选下拉 -->
<select v-model="selectedList" multiple>
<option v-for="item in options" :key="item.value" :value="item.value">
{{ item.text }}
</option>
</select>
</div>
</template>
<script>
export default {
data() {
return {
message: '',
content: '',
checked: false,
hobbies: [],
gender: '',
selected: '',
selectedList: [],
options: [
{ text: '选项A', value: 'A' },
{ text: '选项B', value: 'B' }
]
}
}
}
</script>表单修饰符
vue
<template>
<div>
<!-- .lazy:在 change 事件时同步(而非 input 事件) -->
<input v-model.lazy="message">
<!-- .number:自动转为数字 -->
<input v-model.number="age" type="number">
<!-- .trim:去除首尾空格 -->
<input v-model.trim="username">
</div>
</template><template>
<div>
<!-- .lazy:在 change 事件时同步(而非 input 事件) -->
<input v-model.lazy="message">
<!-- .number:自动转为数字 -->
<input v-model.number="age" type="number">
<!-- .trim:去除首尾空格 -->
<input v-model.trim="username">
</div>
</template>在组件上使用 v-model
vue
<template>
<!-- 父组件 -->
<custom-input v-model="searchText"></custom-input>
<!-- 等价于:<custom-input :value="searchText" @input="searchText = $event"></custom-input> -->
</template><template>
<!-- 父组件 -->
<custom-input v-model="searchText"></custom-input>
<!-- 等价于:<custom-input :value="searchText" @input="searchText = $event"></custom-input> -->
</template>vue
<!-- 子组件 CustomInput.vue -->
<template>
<input :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: ['value']
}
</script><!-- 子组件 CustomInput.vue -->
<template>
<input :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: ['value']
}
</script>自定义组件的 model
vue
<script>
// 修改 v-model 默认的 prop 和事件名
export default {
model: {
prop: 'checked', // 默认是 value
event: 'change' // 默认是 input
},
props: {
checked: Boolean
}
}
</script><script>
// 修改 v-model 默认的 prop 和事件名
export default {
model: {
prop: 'checked', // 默认是 value
event: 'change' // 默认是 input
},
props: {
checked: Boolean
}
}
</script>v-cloak
解决网速慢时页面显示插值表达式的问题。
css
[v-cloak] {
display: none;
}[v-cloak] {
display: none;
}vue
<template>
<div v-cloak>
{{ message }}
</div>
</template><template>
<div v-cloak>
{{ message }}
</div>
</template>v-once
只渲染一次,数据变化时不会重新渲染。
vue
<template>
<div>
<span v-once>初始值:{{ message }}</span>
<!-- 即使 message 改变,上面的 span 也不会更新 -->
</div>
</template><template>
<div>
<span v-once>初始值:{{ message }}</span>
<!-- 即使 message 改变,上面的 span 也不会更新 -->
</div>
</template>v-pre
跳过元素及其子元素的编译,直接显示原始内容,可提升编译速度。
vue
<template>
<span v-pre>{{ 这里的内容不会被编译 }}</span>
</template><template>
<span v-pre>{{ 这里的内容不会被编译 }}</span>
</template>
Cao Note