vue 的特有语法,以 v-
开头的属性,用于更便捷地响应式更新 DOM。
- v-text 基本不用,通常用 {{}}
- v-html 尽量避免使用(避免 XSS 攻击)
v-bind 绑定数据 :
可以与标签的属性绑定的数据有:
- 变量
- 表达式
- 返回一个值的,无副作用(不会改变响应式变量)的函数
其特征如下:
- v-bind 通常简写为
:
- 变量值是 null / undefined 时,该属性会被移除
<!-- "id" 是自定义的变量 -->
<div :id="id"></div>
Vue 3.4+
: 若变量名和属性名相同,可简写为
<div :id></div>
属性值为布尔类型
绑定的变量值为 真值 或一个空字符串时,属性都有效!
<button disabled="">提交</button>
其他 假值 时,该属性失效。
绑定多个属性
用对象
const objectOfAttrs = {
id: 'container',
class: 'wrapper',
style: 'background-color:green'
}
<div v-bind="objectOfAttrs"></div>
绑定动态属性
即属性也是一个变量,其值需为字符串,若为 null , 表示移除该绑定。
<a :[attributeName]="url">
动态属性较复杂时,受字符限制,需用计算属性实现
绑定 class 属性
- 动态样式类
:class
可以和静态样式类class
同时使用
用对象【推荐】
直接绑定对象字面量(样式 2 个以内)
const isActive = ref(true)
const hasError = ref(false)
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
或 绑定对象变量(样式 2 个以上)
const classObject = reactive({
active: true,
'text-danger': false
})
<div class="static" :class="classObject"></div>
或 绑定返回对象的计算属性(逻辑很复杂时)
const isActive = ref(true)
const error = ref(null)
const classObject = computed(() => ({
active: isActive.value && !error.value,
'text-danger': error.value && error.value.type === 'fatal'
}))
<div class="static" :class="classObject"></div>
最终渲染效果
<div class="static active"></div>
用数组
const isActive = ref(true)
const hasError = ref(false)
<div :class="[isActive ? 'active' : '', hasError ? 'text-danger' : '']"></div>
可见用对象更清晰便捷
绑定 style 属性
:style
中使用了需要浏览器特殊前缀的 CSS 属性时,Vue 会自动为他们加上相应的前缀。Vue 是在运行时检查该属性是否支持在当前浏览器中使用。如果浏览器不支持某个属性,那么将尝试加上各个浏览器特殊前缀,以找到哪一个是被支持的。
用对象【推荐】
-
样式名推荐使用 camelCase ,如
fontSize
,但也支持 kebab-cased ,如font-size
-
多样式值用数组描述( 仅会渲染浏览器支持的最后一个值,下例中,在支持不需要特别前缀的浏览器中都会渲染为
display: flex
)<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
演示范例:
const activeColor = ref('red')
const fontSize = ref(30)
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
或 绑定对象变量(样式 2 个以上)
const styleObject = reactive({
color: 'red',
fontSize: '30px'
})
<div :style="styleObject"></div>
或 绑定返回对象的计算属性(逻辑很复杂时)
用数组
const baseStyles= reactive({
color: 'red',
})
const overridingStyles= reactive({
fontSize: '30px'
})
<div :style="[baseStyles, overridingStyles]"></div>
修饰符
-
.camel
将短横线命名的 attribute 转变为驼峰式命名。 -
.prop
(可缩写为.
)强制绑定为 DOM property。(vue3.2 +
)<div :someProperty.prop="someObject"></div> <!-- 等同于 --> <div .someProperty="someObject"></div>
-
.attr
强制绑定为 DOM attribute。(vue3.2 +
)
v-on 绑定事件 @
v-on 通常简写为 @
- 用于普通元素,只监听原生 DOM 事件
- 用于自定义组件,则监听子组件触发的自定义事件。
- 可绑定对象(批量绑定事件),此时不支持任何修饰符
<!-- 对象语法 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
内联事件
- 逻辑简单时使用
- v-on 的值为 js 语句 或 方法的调用
<!-- js 语句 -->
<button @click="count++">Add 1</button>
function say(message) {
alert(message)
}
<!-- 调用方法 -->
<button @click="say('bye')">Say bye</button>
- 调用方法时传入的参数会替代原生 DOM 事件
- 通过
$event
,或者使用内联箭头函数可以传入原生 DOM 事件
function warn(message, event) {
// 这里可以访问原生事件
if (event) {
event.preventDefault()
}
alert(message)
}
<!-- 【推荐】使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
方法事件
- 逻辑复杂时使用
- v-on 的值为方法名
- 方法事件的第一个参数是原生 DOM 事件
const name = ref('Vue.js')
function greet(event) {
alert(`Hello ${name.value}!`)
// `event` 是 DOM 原生事件
if (event) {
alert(event.target.tagName)
}
}
<button @click="greet">Greet</button>
事件修饰符
事件修饰符用于简化 DOM 事件的细节处理。
<a @click.stop="doThis"></a>
.stop
即一种事件修饰符,会在 doThis 方法中添加了代码
// 阻止事件冒泡
event.stopPropagation()
所以,使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。
@click.prevent.self
会阻止元素及其子元素的所有点击事件的默认行为@click.self.prevent
则只会阻止对元素本身的点击事件的默认行为。
全部事件修饰符如下:
事件修饰符 | 功能 |
---|---|
.stop | 阻止事件冒泡 |
.prevent | 阻止事件的默认行为 |
.self | 只有事件从元素本身发出才触发 |
.capture | 采用事件捕获模式(外部元素先响应事件) |
.once | 限制事件只触发一次 |
.passive | 不阻止事件的默认行为 |
.passive
修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能,且和 .prevent
不能同时使用,否则.passive
会失效,浏览器还会抛出警告。
v-on
可以不传值,只有修饰符
<!-- 不带表达式地阻止默认事件 -->
<form @submit.prevent></form>
按键修饰符
键盘事件上添加按键修饰符可便捷指定触发事件的按键。
<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />
全部按键修饰符如下:
按键修饰符 | 对应的按键 |
---|---|
.enter | enter 回车键 |
.tab | tab |
.delete | “Delete” 和 “Backspace” 两个按键 |
.esc | esc |
.up | 方向键 ↑ |
.down | 方向键 ↓ |
.left | 方向键 ← |
.right | 方向键 → |
.ctrl | ctrl |
.alt | alt |
.shift | shift |
.meta | mac 的 ⌘ 和 windows 的 ⊞ |
.exact | 精确控制触发事件所需的系统修饰符的组合 |
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>
keyup.ctrl
只会在你仍然按住 ctrl 但松开了另一个键时被触发。若你单独松开 ctrl 键将不会触发。
<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 仅当没有按下任何系统按键时触发 -->
<button @click.exact="onClick">A</button>
鼠标按键修饰符
鼠标事件上添加鼠标按键修饰符可便捷指定触发事件时按下的鼠标键。
鼠标按键修饰符 | 对应的按键 |
---|---|
.left | 鼠标左键 |
.middle | 鼠标中键 |
.right | 鼠标右键 |
v-show 条件渲染
通过改变元素的 display 样式值来切换元素的显示和隐藏
- 元素隐藏后, DOM 仍存在,其 display 值为 none
- 不支持在
<template>
上使用
<!-- ifShow 为真值时显示,否则隐藏 -->
<h1 v-show="ifShow">Hello!</h1>
v-if 条件渲染
通过重构和移除元素的 DOM 来切换元素的显示和隐藏
- 元素隐藏后, DOM 不存在
- 支持在
<template>
上使用 - 同时使用时,v-if 比 v-for 优先级更高。(但不推荐这样用)
<!-- ifShow 为真值时显示,否则隐藏 -->
<h1 v-if="ifShow">Hello!</h1>
多分支的条件渲染,需配合指令 v-else
、 v-else-if
一起使用
<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>
Not A/B/C
</div>
【考题】v-show 对比 v-if
-
v-show 通过移除/添加
display:none
样式,来显示/隐藏元素,适合频繁切换显隐的情况,有较高的渲染成本(v-show="false"时,该节点也会被创建),不能在<template>
上使用( template 最终不会渲染成 DOM 节点,无法添加移除样式) -
v-if 通过添加/删除DOM元素,来实现显示/隐藏元素,适合无需频繁切换显隐的情况,有较高的切换成本(每次切换,都需重新渲染节点),可以在
<template>
上使用
v-for 列表渲染
用于循环遍历渲染一个列表
-
需绑定唯一标识符 key (数据类型为 number | string | symbol),通常用 id
v-for
的默认方式是就地更新元素而不移动它们。要强制其重新排序元素,需要用 key 来提供一个排序提示 。(详见 diff 算法)
-
可以用 of 代替 in
-
使用
<template v-for>
时,key 应该被放置在这个<template>
上<template v-for="todo in todos" :key="todo.name"> <li>{{ todo.name }}</li> </template>
v-for 整数
<li v-for="n in 10" :key='n'>{{ n }}</li>
n 会从 1 到 10 ,循环渲染 10 次!
v-for 数组
<script setup lang="ts">
import { ref } from "vue";
const list = ref([
{ id: 1, name: "朝阳" },
{ id: 2, name: "晚霞" },
]);
</script>
<template>
<li v-for="(item, index) in list" :key="item.id">
{{ index }} - {{ item.name }}
</li>
</template>
为了方便,也可采用解构写法
<li v-for="({ id, name }, index) in list" :key="id">
{{ index }} - {{ name }}
</li>
数组的变更
数组的 API 很多,以下 API 会改变原数组,会触发页面的响应式更新
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
以下 API 不会改变原数组,而是返回一个新数组,不会触发页面的响应式更新
- filter()
- concat()
- slice()
数组的过滤/排序
此时需要在保留原数组的情况下,渲染过滤/排序后的数组。
filter 不会改变原数组,实现方式如下:
const sets = ref([
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
])
function even(numbers) {
return numbers.filter((number) => number % 2 === 0)
}
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
reverse() 和 sort() 会改变原数组,需先深拷贝一份原数组!
<script setup>
import { ref } from "vue";
const list = ref([1, 2, 3]);
function reverse(arr) {
let arr_temp = [...arr];
return arr_temp.reverse();
}
</script>
<template>
<h2>原数组</h2>
<li v-for="item in list" :key="item">
{{ item }}
</li>
<h2>新数组(翻转后)</h2>
<li v-for="item in reverse(list)" :key="item">
{{ item }}
</li>
</template>
v-for 对象
遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定
<script setup lang="ts">
import { ref } from "vue";
const meObj = ref({ name: "朝阳", age: 35 });
</script>
<template>
<li v-for="(value, key, index) in meObj" :key="key">
{{ index }}. {{ key }}: {{ value }}
</li>
</template>
需同时使用 v-for 和 v-if 时
如
<!--
这会抛出一个错误,因为属性 todo 此时
没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
因 v-if 比 v-for 的优先级更高,导致报错。
需改用如下写法:
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
什么情况可以不用 key ?
- 所迭代的 DOM 内容非常简单 ,不包含组件或有状态的 DOM 元素( 例如表单输入值 )
- 有意采用默认行为来提高性能。
在没有 key 的情况下,Vue 将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。
如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。
key 的更多用法见官网
https://cn.vuejs.org/api/built-in-special-attributes.html#key
v-model
给 v-model 添加参数,绑定多个 v-model ,v-model 的内置修饰符,自定义 v-model 的修饰符等
https://blog.csdn.net/weixin_41192489/article/details/137880927
v-slot
用于声明具名插槽或接收子组件对插槽的传值 props ,详见组件中插槽的语法
https://blog.csdn.net/weixin_41192489/article/details/138502548
v-pre
禁用模板编译
<span v-pre>{{ this will not be compiled }}</span>
渲染结果
v-once
仅渲染一次元素/组件,并跳过之后的更新,用于优化更新时的性能。
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once>
<h1>Comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
v-memo
v-memo 仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量 v-for 列表 (长度超过 1000 的情况):
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>
v-memo="[]"
与 v-once 效果相同
更多详情见官网
https://cn.vuejs.org/api/built-in-directives.html#v-memo
v-cloak
只在没有构建步骤的环境下需要使用,详见
https://cn.vuejs.org/api/built-in-directives.html#v-cloak