1概述
上一章我们说了Vue是用于构建用户界面的框架,它最终呈现给用户的是一个一个HTML标签。Vue可以使用HTML模板和render函数这2种方式来构建这些HTML标签。当开发者使用了HTML模板来构建界面时,Vue内部会将其编译成render函数,最后渲染到挂载点上。render函数生成的是虚拟DOM,虚拟DOM是基于js的,相比于DOM的操作,开销会小很多。
Vue是实例需要一个挂载点,这个挂载点是由Vue的el选项指定的,el的类型可以是string或HTMLElement类型,当为string时,使用css选择器。
<html>
<body>
<div id="app">
<h1>Hello World!</h1>
</div>
<script>
var vm = new Vue({
el:"#app"
});
</script>
</body>
</html>
上面使用了el类型为string的方式查找挂载点,#app
为css的id选择器。Vue实例会将自身生成的DOM替换掉挂载点的元素,这里我们没有定义如何生成DOM,所以,Vue会将挂载点的HTML提取出来当作模板生成DOM,替换掉原来的HTML标签。
2 HTML模板
HTML模板有4种方式定义,分别是:
-
Vue实例不定义,由Vue将挂载点的元素提取出来生成模板,可以理解为将模板写在挂载点内,如上面的例子。
-
将模板以字符串的形式写在实例的配置选项template中
<div id="app"></div> <script> var vm = new Vue({ el:"#app", template:"<h1>Hello World!</h1>" }); </script>
实例挂载后,会将template中的字符串,转换为html标签,替换掉app的内容。
-
使用x-template,在template中定义模板有些弊端,因为它是字符串,所以缺少高亮的语法支持,当需要多行显示是,需要用到
\
,体验不好。x-template是将模板的定义放在scirpt标签中,然后在template中使用#
引用该模板。<div id="app"></div> <!--使用x-template定义模板--> <script type="text/x-template" id="tempHello"> <h1>Hello World!</h1> </script> <!--使用template选项引用x-template定义的模板--> <script> var vm = new Vue({ el:"#app", template:"#tempHello", }); </script>
在定义x-template模板时,需要注意的是,x-template定义的模板在script标签中,type属性为
text/x-template
,需要配上id,后续的template中才能够引用。在template选项中要使用#引用x-template模板(类似使用css的id选择器)。 -
使用单文件组件(single-file components)的方式定义模板(最常用),单文件组件是以
.vue
结尾的文件,一个文件就是一个组件,后续讲解组件时再详细说明。
3 内部指令(directive)
在HTML模板中,html元素仅仅是界面的呈现,我们还需要和vue实例进行交互,这就需要用到vue的指令。在vue中,指令是写在html元素中,以v-
开头,后接表达式(有些不需要表达式),它是Vue实例数据与用户界面之间的纽带。如:
<div id="app">
<p v-text="msg">text</p>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello World!",
}
});
</script>
v-text
指令后接表达式msg,msg是对实例data中Hello World!
的引用。当实例运行时,v-text指令所在的标签内容会替换为Hello World!
。有些指令可以在其后加上修饰符,从而达到特殊的效果。指令大致可以分为显示类、渲染类,事件类3种。
3.1 显示类指令
指令 | 用法 | 说明 |
---|---|---|
v-text | <div v-text="msg"></div> |
以文本的方式更新元素的内容,即使msg中是html标签,它只当做字符串的方式处理,不会解析标签。即,只改变元素的textContent,与插值表达式相同<div>{{msg}}</div>
|
v-html | <div v-html=“html”></div> |
更新元素的 innerHTML 。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译 |
v-cloak | <div v-cloak>{{msg}}</div> |
v-cloak 不接表达式,当网速较慢时,vue实例还没有加载完成,这时界面上会显示{{msg}},在vue实例加载完成后,{{msg}}会替换为对应的内容,不过这使得页面会闪动。当使用v-cloak指令后,vue实例在编译完成后,会移除该指令。通过这一特征,使用v-cloak再配合css的属性选择器[v-cloak]{display:none;} 使用,达到在网速慢时界面不闪动的效果。 |
3.2 渲染类指令
指令 | 用法 | 说明 |
---|---|---|
v-once | <div v-once>{{msg}}</div> |
v-once不接表达式,通常配合其他指令使用,表示只渲染一次。渲染后,当msg的内容发生变化时,也不会重新渲染。 |
v-show | <div v-show=“show”></div> |
v-show后接表达式,表达式返回true或false,根据表达式的值,修改该元素的display的样式,从而达到显示隐藏的效果。注意:因为是使用的display样式,所以该元素始终都会被渲染,只是显示/隐藏而已。 |
v-if v-if-else v-else
v-if
、v-if-else
和v-else
标签需要组合使用,后接表达式。与其它语言的if/else一样,表达式条件为true时进入该分支。
<div id="app">
<p v-if="status==0">0</p>
<p v-else-if="status==1">1</p>
<p v-else="status==2">2</p>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
status:0,
}
});
</script>
与v-show
不同,使用这组指令时,当条件为true时才渲染该分支对应的元素/组件,当条件为false时,对应的元素/组件会被移除。
Vue会出于效率考虑,会复用已有的元素,而非重新渲染,这样做有时会出现不符合需求的情况。比如,当复用input的时候,不会改变input的value值。
<div id="app">
<template v-if="showQQ">
<label>QQ</label>
<input placeholder="QQ">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Email">
</template>
</div>
当showQQ为true时,显示QQ输入框,用户在输入内容后,切换到Email,即改变了showQQ的值为false,这时第一个template的内容将会被移除,但是vue为了提高效率,会重新引用这个label和input,并设置了对应的值,不过,vue并没有修改input的value的值,所以Email输入框中的值还是我们之前输入的QQ内容。
要解决这个问题,只需要在input上加入key
属性,告诉vue这两个input是完全独立的,不要复用它们,注意key的值必须是唯一的。
v-if
和v-show
的区别:
- v-if是条件渲染,v-show是条件显示,v-if会根据条件销毁元素/组件以及其绑定事件,而v-show总是会渲染,根据条件切换css样式来控制显示/隐藏。
- v-show不能使用在template标签上
v-for
v-for
通过遍历数据源渲染指定内容。
<ul>
<li v-for="person in persons">{{person.name}}</li>
</ul>
<script>
var vm = new Vue({
data:{
persons:[{name:"张三"},{name:"李四"},]
}
});
</script>
将v-for加到要渲染的标签上,使用alias in expression
语法为当前遍历的元素提供别名。v-for的数据源可以是数组,也可以是对象,还可以是数字。
- 当数据源为数组时,别名指向的是数组元素,
- 当数据源为对象时,别名指向的是对象的值集合中的值。
- 当数据源为数字时,别名指向的是从1开始的数字,迭代到等于数据源的数字
v-for有多种可选参数形式:
<div v-for="item in array"></div>
<div v-for="(item,index) in array"></div>
<div v-for="value in object"></div>
<div v-for="(value,key) in object"></div>
<div v-for="(value,key,index) in object"></div>
<div v-for="n in number"></div>
3.3 事件类指令
事件类指令包括v-on
,v-bind
,v-model
3个指令,如果说显示类指令和渲染类指令负责界面的呈现,那么事件类指令就负责定义交互逻辑,这些指令非常常用。
3.3.1 v-on 监听事件
使用v-on来监听事件,这些事件包括HTML元素的DOM原生事件,也可能是组件的自定义事件,组件使用$emit()方法来触发事件。v-on缩写为@
符号
<div id="app">
<!-- v-on指令接收javascript代码 -->
<button v-on:click="counter += 1">点击</button>
<!-- v-on语法糖形式 -->
<button @click="counter += 1">点击</button>
<p>点击了按钮 {{ counter }} 次</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
counter: 0
}
})
</script>
事件的处理有3种方式,第1种为直接将js代码加在v-on指令中,如上所示。当代码量较多时,这种方式不可行,此时可用第2种方式,将事件的处理逻辑封装到方法中,把方法与事件绑定起来。
<div id="app">
<!-- v-on指令接收方法名 -->
<button @click="increment">点击</button>
<p>点击了按钮 {{ counter }} 次</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
counter: 0
},
methods:{
increment(){
this.counter++;
}
}
})
</script>
第3种是使用内敛语句调用方法,这种方式和第1种基本一样,只是第1种是执行语句,这里是执行方法,这种方式很常用,它比第2种方式的优势在于,可以传递$event
参数。Vue中使用变量$event
表示原始DOM事件的Event 对象,拿到event对象后我们可以做很多事,比如获取触发事件的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态等。
<div id="app">
<!-- v-on指令使用内敛语句调用方法 -->
<button @click="sayHello('张三',$evnet)">点击</button>
<p>{{ msg }} </p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
msg: "",
},
methods:{
sayHello(name,event){
if(evnet){
event.preventDefault();
}
this.msg = "Hello " + name;
}
}
})
</script>
下面列出一些常用的event事件标准属性和方法,具体参考MDN DOM Event接口
属性/方法 | 描述 |
---|---|
ctrlKey | 当鼠标事件触发时,如果 control 键被按下,则返回 true; |
altKey | 当鼠标事件触发的时,如果alt 键被按下,返回true; |
shiftKey | 当鼠标事件触发时,如果 shift 键被按下,则返回 true; |
button | 当鼠标事件触发的时,如果鼠标按钮被按下,将会返回一个数值。 |
clientX | 鼠标指针在点击元素(DOM)中的X坐标。 |
clientY | 鼠标指针在点击元素(DOM)中的Y坐标。 |
screenX | 鼠标指针相对于全局(屏幕)的X坐标 |
screenY | 鼠标指针相对于全局(屏幕)的Y坐标 |
bubbles | 表示该事件是否在DOM中冒泡。 |
cancelable | 用来表示这个事件是否可以取消。 |
currentTarget | 返回注册这个事件监听的对象。 |
eventPhase | 事件流正在处理哪个阶段。 |
target | 触发该事件的元素。 |
type | 返回当前 Event 对象表示的事件的名称,及事件的类型(不区分大小写) |
initEvent() | 初始化新创建的 Event 对象的属性。 |
preventDefault() | 取消事件,不执行与事件关联的默认动作。 |
stopPropagation() | 停止事件冒泡。 |
v-on修饰符
Vue为v-on提供了事件修饰符,在一定程度上简化了在方法中处理$event
对象的逻辑。
-
.stop
- 调用event.stopPropagation()
。 -
.prevent
- 调用event.preventDefault()
。 -
.capture
- 添加事件侦听器时使用 capture 模式。 -
.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调。 -
.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。 -
.native
- 监听组件根元素的原生事件。 -
.once
- 只触发一次回调。 -
.left
- (2.2.0) 只当点击鼠标左键时触发。 -
.right
- (2.2.0) 只当点击鼠标右键时触发。 -
.middle
- (2.2.0) 只当点击鼠标中键时触发。 -
.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
其中.capture
表示该事件使用捕获模式(默认是冒泡模式),我们在用js原生添加事件监听使用如下方法,event是事件名称,如click;listener是为该事件注册的方法,useCapture是使用捕获模式还是冒泡模式。
addEventListener(event, listener, useCapture)
useCapture默认是false的,也就是说默认使用冒泡事件,关于捕获和冒泡可以简单的理解,捕获模式是事件传播的方向是由根元素向目标前进,冒泡模式表示事件的传播方向是由目标元素向根元素前进。
3.3.2 v-bind 属性绑定
将表达式动态绑定到HTML元素/组件的属性上,缩写为:
符号
<div id="app">
<a v-bind:href="url">百度</a>
<!-- v-bind语法糖形式 -->
<a :href="url">百度</a>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
url:"www.baidu.com",
}
});
</script>
v-bind的写法为v-bind:foo="bar"
,其中foo表示v-bind的参数,代表的是原生/组件的属性名,bar是预期值,即属性对应的值。
参数时可选的,也就是说,v-bind可指定属性名也可不指定属性名,当不带参数时,要绑定到一个包含键值对的预期值对象,对象的key就是参数,对象key对应的value就是参数对应的值,如下:
<img v-bind="{src:'a.png',alt='呵呵',height:32,width:32}">
<!--等价于-->
<img src="a.png" alt="呵呵" height="32" width="32">
可以利用无参指令,一次性绑定多个属性。
在向自定义组件绑定属性值时(通过props传递),数据是单向流动的,父组件向子组件传递数据,反之不行。常规的做法是,父组件通过子组件的props传值给子组件,同时在子组件上注册监听事件,子组件通过$emit方法触发父组件的监听方法,修改props对应的值。
Vue提供了.sync修饰符,它实际上是一个语法糖,加了.sync的prop,vue会自动的为其生成一个事件监听,监听的名称为update:propName
,如要子组件要修改prop的值,只需调用this.$emit('update:propName',value)
即可,如下所示:
<body>
<div id="app">
<h3>{{msg}}</h3>
<!-- 引用组件,将msg赋值到组件的title属性上,
并使用.sync修饰符(语法糖)添加v-on事件监听,
该事件名称的格式为 update:propName,对应到这里是update:title-->
<son :title.sync="msg" />
</div>
<!-- 使用x-template定义组件的HTML模板 -->
<script type="text/x-template" id="son">
<div>
<p>{{title}}</p>
<button @click="btnClicked">点我</button>
</div>
</script>
<script>
// 组件定义
Vue.component('son', {
//定义组件属性
props:['title'],
//找到id为son的HTML模板
template: "#son",
methods: {
btnClicked() {
//触发由.sync生成的upadte:title事件
this.$emit("update:title","子组件改变后的标题")
}
},
});
var vm = new Vue({
el: "#app",
data: {
msg: "标题"
}
});
</script>
</body>
注意:.sync修饰符后面不能接表达式。
3.3.3 class与style增强绑定
v-bind可以绑定元素的所有属性,其中最常用的是元素的class和style属性,不过当这两个属性的逻辑较为复杂时,使用字符串的方式返回该属性值体验非常差,所以,Vue增强了对class和style的绑定。
class的绑定
使用v-bind绑定class有对象语法和数字语法两种方式,在对象语法中key表示css的类名,value是个bool值,为true时,该key对应的类名加入class中,为false时,对应的项从class中移除。
<div class="cell" :class="{'active':isActive,'error':isError}"></div>
<script>
var vm = new Vue({
data:{
isActive:true,
isError:false,
}
});
</script>
上面的代码最终生成的class为:
<div class="cell active"></div>
v-bind:class
生成的动态绑定类会与class属性中的类合并。这isActive的值为true,表示active会加入到class类中,isError为false,div的class中会移除error类。
数组语法就相对简单,只要是数组中的字符串,都会加到class类中。在数组中可以使用三元表达式来动态的加载类。
<div :class="[cell,isActive?'active':'',isError?'error':'']"></div>
style内联样式绑定
v-bind:sytle
也有对象语法和数据语法,但是对象语法较为常见
<div :style="cellStyle"></div>
<script>
var vm = new Vue({
cellStyle:{
fontSize:14+'px',
color:'red'
}
});
</script>
结果为:
<div style="color:red;font-size:14px;"></div>
3.3.4 v-model双向绑定
v-model指令用于在表单类元素上实现数据的双向绑定,它的本质也是监听表单类元素的输入事件,更新绑定的数据,vue对以下列表单元素做了处理。
<!-- 输入框元素,绑定value属性,监听input事件 -->
<input type="text" v-model="text">
<!-- 文本域元素,绑定value属性,监听input事件 -->
<textarea v-model="text"></textarea>
<!-- 单个复选框时,v-model绑定一个bool值,该值指示checkbox添加/移除checked属性,监听change事件 -->
<input type="checkbox" v-model="checked" id="checkbox1">
<label for="cbox1">{{checkd}}</label>
<!--
多个复选框时,v-model绑定一个数组,与value属性配合使用,监听change事件。
选中复选框时,将value值push到数组中,否则,将value值从数组中移除。
-->
<input type="checkbox" id="jack" value="jack" v-model="names">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="john" v-model="names">
<label for="john">John</label>
<!-- 单选按钮,v-model绑定字符串,与value值配合使用-->
<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>
<!--
选择框,v-model绑定value值,监听change事件,也分为单选和多选,
因为实际使用select标签较少(样式不统一),这里不在演示
-->
修饰符
-
.lazy
修饰符适用于输入框,设置后该修饰符后,v-model不再监听input事件,转而监听change事件。也就是当用户输入文本时,数据不是事实的同步,而是在文本框失去焦点或回车时更新。<input type="text" v-model.lazy="text">
-
.number
将输入框的内容转换为数字类型 -
.trim
自动过滤用户输入的首尾空白字符
3.3.5 自定义组件的v-model
v-model指令不仅可以在表单元素上实现数据的双向绑定,还可以在自定义组件上实现双向绑定。上面说了,v-model其实是语法糖,它的本质是绑定值到元素的指定属性上,并监听元素的输入事件,vue在表单元素中为我们实现了该功能,而在自定义组件上,这个功能需要我们自己实现。
v-model在自定义组件上默认绑定value属性,监听input事件。
<body>
<div id="app">
<h3>{{count}}</h3>
<!-- 在自定义组件上使用v-model指令 -->
<my-component v-model="count" />
</div>
<script type="text/x-template" id="myComponent">
<div>
<p>{{value}}</p>
<button @click="btnClicked">点我</button>
</div>
</script>
<script>
// 组件定义
Vue.component('myComponent', {
//组件中定义value属性
props:["value"],
template: "#myComponent",
methods: {
btnClicked() {
// 发送input事件
this.$emit("input",this.value+1);
}
},
});
var vm = new Vue({
el: "#app",
data: {
count: 0,
},
});
</script>
</body>
因为v-model默认绑定的是value属性,所以需要在组件中定义一个value。v-model指令默认监听input事件(自动生成),所以,执行通过$emit触发input事假即可。
如果希望改变v-model在组件上默认的属性和事件,可以使用model选项,model选项接收一个对象,这个对象有两个属性:prop和event。prop的值默认是value,event的值默认是input。
<script>
Vue.component('myComponent',{
model:{
//修改v-model的默认绑定的value属性和input监听事件
prop:"count",
event:"countChange"
},
props:["count"],
template:"#myComponent",
methods:{
btnClicked(){
this.$emit("contChange",this.count+1);
}
}
});
</script>
网友评论