编辑
2023-02-09
前端
00
请注意,本文编写于 650 天前,最后修改于 646 天前,其中某些信息可能已经过时。

目录

模板语法
文本插值 {{ }}
html原始文本 v-html
html attribute 属性绑定 v-bind
模板中可以使用js 表达式
动态attribute参数 [ ]
修饰符
组件state数据 data(){}
dom 的更新不是同步的
状态更新后再执行
状态修改的响应是深层响应
组件中的方法 methods: {}
有状态方法的状态管理
计算属性 computed: {}
使用计算属性 or 调用方法
在计算属性中更新状态值
Class 与 Style 绑定
class 绑定
多个 class 内容
绑定对象内容
绑定计算属性
绑定数组
子组件 class 继承
绑定 style
自动前缀
多样性属性
条件渲染
v-if 、 v-else 、v-else-if
v-if 多个元素
v-show
v-if 和 v-show 的选择
循环渲染 v-for
v-for 数组
v-for 对象
v-for 范围
v-for 多个元素
不要在一个节点上同时使用 v-for 和 v-if
循环渲染的节点需要一个唯一的 key
在组件上使用 v-for
事件绑定 v-on 、 @
内联处理和方法处理
内联处理中调用方法
内联处理访问事件参数 $event 、 =>
事件修饰符 .
键盘按键绑定
按键别名
系统按键
.exact 修饰符
鼠标按键绑定
表单输入绑定 v-model
基本使用
基本表单内容绑定
input
textarea
checkbox input
radio input
select
使用 v-for 渲染select option
表单值与 state 绑定使用 v-bind
checkbox input
radio input
select
v-model 的修饰符
.lazy
.number
.trim
生命周期
监听器 watch: {}
基本使用
深层监听 deep
即时回调的监听器 handler(){} 、immediate
指定回调在组件更新后触发 flush
创建监听器 this.$watch()
终止监听器
dom引用 ref
获取引用的 dom
v-for ref 多个 dom
ref 绑定函数
ref 绑定组件

模板语法

Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。

在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。类似虚拟 dom

文本插值 {{ }}

{{ }}标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。

js
<span>Message: {{ msg }}</span>

html原始文本 v-html

双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html

  • span 的内容将会被替换为 rawHtml 属性的值
  • 插值为纯 HTML,标签中数据绑定将会被忽略
js
// rawHtml = <span style="color: red">This should be red.</span> <p><span v-html="rawHtml"></span></p>

html attribute 属性绑定 v-bind

双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令

  • 如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除
  • 可以绑定js对象
js
// v-bind <div v-bind:id="dynamicId"></div> // 可以使用简写 : 代替 v-bind <div :id="dynamicId"></div> // 值为真值或“”时,会保留该属性 <button :disabled="isButtonDisabled">Button</button> // 绑定js对象 data() { return { objectOfAttrs: { id: 'container', class: 'wrapper' } } } <div v-bind="objectOfAttrs"></div>

模板中可以使用js 表达式

Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式

js
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} // 标签属性也可以使用表达式 <div :id="`list-${id}`"></div> // 表达式中使用一个组件暴露的方法 <span :title="toTitleDate(date)"> {{ formatDate(date) }} </span>

注意

  • 每个绑定仅支持单一表达式,也就是一段能够被求值的 JavaScript 代码
js
// 错误示例 // 这是一个语句,而非表达式 {{ var a = 1 }} // 条件控制也不支持,请使用三元表达式 {{ if (ok) { return message } }}
  • 模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。也可以自行在 app.config.globalProperties 上显式地添加

动态attribute参数 [ ]

这里的 attributeName 会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。

html
attributeName = href // 可以将参数放入 [] 中动态执行 <a v-bind:[attributeName]="url"> ... </a> // 简写 <a :[attributeName]="url"> ... </a>

注意

  • 空格和引号,在 HTML attribute 名称中都是不合法的
html
<!-- 这会触发一个编译器警告 --> <a :['foo' + bar]="value"> ... </a>
  • 需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写
html
// 例子将会在 DOM 内嵌模板中被转换为 :[someattr] // 单文件组件内的模板不受此限制 <a :[someAttr]="value"> ... </a>

