Skip to main content

Vue 3 备忘录

以下内容是基于 Vue 官方文档 梳理的备忘录.

模板语法

Attribute 绑定

同名简写

如果 attribute 的名称与绑定的 JavaScript 值的名称相同,那么可以省略 attribute 值:

<template>
<div :id></div>
<!-- 等同于 -->
<div :id="id"></div>
</template>
<script setup>
const id = ref("value");
</script>

动态绑定多个值

<template>
<!-- 通过不带参数的 `v-bind`,你可以将它们绑定到单个元素上: -->
<div v-bind="objectOfAttrs"></div>
</template>
<script setup>
const objectOfAttrs = {
id: "container",
class: "wrapper",
style: "background-color:green"
};
</script>

使用 JavaScript 表达式

Vue 模版中的数据绑定支持完整的 JavaScript 表达式 ​ 受限的全局访问 ​ 同时模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表:

const GLOBALS_ALLOWED =
"Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI," +
"decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array," +
"Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error,Symbol";

没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window 上的属性。然而,你也可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用。

全局属性

类型

interface AppConfig {
globalProperties: Record<string, any>;
}

app.config.globalProperties 用于注册能够被应用内所有组件实例访问到的全局属性的对象, 这是对 Vue 2 中 Vue.prototype 使用方式的一种替代,此写法在 Vue 3 已经不存在了。如果全局属性与组件自己的属性冲突,组件自己的属性将具有更高的优先级。

用法

// 定义
app.config.globalProperties.msg = "hello"; // 这使得 msg 在应用的任意组件模板上都可用,并且也可以通过任意组件实例的 this 访问到

// 使用
export default {
mounted() {
console.log(this.msg); // 'hello'
}
};

参数 Arguments

<a v-bind:href="url"> ... </a>href 就是一个参数,它告诉 v-bind 指令将表达式 url 的值绑定到元素的 href attribute 上, 另一个例子是 v-on 指令: <a v-on:click="doSomething"> ... </a> 这里 click 就是参数, 表示要监听的事件名称.

动态参数

指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内

<!-- 示例1 -->
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>

<!-- 示例2 -->
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething"> ... </a>

动态参数中表达式的值应当是一个字符串,或者是 null。特殊值 null 意为显式移除该绑定。其他非字符串的值会触发警告。推荐使用计算属性替换复杂的表达式.

指令、参数、修饰符

以下是 Vue 内置指令(directive):

  • v-bind 简写 :, 用于绑定值
  • v-on 简写 @, 用于监听事件
  • v-html 用于设置 html 内容

而指令的参数(argument)位于冒号后面, 比如 v-bind:href 中的 href 或者 v-on:click 中的 click,

修饰符(modfier)用.分割, 比如 v-on:submit.prevent 中的 prevent.

directive-arg-modifier

响应式基础

浅层响应性 ​ ​ 可以通过 shallowRef<T>(value: T) 来放弃深层响应性。对于浅层 shallowRef,只有 .value 的访问会被追踪, 可以避免对大型数据的响应性开销来优化性能、或者有外部库管理其内部状态的情况。

reactive() 的局限性

  • 有限的值类型 它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。
  • 不能替换整个对象 由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失
  • 对解构操作不友好 我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接
替换对象失去响应性
let state = reactive({count: 0});

// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({count: 1});
解构操作失去响应性
const state = reactive({count: 0});

// 当解构时,count 已经与 state.count 断开连接
let {count} = state;
// 不会影响原始的 state
count++;

// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count);

计算属性

可写计算属性

计算属性默认是只读的: const fullName = computed((previousValue) => firstName.value + ' ' + lastName.value), 可以通过同时提供 getter 和 setter 来创建可读写的计算属性:

import {ref, computed} from "vue";

const firstName = ref("John");
const lastName = ref("Doe");

const fullName = computed({
// getter
get(previousValue) {
return firstName.value + " " + lastName.value;
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(" ");
}
});

搭配 defineModel 使用示例:

const modelValue = defineModel<string>({default: ""});

