Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。类似虚拟 dom
{{ }}
标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。
js<span>Message: {{ msg }}</span>
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html
js// rawHtml = <span style="color: red">This should be red.</span>
<p><span v-html="rawHtml"></span></p>
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令
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>
Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式
js{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
// 标签属性也可以使用表达式
<div :id="`list-${id}`"></div>
// 表达式中使用一个组件暴露的方法
<span :title="toTitleDate(date)">
{{ formatDate(date) }}
</span>
注意
js// 错误示例
// 这是一个语句,而非表达式
{{ var a = 1 }}
// 条件控制也不支持,请使用三元表达式
{{ if (ok) { return message } }}
app.config.globalProperties
上显式地添加这里的 attributeName 会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。
htmlattributeName = href
// 可以将参数放入 [] 中动态执行
<a v-bind:[attributeName]="url"> ... </a>
// 简写
<a :[attributeName]="url"> ... </a>
注意
html<!-- 这会触发一个编译器警告 -->
<a :['foo' + bar]="value"> ... </a>
html// 例子将会在 DOM 内嵌模板中被转换为 :[someattr]
// 单文件组件内的模板不受此限制
<a :[someAttr]="value"> ... </a>
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent
修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault()
:
js<form @submit.prevent="onSubmit">...</form>
选用选项式 API 时,会用 data
选项来声明组件的响应式状态。此选项的值应为返回一个对象的函数。
顶层属性
都会被代理到组件实例 (即方法和生命周期钩子中的 this) 上$
作为前缀,并且保留内部属性的_
前缀,所以 data 的顶层属性不应使用$
、_
作为前缀jsexport 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 不是同步更新的。和 react 相同,vue 会把数据更新放入更新队列,完成计算后,缓冲它们直到更新周期的 “下个时机” 以确保无论你进行了多少次状态更改,每个组件都只更新一次。
若要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick()
这个全局 API
jsimport { nextTick } from 'vue'
export default {
methods: {
increment() {
this.count++
nextTick(() => {
// 访问更新后的 DOM
})
}
}
}
在 Vue 中,状态都是默认深层响应式的。这与 react 默认的不同 这意味着即使在更改深层次的对象或数组,你的改动也能被检测到。
jsexport default {
data() {
return {
obj: {
nested: { count: 0 },
arr: ['foo', 'bar']
}
}
},
methods: {
mutateDeeply() {
// 以下都会按照期望工作
this.obj.nested.count++
this.obj.arr.push('baz')
}
}
}
要为组件添加方法,需要在 methods
中定义组件中所有方法
Vue 自动为 methods
中的方法绑定了永远指向组件实例
的 this
,使用 this.方法名
调用
方法也可以在模板上被访问,常常被用作事件监听器
jsexport default {
data() {
return {
count: 0
}
},
// 在 method 中定义组件中所有方法,使用 this.方法名 调用
methods: {
increment() {
this.count++
}
},
mounted() {
// 在其他方法或是生命周期中也可以调用方法
this.increment()
}
}
// 方法也可以在模板上被访问
<button @click="increment">{{ count }}</button>
注意
methods
中的方法 this
都会指向组件本身,保证事件或回调的 this
正确
不应该在定义 methods 时使用箭头函数,因为箭头函数没有自己的 this 上下文jsexport default {
methods: {
increment: () => {
// 反例:无法访问此处的 `this`!
}
}
}
在某些情况下,我们可能会在组件方法中添加一些状态,比如创建一个预置防抖的事件处理器,或者常规的计时器
jsimport { debounce } from 'lodash-es'
export default {
methods: {
// 使用 Lodash 的防抖函数
click: debounce(function () {
// ... 对点击的响应 ...
}, 500)
}
}
这种方法对于被重用的组件来说是有问题的,它在运行时维护着一个内部状态。如果多个组件实例都共享这同一个预置防抖的函数,那么它们之间将会互相影响。
解决方法:可以把动态的变量放入声明周期中进行管理,而不放在方法中处理即可
jsexport default {
created() {
// 每个实例都有了自己的预置防抖的处理函数
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// 最好是在组件卸载时
// 清除掉防抖计时器
this.debouncedClick.cancel()
},
methods: {
click() {
// ... 对点击的响应 ...
}
}
}
计算属性,相当于一个数据 filter,在不改变组件状态的情况下获取处理的结果
可以利用这个特性,替代在模板中的依赖 state 的复杂 js结果 计算
jsexport default {
data() {
...
}
}
// template
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
使用 computed 重构
jsexport default {
data() {
...
},
computed: {
// 一个计算属性的 getter
publishedBooksMessage() {
// `this` 指向当前组件实例
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}
//
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
注意
js// 调用方法渲染
// 组件中
methods: {
calculateBooksMessage() {
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
// template
<p>{{ calculateBooksMessage() }}</p>
虽然使用调用方法渲染也可以达到计算属性的效果。 但是,计算属性值会基于其响应式依赖被缓存,而调用方法进行渲染,每次更新组件都会重新执行
如果是使用计算属性,只要 author.books
不改变,无论多少次访问 publishedBooksMessage
都会立即返回先前的计算结果,而不用重复执行 getter 函数
计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。
只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter
和 setter
来创建一个组件 state
jsexport 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
都是 attribute
,我们可以和其他 attribute 一样使用 v-bind
将它们和动态的字符串绑定
但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。
Vue 专门为 class
和 style
的 v-bind
用法提供了特殊的功能增强。在读取这两个属性的时候,除了字符串外,表达式的值也可以是对象或数组。
可以给 :class
传递一个对象来动态切换 class
html// active 是否存在取决于数据属性 isActive 的真假值
<div :class="{ active: isActive }"></div>
<div :class="{ 'active': isActive }"></div>
jsdata() {
return {
isActive: true,
hasError: false
}
}
// tmplate
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
>
</div>
// 渲染结果
<div class="static active"></div>
绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象
jsdata() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
//
<div :class="classObject"></div>
可以通过计算属性返回一个对象,进行绑定
jsdata() {
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>
jsdata() {
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>
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
获取传入的 classhtml<!-- 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 的绑定与 class 相同
htmldata() {
return {
activeColor: 'red',
fontSize: 30
}
}
// 小驼峰
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
// css key
<div :style="{ 'font-size': fontSize + 'px' }"></div>
当你在 :style
中使用了需要浏览器特殊前缀的 CSS 属性时,Vue 会自动根据浏览器为他们加上相应的前缀。
Vue 是在运行时检查该属性是否支持在当前浏览器中使用。如果浏览器不支持某个属性,那么将尝试加上各个浏览器特殊前缀,以找到哪一个是被支持的。
你可以对一个样式属性提供多个 (不同前缀的) 值,举例来说:
jstemplate
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
数组仅会渲染浏览器支持的最后一个值。在这个示例中,在支持不需要特别前缀的浏览器中都会渲染为 display: flex。
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 是一个指令,他必须依附于某个元素
如果想要切换不止一个元素,可以在一个 <template>
元素上使用 v-if
,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template>
元素。
v-else
和 v-else-if
也可以在 <template>
上使用。html<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样,但是有以下特性
<template>
元素上使用,也不能和 v-else 搭配使用。html<h1 v-show="ok">Hello!</h1>
v-show:初始渲染开销高,元素一定会渲染,只是 css 为不显示
v-if:切换开销高,不符合条件不会生成相关 dom
选择: 如果需要频繁切换是否显示,则使用 v-show 较好; 如果在运行时绑定条件很少改变,则 v-if 会更合适。
v-for 类似 for ... of ...
和 for ... in ...
的逻辑
v-for 指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名
htmldata() {
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 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys()
的返回值来决定
htmldata() {
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 可以直接接受一个整数值。用于遍历 1
到 n
html<span v-for="n in 10">{{ n }}</span>
与模板上的 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-if
比 v-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>
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key
attribute
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>
组件不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 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 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript
htmldata() {
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>
除了直接绑定方法名,还可以在内联事件处理器中调用方法。 这样就可以向方法传入自定义参数以代替原生事件
htmlmethods: {
say(message) {
alert(message)
}
}
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event
变量,或者使用内联箭头函数
这里箭头函数的使用与 react 的使用意义相同
jsmethods: {
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
提供了事件修饰符。修饰符是用 .
表示的指令后缀
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>
相关信息
使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。因此使用 @click.prevent.self 会阻止**元素及其子元素的所有点击事件的默认行为,**而 @click.self.prevent 则只会阻止对元素本身的点击事件的默认行为。
请勿同时使用 .passive 和 .prevent,因为 .passive 已经向浏览器表明了你不想阻止事件的默认行为。如果你这么做了,则 .prevent 会被忽略,并且浏览器会抛出警告。
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许在 v-on 或 @ 监听按键事件时添加按键修饰符。
html<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />
<input @keyup.page-down="onPageDown" />
Vue 为一些常用的按键提供了别名
系统按键与其他按键不同,在于系统按键可以还可以由鼠标事件触发(click)
html<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>
注意
系统按键修饰符和常规按键不同。 与 keyup 事件一起使用时,该按键必须在事件发出时处于按下状态。换句话说,keyup.ctrl 只会在你仍然按住 ctrl 但松开了另一个键时被触发。若你单独松开 ctrl 键将不会触发。
系统按键需要长按至操作结束,才能触发事件
.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>
用法与键盘按键绑定相同
在前端处理表单时,我们常常需要将表单输入框的内容同步给 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 选项来声明该初始值。
html// input
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />
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>
html<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
可以将多个复选框绑定到同一个数组或集合的值
htmlexport 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>
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>
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>
htmlexport 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>
对于单选按钮,复选框和选择器选项,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
还使我们可以将选项值绑定为非字符串的数据类型
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" />
html// pick 会在第一个按钮选中时被设为 first,在第二个按钮选中时被设为 second
<select v-model="selected">
<!-- 内联对象字面量 -->
<option :value="{ number: 123 }">123</option>
</select>
html// 当某个选项被选中,selected 会被设为该对象字面量值 { number: 123 }
<select v-model="selected">
<!-- 内联对象字面量 -->
<option :value="{ number: 123 }">123</option>
</select>
默认情况下,v-model
会在每次 input
事件后更新数据 (IME 拼字阶段的状态例外)。
你可以添加 lazy
修饰符来改为在每次 change
事件后更新数据
html<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />
如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number
修饰符来管理输入
html<input v-model.number="age" />
如果你想要默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符
html<input v-model.trim="msg" />
最常用的生命周期是 mounted
、updated
和 unmounted
jsexport default {
mounted() {
console.log(`the component is now mounted.`)
}
}
注意
避免用箭头函数来定义生命周期钩子,因为如果这样的话你将无法在函数中通过 this 获取组件实例
可以使用 watch
选项在每次响应式属性发生变化时触发一个函数
jsexport 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 选项也支持把键设置成用 .
分隔的路径:
jsexport default {
watch: {
// 注意:只能是简单的路径,不支持表达式。
'some.nested.key'(newValue) {
// ...
}
}
}
watch 默认是浅层的:被侦听的属性,仅在被赋新值时,才会触发回调函数——而嵌套属性的变化不会触发。
如果想侦听所有嵌套的变更,你需要深层侦听器,添加 deep
属性:
jsexport default {
watch: {
someObject: {
handler(newValue, oldValue) {
// 注意:在嵌套的变更中,
// 只要没有替换对象本身,
// 那么这里的 `newValue` 和 `oldValue` 相同
},
deep: true
}
}
}
watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。
例如,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。
我们可以用一个对象来声明侦听器,这个对象有 handler
方法和 immediate: true
选项,这样便能强制回调函数立即执行:
jsexport default {
// ...
watch: {
question: {
handler(newQuestion) {
// 在组件实例创建时会立即调用
},
// 强制立即执行回调
immediate: true
}
}
// ...
}
默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用
如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,你需要指明 flush: 'post'
选项
jsexport default {
// ...
watch: {
key: {
handler() {},
flush: 'post'
}
}
}
我们也可以使用组件实例的 $watch() 方法来命令式地创建一个侦听器:
jsexport default {
created() {
this.$watch('question', (newQuestion) => {
// ...
})
}
}
如果要在特定条件下设置一个侦听器,或者只侦听响应用户交互的内容,这方法很有用。
它还允许你提前停止该侦听器。
用 watch 选项或者 $watch() 实例方法声明的侦听器,会在宿主组件卸载时自动停止。因此,在大多数场景下,你无需关心怎么停止它。
在少数情况下,你的确需要在组件卸载之前就停止一个侦听器,这时可以调用 $watch()
API 返回的函数:
jsconst unwatch = this.$watch('foo', callback)
// ...当该侦听器不再需要时
unwatch()
ref
是一个特殊的 attribute
,允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。
html<input ref="input">
$refs.input
,在初次渲染时会是 null<script> export default { mounted() { // 获取到指向的 input 并 focus this.$refs.input.focus() } } </script> <template> <input ref="input" /> </template>
当在 v-for 中使用模板引用时,
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
attribute 还可以绑定为一个函数,会在每次组件更新时都被调用。该函数会收到元素引用作为其第一个参数
js<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
ref 引用也可以被用在一个子组件上。 这种情况下引用中获得的值是组件实例,引用的组件实例和该子组件的 this 完全一致,这意味着父组件对子组件的每一个属性和方法都有完全的访问权
为了做出限制,可以使用 expose
限制子组件的可访问性
jsexport default {
expose: ['publicData', 'publicMethod'],
data() {
return {
publicData: 'foo',
privateData: 'bar'
}
},
methods: {
publicMethod() {
/* ... */
},
privateMethod() {
/* ... */
}
}
}
上面这个例子中,父组件通过模板引用访问到子组件实例后,仅能访问 publicData 和 publicMethod
本文作者:Silon汐冷
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!