Vue 模板指令

Vue 设计了很多自创的 v- 指令,来了解一下吧


模板输出

首先要知道,最基本的输出到模板的方法

假设 script 中的 vue 实例的 data 包含以下字段

1
2
3
4
5
data(){
return {
n: 0
}
}

双大括号语法

通过在 template 模板中使用双大括号语法可以将 data 中的值插入到模板,如

1
<span>{{n}}</span>

渲染后可以得到最终显示结果 0

当 n 变化时会自动重渲染到页面上(响应式)

v-text

效果与双大括号语法一样,用法上略有区别

如上例,在此处应改为如下模样

1
<span v-text="n"></span>

注意此处 v-text 后的字符串表示一个 javascript 表达式,而不是单纯字符串

本语法与双大括号语法一样,都不会显示空值,即如果 n 是 undefined 或者 null,则渲染后不会有任何显示

v-html

用于向模板中插入 html 字符串,例如

1
<div v-html="<img src=xx.png />"></div>

路径正确的话会显示对应的图片

v-pre

被 v-pre 标记的元素会跳过编译,当有大量连续的没有指令的元素时,建议使用 v-pre 加快编译

示例

1
<div v-pre>{{n}}</div>

此时不会显示 n 的值,而是原封不动显示

1
{{n}}

选择语句

有时候希望用户看得见或看不见某些部分,比如 SPA 中切换页面

v-if

可以使用 v-if 来控制元素是否编译

假设 data 中有 number 型数据 n

1
<div v-if="n===0">n is 0</div>

当 n 为 0 时编译该 div

可以使用 else 或 else if 来控制多条件

1
2
3
<div v-if="n>0">n is bigger than 0</div>
<div v-else-if="n===0">n is 0</div>
<div v-else>n is smaller than 0</div>

字面意思,不解释了

v-show

仅控制当前元素满足条件时显示或不显示,无法实现上例中的复杂逻辑控制

1
<div v-show="n===0">n is 0</div>

两者区别

乍一看似乎差不多,但还是有区别的

v-if 控制的是编译过程,所以一旦 v-if 判断不通过,那么该元素不会出现在 DOM 树上

v-show 其实仅控制 CSS,所以 v-show 判断不通过时,元素仍然会出现在 DOM 树上,但是display: none

显然 v-if 具有更高的切换开销(重构 DOM 树),v-show 具有更高的初始开销(一开始就要渲染),所以选择用哪个,还是要看具体的业务需求

循环语句

当使用 ul 之类列表的时候,经常要批量渲染一个数组之类

假设 data 中有如下数组

1
2
3
4
5
data(){
return {
fruits: ['apple','banana','orange']
}
}

则可以使用 v-for 指令来循环渲染

1
2
3
4
5
6
7
8
9
10
11
<ul>
<li v-for="item in fruits">
{{item}}
</li>
</ul>
<!-- 或者如下写法 -->
<ul>
<li v-for="(item,index) in fruits" :key="index">
{{item}}
</li>
</ul>

v-for 提供两个参数,按顺序分别是数组中的 value 和 key

在模板中可以也使用 v-text 来取值

一般建议采用绑定 key 的写法,否则 vue 可能会报错

此处 key 必须是不重复的值,所以一般取索引作为 key

动态绑定

v-bind

一般需要从内存到页面绑定一组关系,此时可以使用 v-bind 指令

例如,要为一个 img 绑定一个图片路径,可以使用如下写法

设有如下 script

1
2
3
4
5
data(){
return {
src: 'xxx.png'
}
}

然后模板写法如下

1
<img v-bind:src="src" />

当 data 中的 src 变化时,页面上的图片也会变化

v-bind 指令可以缩写为仅一个冒号,示例如下

1
<img :src="src" />

注意,此时如果通过开发者工具修改 src,并不会同步到内存中的 src,因为 v-bind 只是单向绑定

v-on

可以为 button 或者 a 标签绑定自定义事件

假设有如下 script

1
2
3
4
5
6
7
8
9
10
11
data(){
return {
n: 0
}
},
methods: {
add(x){
if(x) n+=x
else n++
}
}

且此时模板如下

1
2
<p>{{n}}</p>
<button v-on:click="add">点我+1</button>

则一开始 n 会显示 0,之后每次点击 button 都会调用 add 函数,可以写,也可以不写括号

如果要传参数,除形参列表外,还可以传递一个事件信息参数,命名为 $event

例如

1
<button v-on:click="add(2,$event)">点我+2</button>

v-on 可以缩写为 @