修饰符

修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault()

js
<form @submit.prevent="onSubmit">...</form>

image.png

组件state数据 data(){}

选用选项式 API 时,会用 data 选项来声明组件的响应式状态。此选项的值应为返回一个对象的函数。

  • 返回对象的所有顶层属性都会被代理到组件实例 (即方法和生命周期钩子中的 this) 上
  • 不在 data 中添加的组件属性,无法触发响应式更新
  • 若所需的值还未准备好,在必要时也可以使用 null、undefined 或者其他一些值占位
  • vue 内置 api 使用 $作为前缀,并且保留内部属性的_前缀,所以 data 的顶层属性不应使用$_作为前缀
js
export default { data() { return { count: 1 // 不可以使用$ _ 做顶层属性前缀 $count: 3 //错误 _count: 5 //错误 // 只有顶层属性会被绑定至 this // 访问 x 必须使用 this.position.x position: { x: 1, y: 3 } } }, // `mounted` 是生命周期钩子 mounted() { // `this` 指向当前组件实例 console.log(this.count) // => 1 // 数据属性也可以被更改 this.count = 2 } }

dom 的更新不是同步的

当修改组件的状态数据时,绑定的 dom 会自动跟随更新。

DOM 不是同步更新的。和 react 相同,vue 会把数据更新放入更新队列,完成计算后,缓冲它们直到更新周期的 “下个时机” 以确保无论你进行了多少次状态更改,每个组件都只更新一次。

状态更新后再执行

若要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick() 这个全局 API

js
import { nextTick } from 'vue' export default { methods: { increment() { this.count++ nextTick(() => { // 访问更新后的 DOM }) } } }

状态修改的响应是深层响应

在 Vue 中,状态都是默认深层响应式的。这与 react 默认的不同 这意味着即使在更改深层次的对象或数组,你的改动也能被检测到。

js
export default { data() { return { obj: { nested: { count: 0 }, arr: ['foo', 'bar'] } } }, methods: { mutateDeeply() { // 以下都会按照期望工作 this.obj.nested.count++ this.obj.arr.push('baz') } } }

组件中的方法 methods: {}

要为组件添加方法,需要在 methods 中定义组件中所有方法

  • Vue 自动为 methods 中的方法绑定了永远指向组件实例this,使用 this.方法名 调用

  • 方法也可以在模板上被访问,常常被用作事件监听器

js
export default { data() { return { count: 0 } }, // 在 method 中定义组件中所有方法,使用 this.方法名 调用 methods: { increment() { this.count++ } }, mounted() { // 在其他方法或是生命周期中也可以调用方法 this.increment() } } // 方法也可以在模板上被访问 <button @click="increment">{{ count }}</button>

注意

  1. methods 中的方法 this 都会指向组件本身,保证事件或回调的 this 正确 不应该在定义 methods 时使用箭头函数,因为箭头函数没有自己的 this 上下文
js
export default { methods: { increment: () => { // 反例:无法访问此处的 `this`! } } }

有状态方法的状态管理

在某些情况下,我们可能会在组件方法中添加一些状态,比如创建一个预置防抖的事件处理器,或者常规的计时器

js
import { debounce } from 'lodash-es' export default { methods: { // 使用 Lodash 的防抖函数 click: debounce(function () { // ... 对点击的响应 ... }, 500) } }

这种方法对于被重用的组件来说是有问题的,它在运行时维护着一个内部状态。如果多个组件实例都共享这同一个预置防抖的函数,那么它们之间将会互相影响。

解决方法:可以把动态的变量放入声明周期中进行管理,而不放在方法中处理即可

js
export default { created() { // 每个实例都有了自己的预置防抖的处理函数 this.debouncedClick = _.debounce(this.click, 500) }, unmounted() { // 最好是在组件卸载时 // 清除掉防抖计时器 this.debouncedClick.cancel() }, methods: { click() { // ... 对点击的响应 ... } } }

计算属性 computed: {}

计算属性,相当于一个数据 filter,在不改变组件状态的情况下获取处理的结果

可以利用这个特性,替代在模板中的依赖 state 的复杂 js结果 计算

  • vue 可以追踪到 compute 中的属性依赖,并在依赖状态改变的时候更新组件
js
export default { data() { ... } } // template <p>Has published books:</p> <span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>

使用 computed 重构

js
export default { data() { ... }, computed: { // 一个计算属性的 getter publishedBooksMessage() { // `this` 指向当前组件实例 return this.author.books.length > 0 ? 'Yes' : 'No' } } } // <p>Has published books:</p> <span>{{ publishedBooksMessage }}</span>

注意

  1. 为了保证计算结果正常,计算属性的处理(setter)应该是一个纯函数,不应该有其他副作用
  2. 计算结果和 state 一样,是一个临时快照,是只读的,所以不应直接修改计算属性,而是修改其所依赖的状态

使用计算属性 or 调用方法

js
// 调用方法渲染 // 组件中 methods: { calculateBooksMessage() { return this.author.books.length > 0 ? 'Yes' : 'No' } } // template <p>{{ calculateBooksMessage() }}</p>

虽然使用调用方法渲染也可以达到计算属性的效果。 但是,计算属性值会基于其响应式依赖被缓存,而调用方法进行渲染,每次更新组件都会重新执行

如果是使用计算属性,只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果,而不用重复执行 getter 函数

在计算属性中更新状态值

计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。

只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 gettersetter 来创建一个组件 state

js
export default { data() { return { firstName: 'John', lastName: 'Doe' } }, methods: { // 当触发该方法时,setter 会被调用 // this.firstName 和 this.lastName 会随之更新 change(){ this.fullName = 'John Doe' } } computed: { // 创建一个 fullName 组件 state fullName: { // getter,调用 fullName 获取其返回值 get() { return this.firstName + ' ' + this.lastName }, // setter,当更改 fullName 时触发 setter set(newValue) { // 解构赋值语法 [this.firstName, this.lastName] = newValue.split(' ') } } } }

再运行 this.fullName = 'John Doe' 时,setter 会被调用而 this.firstName 和 this.lastName 会随之更新

Class 与 Style 绑定

classstyle 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定

但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。 Vue 专门为 classstylev-bind 用法提供了特殊的功能增强。在读取这两个属性的时候,除了字符串外,表达式的值也可以是对象或数组

class 绑定

可以给 :class 传递一个对象来动态切换 class

html
// active 是否存在取决于数据属性 isActive 的真假值 <div :class="{ active: isActive }"></div> <div :class="{ 'active': isActive }"></div>

多个 class 内容

js
data() { return { isActive: true, hasError: false } } // tmplate <div class="static" :class="{ active: isActive, 'text-danger': hasError }" > </div> // 渲染结果 <div class="static active"></div>

绑定对象内容

绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象

js
data() { return { classObject: { active: true, 'text-danger': false } } } // <div :class="classObject"></div>

绑定计算属性

可以通过计算属性返回一个对象,进行绑定

js
data() { return { isActive: true, error: null } }, computed: { classObject() { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } } // <div :class="classObject"></div>

绑定数组

js
data() { return { activeClass: 'active', errorClass: 'text-danger' } } // <div :class="[activeClass, errorClass]"></div> // 渲染结果 <div class="active text-danger"></div>

也可以在绑定的数组中,使用条件表达式

html
<div :class="[isActive ? activeClass : '', errorClass]"></div> // 简化 <div :class="[{ active: isActive }, errorClass]"></div>

子组件 class 继承

  • 对于只有一个根元素的子组件,调用子组件时传入 class,会和子组件根元素的 class 合并。 这里的 class 会被识别为 attribute 而不是 props
html
// 子组件 <p class="foo bar">Hi!</p> // 父组件中调用 <MyComponent class="baz boo" /> // 注意这里是当做 attribute 处理的 <MyComponent :class="{ active: isActive }" /> // 结果 <p class="foo bar baz boo">Hi</p>
  • 如果子组件有多个根元素,可以通过 $attrs 获取传入的 class
html
<!-- MyComponent 模板使用 $attrs 时 --> <p :class="$attrs.class">Hi!</p> <span>This is a child component</span> // 调用子组件 <MyComponent class="baz" /> // 渲染结果 <p class="baz">Hi!</p> <span>This is a child component</span>

绑定 style

style 的绑定与 class 相同

  • 绑定
    可以识别小驼峰命名,同时兼容 kebab-cased(css 属性 key) 命名
html
data() { return { activeColor: 'red', fontSize: 30 } } // 小驼峰 <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> // css key <div :style="{ 'font-size': fontSize + 'px' }"></div>
  • style 的对象绑定、计算结果绑定、数组绑定与 class 相同,可参考 class 部分内容

自动前缀

当你在 :style 中使用了需要浏览器特殊前缀的 CSS 属性时,Vue 会自动根据浏览器为他们加上相应的前缀。

Vue 是在运行时检查该属性是否支持在当前浏览器中使用。如果浏览器不支持某个属性,那么将尝试加上各个浏览器特殊前缀,以找到哪一个是被支持的。

多样性属性

你可以对一个样式属性提供多个 (不同前缀的) 值,举例来说:

js
template <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

数组仅会渲染浏览器支持的最后一个值。在这个示例中,在支持不需要特别前缀的浏览器中都会渲染为 display: flex。

条件渲染

v-if 、 v-else 、v-else-if

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染

html
<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-if 多个元素

v-if 是一个指令,他必须依附于某个元素

如果想要切换不止一个元素,可以在一个 <template> 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template> 元素。

  • v-elsev-else-if 也可以在 <template> 上使用。
html
<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>

v-show

另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样,但是有以下特性

  • v-show 会在 DOM 渲染中保留该元素
  • v-show 仅切换了该元素上名为 display 的 CSS 属性。
  • v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。
html
<h1 v-show="ok">Hello!</h1>

v-if 和 v-show 的选择

  • v-show:初始渲染开销高,元素一定会渲染,只是 css 为不显示

  • v-if:切换开销高,不符合条件不会生成相关 dom

选择: 如果需要频繁切换是否显示,则使用 v-show 较好; 如果在运行时绑定条件很少改变,则 v-if 会更合适。

循环渲染 v-for

v-for 类似 for ... of ...for ... in ...的逻辑

v-for 数组

v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名

html
data() { return { items: [{ message: 'Foo' }, { message: 'Bar' }] } } // v-for <li v-for="item in items"> {{ item.message }} </li> // 可以使用 of 代替 in <div v-for="item of items"></div> // 支持可选的第二个参数表示当前项的索引 <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> // 支持解构 <li v-for="({ message }, index) in items"> {{ message }} {{ index }} </li> // v-for 可以多层嵌套 <li v-for="item in items"> <span v-for="childItem in item.children"> {{ item.message }} {{ childItem }} </span> </li>

v-for 对象

可以使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定

html
data() { return { myObject: { title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' } } } // 遍历对象 <ul> <li v-for="value in myObject"> {{ value }} </li> </ul> // 提供两个可选参数 属性名 key,位置索引 index <li v-for="(value, key, index) in myObject"> {{ index }}. {{ key }}: {{ value }} </li>

v-for 范围

v-for 可以直接接受一个整数值。用于遍历 1n

  • 注意此处 n 的初值是从 1 开始而非 0。
html
<span v-for="n in 10">{{ n }}</span>

v-for 多个元素

与模板上的 v-if 类似,你也可以在 <template> 标签上使用 v-for 来对多个元素一起使用 v-for。

html
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </template> </ul>

不要在一个节点上同时使用 v-for 和 v-if

v-ifv-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名

html
<!-- 这会抛出一个错误,因为属性 todo 此时 没有在该实例上定义 --> <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo.name }} </li> <!-- 拆分解决 --> <template v-for="todo in todos"> <li v-if="!todo.isComplete"> {{ todo.name }} </li> </template>

循环渲染的节点需要一个唯一的 key

为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key attribute

  • vue 会根据 key 值的变化来判断是否需要更新
  • key属性应当和 v-for 在同一个元素上
html
<div v-for="item in items" :key="item.id"> <!-- 内容 --> </div> <template v-for="todo in todos" :key="todo.name"> <li>{{ todo.name }}</li> </template>

在组件上使用 v-for

组件不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props

html
<MyComponent v-for="item in items" :key="item.id" /> // 将迭代数据传入组件中 <MyComponent v-for="(item, index) in items" :item="item" :index="index" :key="item.id" />

事件绑定 v-on 、 @

内联处理和方法处理

我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript

html
data() { return { count: 0 } } // 内联处理 <button @click="count++">Add 1</button> <p>Count is: {{ count }}</p>
html
// 调用方法处理 data() { return { name: 'Vue.js' } }, methods: { greet(event) { // 方法中的 `this` 指向当前活跃的组件实例 alert(`Hello ${this.name}!`) // `event` 是 DOM 原生事件 if (event) { alert(event.target.tagName) } } } <!-- `greet` 是上面定义过的方法名 --> <button @click="greet">Greet</button>

内联处理中调用方法

除了直接绑定方法名,还可以在内联事件处理器中调用方法。 这样就可以向方法传入自定义参数以代替原生事件

html
methods: { say(message) { alert(message) } } <button @click="say('hello')">Say hello</button> <button @click="say('bye')">Say bye</button>

内联处理访问事件参数 $event 、 =>

有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数

这里箭头函数的使用与 react 的使用意义相同

js
methods: { warn(message, event) { // 这里可以访问 DOM 原生事件 if (event) { event.preventDefault() } alert(message) } }
html
<!-- 使用特殊的 $event 变量 --> <button @click="warn('Form cannot be submitted yet.', $event)"> Submit </button> <!-- 使用内联箭头函数 --> <button @click="(event) => warn('Form cannot be submitted yet.', event)"> Submit </button>

事件修饰符 .

为了简化处理时事件处理细节,例如event.preventDefault(),Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀

  • .stop
  • .prevent
  • .self
  • .capture
  • .once
  • .passive
html
<!-- 单击事件将停止传递 --> <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>

.capture.once.passive 修饰符与原生 addEventListener 事件相对应

html
<!-- 添加事件监听器时,使用 `capture` 捕获模式 --> <!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 --> <div @click.capture="doThis">...</div> <!-- 点击事件最多被触发一次 --> <a @click.once="doThis"></a> <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 --> <!-- 以防其中包含 `event.preventDefault()` --> <!-- .passive 修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能 --> <div @scroll.passive="onScroll">...</div>

相关信息

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

  2. 请勿同时使用 .passive 和 .prevent,因为 .passive 已经向浏览器表明了你不想阻止事件的默认行为。如果你这么做了,则 .prevent 会被忽略,并且浏览器会抛出警告。

键盘按键绑定

在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许在 v-on 或 @ 监听按键事件时添加按键修饰符。

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

按键别名

Vue 为一些常用的按键提供了别名

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

系统按键

系统按键与其他按键不同,在于系统按键可以还可以由鼠标事件触发(click)

  • .ctrl
  • .alt
  • .shift
  • .meta(window 的菜单键、mac 的 command 键)
html
<!-- Alt + Enter --> <input @keyup.alt.enter="clear" /> <!-- Ctrl + 点击 --> <div @click.ctrl="doSomething">Do something</div>

注意

系统按键修饰符和常规按键不同。 与 keyup 事件一起使用时,该按键必须在事件发出时处于按下状态。换句话说,keyup.ctrl 只会在你仍然按住 ctrl 但松开了另一个键时被触发。若你单独松开 ctrl 键将不会触发。

系统按键需要长按至操作结束,才能触发事件

.exact 修饰符

.exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符

html
<!-- 当按下 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 这些修饰符将处理程序限定为由特定鼠标按键触发的事件。

表单输入绑定 v-model

基本使用

在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:

html
<input :value="text" @input="event => text = event.target.value">

v-model 指令帮我们简化了这一步骤:

html
<input v-model="text">

它会根据所使用的元素自动使用对应的 DOM 属性和事件组合

注意

v-model 会忽略任何表单元素上初始的 value、checked 或 selected attribute。 它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。 你应该在 JavaScript 中使用data 选项来声明该初始值。

基本表单内容绑定

input

html
// input <p>Message is: {{ message }}</p> <input v-model="message" placeholder="edit me" />

textarea

html
// textarea <span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <textarea v-model="message" placeholder="add multiple lines"></textarea>

相关信息

<textarea> 中是不支持插值表达式的

html
<!-- 错误 --> <textarea>{{ text }}</textarea> <!-- 正确 --> <textarea v-model="text"></textarea>

checkbox input

html
<input type="checkbox" id="checkbox" v-model="checked" /> <label for="checkbox">{{ checked }}</label>

可以将多个复选框绑定到同一个数组或集合的值

html
export default { data() { return { checkedNames: [] } } } <div>Checked names: {{ checkedNames }}</div> <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>

radio input

html
<div>Picked: {{ picked }}</div> <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>

select

html
// 单选 <div>Selected: {{ selected }}</div> <select v-model="selected"> <option disabled value="">Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select>
html
// 多选 <div>Selected: {{ selected }}</div> <select v-model="selected" multiple> <option>A</option> <option>B</option> <option>C</option> </select>
使用 v-for 渲染select option
html
export default { data() { return { selected: 'A', options: [ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ] } } } <select v-model="selected"> <option v-for="option in options" :value="option.value"> {{ option.text }} </option> </select> <div>Selected: {{ selected }}</div>

表单值与 state 绑定使用 v-bind

对于单选按钮,复选框和选择器选项,v-model 绑定的值通常是静态的字符串 (或者对复选框是布尔值)

html
<!-- `picked` 在被选择时是字符串 "a" --> <input type="radio" v-model="picked" value="a" /> <!-- `toggle` 只会为 true 或 false --> <input type="checkbox" v-model="toggle" /> <!-- `selected` 在第一项被选中时为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select>

有时我们可能希望将该值绑定到当前组件实例上的动态数据。 可以通过使用 v-bind 来实现。 此外,使用 v-bind 还使我们可以将选项值绑定为非字符串的数据类型

checkbox input

html
// true-value 和 false-value 是 Vue 特有的 attributes // 仅支持和 v-model 配套使用 // toggle 属性的值会在选中时被设为 'yes',取消选择时设为 'no' <input type="checkbox" v-model="toggle" true-value="yes" false-value="no" /> <input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue" />

radio input

html
// pick 会在第一个按钮选中时被设为 first,在第二个按钮选中时被设为 second <select v-model="selected"> <!-- 内联对象字面量 --> <option :value="{ number: 123 }">123</option> </select>

select

html
// 当某个选项被选中,selected 会被设为该对象字面量值 { number: 123 } <select v-model="selected"> <!-- 内联对象字面量 --> <option :value="{ number: 123 }">123</option> </select>

v-model 的修饰符

.lazy

默认情况下,v-model 会在每次 input 事件后更新数据 (IME 拼字阶段的状态例外)。 你可以添加 lazy 修饰符来改为在每次 change 事件后更新数据

html
<!-- 在 "change" 事件后同步更新而不是 "input" --> <input v-model.lazy="msg" />

.number

如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入

  • 如果该值无法被 parseFloat() 处理,那么将返回原始值。
  • number 修饰符会在输入框有 type="number" 时自动启用
html
<input v-model.number="age" />

.trim

如果你想要默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符

html
<input v-model.trim="msg" />

生命周期

最常用的生命周期是 mountedupdatedunmounted

  • 所有生命周期钩子函数的 this 上下文都会自动指向当前调用它的组件实例
js
export default { mounted() { console.log(`the component is now mounted.`) } }

注意

避免用箭头函数来定义生命周期钩子,因为如果这样的话你将无法在函数中通过 this 获取组件实例

监听器 watch: {}

可以使用 watch 选项在每次响应式属性发生变化时触发一个函数

基本使用

js
export default { data() { return { question: '', answer: 'Questions usually contain a question mark. ;-)' } }, watch: { // 每当 question 改变时,这个函数就会执行 question(newQuestion, oldQuestion) { if (newQuestion.includes('?')) { this.getAnswer() } } }, methods: { async getAnswer() { this.answer = 'Thinking...' try { const res = await fetch('https://yesno.wtf/api') this.answer = (await res.json()).answer } catch (error) { this.answer = 'Error! Could not reach the API. ' + error } } } }
html
<p> Ask a yes/no question: <input v-model="question" /> </p> <p>{{ answer }}</p>

watch 选项也支持把键设置成用 . 分隔的路径:

js
export default { watch: { // 注意:只能是简单的路径,不支持表达式。 'some.nested.key'(newValue) { // ... } } }

深层监听 deep

watch 默认是浅层的:被侦听的属性,仅在被赋新值时,才会触发回调函数——而嵌套属性的变化不会触发。

如果想侦听所有嵌套的变更,你需要深层侦听器,添加 deep 属性:

js
export default { watch: { someObject: { handler(newValue, oldValue) { // 注意:在嵌套的变更中, // 只要没有替换对象本身, // 那么这里的 `newValue` 和 `oldValue` 相同 }, deep: true } } }

即时回调的监听器 handler(){} 、immediate

watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。

例如,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。

我们可以用一个对象来声明侦听器,这个对象有 handler 方法和 immediate: true 选项,这样便能强制回调函数立即执行:

js
export default { // ... watch: { question: { handler(newQuestion) { // 在组件实例创建时会立即调用 }, // 强制立即执行回调 immediate: true } } // ... }
  • 回调函数的初次执行就发生在 created 钩子之前。Vue 此时已经处理了 data、computed 和 methods 选项,所以这些属性在第一次调用时就是可用的。

指定回调在组件更新后触发 flush

默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用

如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,你需要指明 flush: 'post' 选项

js
export default { // ... watch: { key: { handler() {}, flush: 'post' } } }

创建监听器 this.$watch()

我们也可以使用组件实例的 $watch() 方法来命令式地创建一个侦听器:

js
export default { created() { this.$watch('question', (newQuestion) => { // ... }) } }

如果要在特定条件下设置一个侦听器,或者只侦听响应用户交互的内容,这方法很有用。

它还允许你提前停止该侦听器。

终止监听器

用 watch 选项或者 $watch() 实例方法声明的侦听器,会在宿主组件卸载时自动停止。因此,在大多数场景下,你无需关心怎么停止它。

在少数情况下,你的确需要在组件卸载之前就停止一个侦听器,这时可以调用 $watch() API 返回的函数:

js
const unwatch = this.$watch('foo', callback) // ...当该侦听器不再需要时 unwatch()

dom引用 ref

ref 是一个特殊的 attribute,允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。

html
<input ref="input">

获取引用的 dom

  • 挂载结束后引用都会被暴露在 this.$refs 之上
  • 只可以在组件挂载后才能访问模板引用。
  • 如果你想在模板中的表达式上访问 $refs.input,在初次渲染时会是 null
<script> export default { mounted() { // 获取到指向的 input 并 focus this.$refs.input.focus() } } </script> <template> <input ref="input" /> </template>

v-for ref 多个 dom

当在 v-for 中使用模板引用时,

  • 相应的引用中包含的值是一个数组
  • ref 数组并不保证与源数组相同的顺序
js
<script> export default { data() { return { list: [ /* ... */ ] } }, mounted() { console.log(this.$refs.items) } } </script> <template> <ul> <li v-for="item in list" ref="items"> {{ item }} </li> </ul> </template>

ref 绑定函数

ref attribute 还可以绑定为一个函数,会在每次组件更新时都被调用。该函数会收到元素引用作为其第一个参数

  • 这里需要使用动态的
    绑定才能够传入一个函数
  • 当绑定的元素被卸载时,函数也会被调用一次,此时的 el 参数会是 null
js
<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">

ref 绑定组件

ref 引用也可以被用在一个子组件上。 这种情况下引用中获得的值是组件实例,引用的组件实例和该子组件的 this 完全一致,这意味着父组件对子组件的每一个属性和方法都有完全的访问权

为了做出限制,可以使用 expose 限制子组件的可访问性

  • expose 选项可以用于限制对子组件实例的访问
js
export default { expose: ['publicData', 'publicMethod'], data() { return { publicData: 'foo', privateData: 'bar' } }, methods: { publicMethod() { /* ... */ }, privateMethod() { /* ... */ } } }

上面这个例子中,父组件通过模板引用访问到子组件实例后,仅能访问 publicData 和 publicMethod

本文作者:Silon汐冷

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!