// 内部状态,使用计算属性处理受控的值
const code = computed({
get: previousValue => modelValue.value,
set: newVal => {
const newCode = (newVal || "").substring(0, props.maxLength);
modelValue.value = newCode;
}
});

如果需要,可以通过访问计算属性的 getter 的第一个参数来获取计算属性返回的上一个值(previousValue)

Class 与 Style 绑定

<!-- 支持绑定字符串 -->
<div :style="'color: red'"></div>
<div :class="'mx-2 active'"></div>

<!-- 支持绑定对象 -->
<div :style="{ color: 'red' }"></div>
<div :class="{ active: isActive }"></div>

<!-- 或者绑定数组 -->
<div :style="[{ color: 'red' }, { backgroundColor: "white" }]"></div>
<div :class="[isActive ? activeClass : '', errorClass]"></div>

<!-- 或者在数组中嵌套对象 -->
<div :class="[{ [activeClass]: isActive }, errorClass]"></div>

在组件上使用

​ 对于只有一个根元素的组件,当你使用了 class attribute 时,这些 class 会被添加到根元素上并与该元素上已有的 class 合并。

<!-- 子组件模板 -->
<p class="foo bar">Hi!</p>

<!-- 在使用组件时 -->
<MyComponent class="baz boo" />

<!-- 最终渲染出的结果: -->
<p class="foo bar baz boo">Hi!</p>

如果你的组件有多个根元素,你将需要指定哪个根元素来接收这个 class。你可以通过组件的 $attrs 属性来指定接收的元素:

<!-- MyComponent 模板使用 $attrs 时 -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>

<!-- 这将被渲染为: -->
<p class="baz">Hi!</p>
<span>This is a child component</span>

条件渲染

​ 当 v-ifv-for 同时存在于一个元素上的时候,v-if 会首先被执行。

列表渲染

<li v-for="item in items">{{ item.message }}</li>

<!-- `v-for` 你也可以使用 `of` 作为分隔符来替代 `in` -->
<li v-for="item of items">{{ item.message }}</li>

<!-- 解构 + 索引 -->
<li v-for="({ message }, index) in items">#{{index + 1}} {{ message }}</li>

<!-- 迭代对象 -->
<ul>
<li v-for="(value, key, index) in { title: 'hello', author: 'Tom' }">{{ value }}</li>
</ul>

事件处理

监听事件

用法:v-on:click="handler"@click="handler"

事件处理器 (handler) 的值可以是:

  • 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与 onclick 类似)。
  • 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

内联事件处理器示例

<!-- 简单场景 -->
<button @click="count++">Add 1</button>

<!-- 类似 onclick 传递函数 -->
<button @click="increaseCount()">Add 1</button>

<!-- 访问事件参数: 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">Submit</button>

<!-- 访问事件参数: 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">Submit</button>

方法事件处理器

<template>
<button @click="increaseCount">Greet</button>
</template>
<script setup>
const count = ref(0);
const increaseCount = (event) => {
console.log("event target: ", event.target):
count.value ++;
}
</script>

事件修饰符

​ Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:

  • .stop
  • .prevent
  • .self
  • .capture
  • .once
  • .passive
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>

<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>

<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
info

使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。因此使用 @click.prevent.self 会阻止元素及其子元素的所有点击事件的默认行为,而 @click.self.prevent 则只会阻止对元素本身的点击事件的默认行为。

按键修饰符

​ Vue 允许在 v-on@ 监听按键事件时添加按键修饰符:

<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />

<input @keyup.page-down="onPageDown" />

常用的按键提供了别名:

  • .enter
  • .tab
  • .delete (捕获“Delete”和“Backspace”两个按键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
  • .ctrl
  • .alt
  • .shift
  • .meta

.exact 修饰符允许精确控制触发事件所需的系统修饰符的组合:

<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 仅当没有按下任何系统按键时触发 -->
<button @click.exact="onClick">A</button>

鼠标按键修饰符

  • .left
  • .right
  • .middle