Vue2模板语法

2/18/2020

Vue.js的核心之一,是允许采用简洁的模板,通过声明式,将数据渲染进DOM中。结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。

模板语法-前端渲染

更多具体内容,可参考模板传送门 (opens new window)模板传送门 (opens new window)指令传送门 (opens new window)

# 插值

插值是数据渲染到DOM节点的一种方式,用于解析标签体内容。

# 文本

数据绑定最常见的形式就是使用{{}}(双大括号)的文本插值。

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

{{}}双大括号的中的内容,会由在data中对应的数据替代。无论何时,绑定到数据对上的属性发生改变时,插值处的内容也会更新。

# 基础指令

指令是带有v-前缀的特殊属性,用于解析标签(包括:标签属性、标签体内容、绑定事件.......)。

# v-html

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML。需要使用v-html指令。

<div id="app">
    <p>Using mustaches: {{ rawHtml }}</p>
    <p>Using v-html directive: <span v-html="rawHtml"></span></p>
</div>

<script>
    var vm = new Vue({
        el:"#app",
        data:{
            rawHtml:"<span style='color:red'>This should be red</span>"
        }
    });
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

# v-bind

{{}}插值不能作用在HTML标签属性上,需要通过v-bind指令。v-bind 指令可以用于响应式地更新 HTML attribute。

<a v-bind:href="url">...</a>
1

在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

<a v-on:click="doSomething">...</a>
1

# v-once

通过使用 v-once 指令,能执行一次性地插值,当数据改变时,插值处的内容不会更新。

<span v-once>这个将不会改变: {{ msg }}</span>
1

# 指令缩写

v-bind缩写

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>
1
2
3
4
5

v-on缩写

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>
1
2
3
4
5

# 事件指令

# v-on

v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

<div id="app">
	<button v-on:click="greet">Greet</button>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			name:'snake8859'
		},
		methods:{
			greet:function(event){
				// this在方法里指向当前 Vue 实例
				alert('Hello ' + this.name + '!')
			}
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

还可以在事件函数中传递参数

<div id="app">
	<button v-on:click="lucky('hi')">Lucky</button>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			name:'snake8859'
		},
		methods:{
			lucky:function(msg){
			// this在方法里指向当前 Vue 实例
				alert(msg+'!')
			}
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
1
2
3
// ...
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) {
      event.preventDefault()
    }
    alert(message)
  }
}
1
2
3
4
5
6
7
8
9
10

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

更多具体内容,可参考传送门 (opens new window)传送门 (opens new window)

# 条件指令

# v-if和v-else

  • v-if

    条件判断使用v-if指令。

    <div id="app">
    	<h1 v-if="flag1">我是可见的</h1>
    	<h1 v-if="flag2">我是不可见的</h1>
    </div>
    <script>
    	var vm = new Vue({
    		el:"#app",
    		data:{
    			flag1:true,
    			flag2:false
    		}
    	});
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    这里,v-if指令根据表达式flag的值(true或者false)来决定是否插入h1元素。

  • v-else

    可以用 v-else指令给 v-if 添加一个 "else" 块。

    <div id="app">
    	<h1 v-if="flag">我是可见的</h1>
    	<h1 v-else>我是不可见的</h1>
    </div>
    <script>
    	var vm = new Vue({
    		el:"#app",
    		data:{
    			flag:true
    		}
    	});
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# v-else-if

v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用

<div id="app">
	<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>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			type:""
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# v-show

另一种用户根据条件展示元素的指令是v-show

<div id="app">
	<h1 v-show="flag">我是可见的</h1>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			flag:true
		}
	})
</script>
1
2
3
4
5
6
7
8
9
10
11

# v-if 和 v-show区别

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

更多具体内容,可参考传送门 (opens new window)传送门 (opens new window)

# 迭代指令

v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。

# v-for迭代数组

<div id="app">
	<ul>
		<li v-for ="item in items">
			{{item.song}}
		</li>
	</ul>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			items:[
			  { song: '平凡之路' },
			  { song: '奉献' }
			]
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

v-for 还支持一个可选的第二个参数,即当前项的索引。

<div id="app">
	<ul>
		<li v-for ="item,index in items">
			{{index}}-{{item.song}}
		</li>
	</ul>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			items:[
			  { song: '平凡之路' },
			  { song: '奉献' }
			]
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# v-for迭代对象

也可以用v-for来遍历一个对象的属性。

<div id="app">
	<ul>
		<li v-for ="value,name in song">
			{{name}}:{{value}}
		</li>
	</ul>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			song:{
				name:"平凡之路",
				singer:"朴树"
			}
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

还可以用第三个参数作为索引。

<div id="app">
	<ul>
		<li v-for ="value,name,index in song">
			{{name}}:{{value}}-{{index}}
		</li>
	</ul>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			song:{
				name:"平凡之路",
				singer:"朴树"
			}
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

更多具体内容,可参考传送门 (opens new window)传送门 (opens new window)

# :key

v-for里有一个重要的属性key,它是虚拟DOM的标识。当数据发生变化的时候,Vue会根据新的数据生成新的虚拟DOM,然后对比新的虚拟DOM和旧的虚拟DOM,比较规则如下:

  1. 在旧xuniDOM找到与新虚拟DOM相同的key

    • 若虚拟DOM内容没有变,直接使用之前真实DOM
    • 若虚拟DOM内容发生变化,则生成新的真实DOM,替换之前的真实DOM
  2. 旧虚拟DOM中未找到新虚拟DOM相同的key

    创建新的真实DOM,随后渲染到页面