例如

1
2
<p>{{n}}</p>
<button @click="add">点我+1</button>

注意,当用在原生 DOM 事件上时,只能监听原生 DOM 事件

当用在自定义元素组件上时,也可以监听自定义事件

v-model

只有 v-bind 单向绑定怎么够用?所以就有了 v-model 双向绑定

一般用于表单中的 input 元素,以同步页面上的变动和内存中的变动

例如设有模板如下

1
2
3
4
5
<form>
<input v-model="username" />
<input v-model="password" />
</form>
<p>{{username}}</p>

此时 data 如下

1
2
3
4
5
6
data(){
return {
username: ''.
password: ''
}
}

当页面上的 username 变动时,内存中的也会随之变动,显式表现为 p 标签中内容会随着 input 中内容变动而变动

反之,内存变动也会同步显示到页面上

其中原理约等于

1
<input :value="username" @change="usernameChange" />

监听 change 事件,然后在 change 的时候修改 username

其余指令

v-once

被该指令标记的元素只会编译一次,当任意绑定发生更新时,均不会更新该元素

例如,假设有 n=0,且模板如下

1
<p v-once>{{n}}</p>

则第一次渲染时,会呈现 n 的值 0

之后不论 n 的值如何变化,此处都不会再次更新

v-slot

用于 vue 插槽的指令,也许以后会专门写一篇讲插槽

使用形如

1
2
3
4
5
<div class="container">
<header>
<slot name="header"></slot>
</header>
</div>

来声明一个名为 header 的具名插槽

并使用形如

1
2
3
4
5
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
</base-layout>

来声明一个出口为 header 的插槽内容

此时会渲染为

1
2
3
4
5
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
</div>

若声明的是匿名插槽,则名称为 default

此时插槽模板中所有未使用 v-slot 指令的内容都会被作为 default 插槽内容

例如模板结构如下

1
2
3
4
5
6
7
8
9
10
11
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>

此时插槽内容声明如下

1
2
3
4
5
6
7
8
9
10
11
12
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>

<p>A paragraph for the main content.</p>
<p>And another one.</p>

<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>

也可以具体声明为 default 插槽内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>

<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>

<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>

不论何种,都会渲染为

1
2
3
4
5
6
7
8
9
10
11
12
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>

v-cloak

一个仅在编译期有效的标记,常与 CSS 属性选择器共同使用

例如同时有以下 CSS 和 模板

1
2
3
[v-cloak]{
display: none;
}
1
2
3
<div v-cloak>
{{ message }}
</div>

直到编译结束,该 div 都不会显示

修饰符

一般只需要了解如下修饰符

修饰符示例 限用 功能
@click .stop=”myEvent” v-on 阻止事件冒泡
@click .prevent=”myEvent” v-on 阻止事件默认动作
@keypress.enter=”myEvent” v-on 当按下回车时执行
:money.sync=”myAttr” v-bind 组件之间的响应式更新
v-model.lazy=”myAttr” v-model 缓冲 input 的变化直到 input 失去焦点
v-model.number=”myAttr” v-model 自动转换输入为合法的数字
v-model.trim=”myAttr” v-model 自动去除输入的首尾空白

注意,修饰符可以连续施与,例如同时要取消一个事件的冒泡和默认动作,可以这么写

1
<button @click.stop.prevent="mySubmit">点我提交</button>

其中仅 .sync 需要单独讲解

.sync

在父子组件通信中,有时候需要在父子组件之间共享一个属性

但是 provide/inject 方法不能同步子组件的修改到父组件

v-model 又会造成父子组件对属性的修改没有区别,无法区分是谁做出的修改

此时就需要通过 .sync 修饰符来在父子组件之间共享属性

例如父组件中,现在有一子组件 text-document,希望在父子组件之间共享父组件的 title 属性

则模板如下

1
<text-document v-bind:title.sync="title"></text-document>

此时子组件可以通过以下语句来显式触发事件 update:title 以修改 title 的值

1
this.$emit('update:title', newTitle) // newTitle 为子组件中传出的新值

该事件必须有 update 前缀

而父组件修改不需要这么做,直接改就可以了

这样就可以区分开这个属性此时发生的修改究竟是父组件还是子组件所触发的

但 .sync 其实只是一个语法糖,其原理如下

1
2
3
4
<text-document
v-bind:title="title"
v-on:update:title="title = $event"
></text-document>

然后在子组件传出变更的新值

1
this.$emit('update:title', $event.target.value)

感谢阅读

--It's the end.Thanks for your read.--