一般来说,如果不写key属性,默认会使用index作为key,但是会引发以下问题:

  1. 若对数据进行逆序添加或者删除等破坏顺序操作,会产生没有必要的真实DOM更新,虽然不影响界面效果,但效率低
  2. 若结构中包含输入类的DOM,会产生错误DOM更新

因此在应用key的时候,一般是建议使用每条数据唯一标识作为key

<!-- 示例代码: 没有key属性的v-for -->
<template>
  <div id="app">
    <div>
      <input type="text" v-model="name" />
      <button @click="addItem">添加</button>
    </div>
    <ul>
      <li v-for="(item, index) in list">
        <input type="checkbox" />
        {{item.name}}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      name: "",
      newId: 3,
      list: [
        { id: 1, name: "苹果" },
        { id: 2, name: "香蕉" },
        { id: 3, name: "火龙果" }
      ]
    };
  },
  methods: {
    addItem() {
      this.list.unshift({ id: ++this.newId, name: this.name });
    }
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

选中‘苹果’,然后添加‘西瓜’,选中项意外发生了变化。

在这里插入图片描述

为了让Vue能跟踪每个节点的身份,从而重用和重新排序现有元素,我们需要为每项提供一个唯一 key 。

<li v-for="(item, index) in list" :key="item.id">
  <input type="checkbox" />
  {{item.name}}
</li>
1
2
3
4

在这里插入图片描述

# 自定义指令

传送门 (opens new window)

# 样式绑定

# 绑定内联样式

v-bind:style用于绑定内联样式。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。

<div id="app">
	<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">内联样式1</div>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			activeColor: 'red',
			fontSize: 30
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12

直接绑定到一个样式对象通常更好,这会让模板更清晰:

<div id="app">
	<div v-bind:style="styleObject">内联样式2</div>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			styleObject:{
                color:'Blue',
                fontSize:'30px'
            }
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

还可以将多个样式对象绑定在同一元素上

<div id="app">
	<div v-bind:style="[baseStyles, overridingStyles]">多样式绑定</div>
</div>
<script>
	var vm = new Vue({
		el:"#app",
		data:{
			baseStyles:{
				color:'yellow'
			},
			overridingStyles:{
				fontSize:'30px'
			}			
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 绑定HTML Class

v-bind:class用于绑定类样式,可以传递一个对象,以动态切换class

<div id="app">
	<div 
	v-bind:class="{active:isActive}"
	style="width: 200px;height: 12.5rem;text-align: center;line-height: 12.5rem;">
		hi vue
	</div>
</div>
<script type="text/javascript">
	var vm = new Vue({
		el:"#app",
		data:{
			isActive:true
		}
	})
</script>
<style type="text/css">
	.active{
		background-color: brown;
	}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

v-bind:class还可以使用三元运算符。

<div id="app">
	<div 
	v-bind:class="[isActive ? activeClass : errorClass]"
	style="width: 200px;height: 12.5rem;text-align: center;line-height: 12.5rem;">
		hi vue
	</div>
</div>
<script type="text/javascript">
	var vm = new Vue({
		el:"#app",
		data:{
			isActive:false,
			activeClass:'active',
			errorClass:'text-danger'
		}
	})
</script>
<style type="text/css">
	.active{
	background-color:yellowgreen;
    }
    .error{
        background-color: red;
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

更多具体内容,可参考传送门 (opens new window)

# 数据绑定

# 单向绑定v-bind

v-bind指令是进行单向绑定,数据只能从data流向页面,在页面修改的数据无法流回data。

# 双向绑定v-model

v-model 指令是进行双向绑定,data和页面是联动的,任意一则修改都会影响。

v-model用于在表单 <input><textarea><select> 元素上创建双向数据绑定。

  • 文本
<div id="app">
	<!--文本-->
	<div>
		<input v-model="message1" placeholder="edit me">
		<p>Message1 is: {{ message1 }}</p>
	</div>
</div>

<script>
	var vm = new Vue({
		el: "#app",
		data: {
			message1: ""
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 多行文本
<div id="app">
	<!--多行文本-->
	<div>
        <textarea v-model="message2" placeholder="add multiple lines"></textarea>
        <br>
        <span>Multiline message2 is:{{message2}}</span>
	</div>
</div>

<script>
	var vm = new Vue({
		el: "#app",
		data: {
			message2: ""
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 单选
<div id="app">
	<!--单选按钮-->
    <div>
        <input type="radio" id="one" value="One" v-model="picked">
        <label for="one">One</label>
        <br>
        <input type="radio" id="two" value="Two" v-model="picked">
        <label for="two">Two</label>
        <br>
        <span>Picked: {{ picked }}</span>
    </div>
</div>

<script>
	var vm = new Vue({
		el: "#app",
		data: {
			picked: ""
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 多选
<div id="app">
	<!--多选按钮-->
    <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>
        <br>
        <span>Checked names: {{ checkedNames }}</span>
    </div>
</div>

<script>
	var vm = new Vue({
		el: "#app",
		data: {
			checkedNames: []
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • 选择框
<div id="app">
	<!--选择框-->
    <div>
        <select v-model="selected">
            <option disabled value="">请选择</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>
        <span>Selected: {{ selected }}</span>
    </div>
</div>

<script>
	var vm = new Vue({
		el: "#app",
		data: {
			selected: ""
		}
	});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 修饰符

  • lazy

    在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:

    <!-- 在“change”时而非“input”时更新 -->
    <input v-model.lazy="msg" >
    
    1
    2
  • number

    如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

    <input v-model.number="age" type="number">
    
    1

    这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

  • trim

    如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

    <input v-model.trim="msg">
    
    1

更多具体内容,可参考传送门 (opens new window)传送门 (opens new window)

Last Updated: 8/23/2023, 2:39:40 PM