Vue.js从入门到实践学习笔记(二)——指令


指令是带有v-前缀的特殊属性,其值限定为单个表达式。指令的作用是,当表达式的值发生改变时,将其产生的连带影响应用到DOM上。

一、内置指令

针对一些常用的页面功能提供以指令来封装的使用形式,以HTML元素属性的方式来使用。

1、v-show(是否显示)

v-show指令根据表达式的值的真假来显示或隐藏HTML元素。使用v-show指令,元素本身是要被渲染的,通过设置CSS样式属性display来控制元素是否显示。
值为false时,相当于设置样式:

    style="display:none"

如何显示或隐藏多个元素?

使用HTML5新增的<template>元素来包裹需要切换显示与隐藏的多个元素,然后在<template>元素上使用v-show指令。最终渲染结果不会包含元素,元素被当作一个不可见的包裹元素,主要用于分组的条件判断和列表渲染。

	<template v-show="isShow">
		<form>
			<p>template里用v-show指令</p>
			<p>template里用v-show指令</p>
		</form>
	</template>

2、v-if/v-else-if/v-else (条件判断)

v-if指令

根据表达式的值的真假来生成或删除一个元素(与v-show效果一样,实现机制不一样)

v-if指令在HTML元素的显示与否的实现机制上与v-show指令不同:
表达式的值为false时,v-if指令不会创建该元素,只有为true时,才会真正创建该元素;
而v-show指令不管表达式的值是真是假,元素都会被创建,显示与否通过CSS的样式属性display来控制。

v-if控制多个元素的创建或删除,也用<template>元素包裹多个元素。

v-if与v-show对比

  • v-if具有更高的切换开销,如果运行条件很少改变,使用v-if较好;
  • v-show具有更高的初始渲染开销,如需要频繁切换元素的显示或隐藏,使用v-show更好。

v-else-if、v-else指令与v-if一起使用,可以实现互斥条件判断:

  • 当一个条件满足时,后续的条件都不会再判断。
  • v-else-if、v-else要紧跟在v-if或v-else-if之后。

用key管理可复用的元素

Vue会尽可能高效的渲染元素,通常会复用已有元素而不是从头开始渲染,这样会使渲染效率很高。

比如:在两个模板里使用相同的<input>元素,Vue为了提高渲染效率,会复用<input>元素,这样在切换时,<input>的内容不会被替换掉,仅替换了它的placeholder属性。

如果不希望重复使用,可以通过为元素添加一个具有唯一值的key属性,来告诉Vue,“这两个元素是完全独立的,不要重复使用它们”。

	<input placeholder="请输入你的用户名" key="username-input">
	<input placeholder="请输入你的邮箱" key="email-input">

3、v-for(循环)

v-for指令通过循环的方式来渲染一个列表,循环的对象可以是数组,也可以是一个JavaScript对象。

v-for遍历数组

表达式语法:item in items,其中items是源数据数组,item是被迭代的数组元素的别名。

<html>中,

	<li v-for="book in books">{
    
    {
    
    book.title}}</li>

<script>中,Vue实例的数据对象中定义一个数组属性books:

	data:{
    
    
		books:[
			{
    
    title:'vue'},
			{
    
    title:'123'},
			{
    
    title:'java'}
		]
	}

book是数组中元素的别名,每次循环,book的值都被重置为数组当前索引的值,在<li>元素内部,可以通过Mustache语法来引用该变量。

提示:v-for指令的表达式也可以用of代替in作为分隔符,更接近JavaScript迭代器的语法,如

	<li v-for="book of books">{
    
    {
    
    book.title}}</li>

支持一个可选的参数作为当前项的索引

	<li v-for="(book, index) in books">{
    
    {
    
    index}} - {
    
    {
    
    book.title}}</li>    //多个参数需要放在圆括号里

数组更新检测

Vue的核心是视图与数据的双向绑定,为了监测数组中元素的变化,以便能及时将变化反映到视图中。Vue对数组的下列变异方法(mutation method)进行了包裹:

	push()
	pop()    
	shift()
	unshift()    //数组的unshift()方法,该方法向数组的开头添加一个或多个元素
	splice()
	sort()
	reverse()
	//如:
	vm.books.push({
    
    title:'java web'})    //新增一条数据在数组末尾

数组中还有一些非变异方法(non-mutating method),如:filter()、concat()、slice(),它们不会改变原始数据。而是返回一个新的数组。如果要自动更新视图,可以使用新数组来替换原来的数组。如:

	vm.books = vm.books.concat({
    
    title:'java web'})     //使用新数组替换原数组,同步视图更新

Vue在检测到数组变化时,不会重新渲染整个列表,而是最大化复用DOM元素。因此,替换的数组中,含有相同元素的项不会重新渲染。新数组替换旧数组,不用担心性能问题。

注意1、如何通过索引直接设置数据项:(效果类似于vm.books[0]={…})

	Vue.set(vm.books,  0, {
    
    title:'java web'})    //使用Vue全局set()方法
	vm.books.splice(0, 1, {
    
    title:'java web'})    //使用数组原型的splice()方法
	vm.set(vm.books,  0, {
    
    title:'java web'})    //使用Vue实例的set()方法,该方法是Vue.set的一个别名。

注意 2、如何修改数组的长度:(效果类似于vm.books.length = 1)

	vm.books.splice(1)

过滤与排序

有时想要显示一个数组经过过滤或排序后的版本,但不实际改变或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。

	<li v-for="n in evenNumbers">{
    
    {
    
    n}}</li> 
	......
	
	data:{
    
    
		numbers:[1,2,3,4,5]
	},
	computed:{
    
     
		evenNumbers: function(){
    
      
			return this.numbers.filter(fuction(number){
    
    
				return number % 2 == 0
			})
		}
	}

在计算属性不适应的情况下,如在嵌套v-for循环中,也可以:

	<li v-for="n in even(numbers)">{
    
    {
    
    n}}</li>
	......
	
	data:{
    
    
		numbers:[1,2,3,4,5]
	}, 
	methods:{
    
     
		even: function(numbers){
    
      
			return numbers.filter(fuction(number){
    
    
				return number % 2 == 0
			})
		}
	}

遍历整数

v-for指令可以接受整数。这种情况下,渲染时会把模板重复对应的次数。

	<div>
		<--遍历110-->
		<span v-for="n in 10">{
    
    {
    
    n}}</span>
	</div>    

输出结果是:1 2 3 4 5 6 7 8 9 10

v-for遍历对象

遍历对象的语法和遍历数组的一样,即value in object,其中object是被迭代的对象,value是被迭代的对象属性的别名。

	<li v-for="value in book">{
    
    {
    
    value}}</li>
	data:{
    
    
		book:{
    
    
			title:'vc++',
			author:'sun',
			isbn:'123232'
		}
	}

如果要获取对象的属性名,可以使用可选的属性名参数(键名)作为第二个参数。

	<li v-for="(value, key) in book">{
    
    {
    
    key}} : {
    
    {
    
    value}}</li>  

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

	<li v-for="(value, key, index) in book">{
    
    {
    
    index}}.{
    
    {
    
    key}} : {
    
    {
    
    value}}</li>  

对象更新检测

由于JavaScript的限制,Vue不能检测对象属性的添加和删除。可以使用Vue全局的set()和delete()方法来添加和删除属性,并触发视图更新。

	Vue.set(vm.book,  'pulishdate', '2019-06-01')    //使用Vue全局set()方法添加属性
	vm.set(vm.book,   'pulishdate', '2019-06-01')    //使用Vue实例的set()方法添加属性
	Vue.delete(vm.book,  'isbn')    //使用Vue全局delete()方法删除属性
	vm.delete(vm.book,   'isbn')    //使用Vue实例的delete()方法删除属性

<temple>上使用v-for

类似v-show和v-if,可以渲染一段包含多个元素的内容。

<template v-for="item in items"><li>{
    
    {
    
    item.msg}}</li><li>{
    
    {
    
    item.code}}</li></template>

key属性(用来提醒Vue跟踪改变的元素)

当Vue正在更新使用v-for渲染的元素列表时,默认使用“就地更新”策略。如果数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素,并确保它们在每个索引位置正确渲染。
当勾选后,指令记住的是勾选数组下标0,当添加新元素后,虽然数组长度发生变化,但是还是记得勾选下标0的数据,就出现问题了。
为了给Vue提示跟踪每个节点的身份,从而重用和重新排序现有元素,需要为列表每一项提供一个唯一key属性。
key属性的类型只能是string或number类型。

	<p v-for="book in books" v-bind:key="book.id">

v-for与v-if一同使用

当v-for与v-if一起使用时,v-for优先级高于v-if,这意味着v-if将分别重复运行于每个v-for循环中。即如果需要对列表的某些项根据条件判断是否渲染,可以将v-if联合v-for一起使用。

	<li v-for="plan in plans" v-if="plan.isComplete">{
    
    {
    
    plan.content}}</li> //会对循环的每一个plan分别判断if

如果仅根据某个条件的真假决定是否跳过整个循环的执行,可以将v-if置于外层元素(或<template>)上,如:

	<ul v-if="plans.length">
		<li v-for="plan in plans">{
    
    {
    
    plan.content}}</li>
	</ul>
	<p v-else>没有工作计划</p>

4、v-bind(单向绑定数据)

用于响应更新HTML元素的属性,将一个或多个属性或一个组件的prop动态绑定到表达式。

	<--绑定一个属性-->
	<img v-bind:src="imgSrc">
    <--缩写-->
    <img  :src="imgSrc">
    <--动态属性名-->
    <a v-bind:[attrname]="url"></a>
    <--内联字符串拼接-->
    <img  :src="'images/'+file/Name">
    
    data:{
    
    
    	attrname:'href',
    	url:'http://www.sina.com.cn/', 
    	imgSrc:'images/bg.jpg', 
    	fileName:'bg.jpg'
    }

v-bind指令还可以直接绑定一个有属性的对象,如:

	<form v-bind="formObj"></form>
	data:{
    
    
		formObj:{
    
    
			method:'get, action:'#'
		}
	}

5、v-model(双向绑定数据)

v-model指令用来在表单<input><textarea><select>元素上创建双向数据绑定,它会根据控件类型自动选取正确的方法来更新元素。

本质上是语法糖,指计算机语言中添加的某种语法,对语言功能没有影响,但是更方便程序员使用。通常会增加程序的可读性,从而减少代码出错的可能。

负责监听用户的输入事件以更新数据,并对一些极端场景进行特殊处理。

	<input type="text" v-model="message">
	
    data:{
    
    
    	message:'hello world'
    }

输入框会显示默认message内容,用户输入后,message的值会更新成用户输入的内容。如果直接改变message的值,如:vm.message=“welcome”,输入框内的值也会改变——双向绑定。

6、v-on(监听触发事件)

v-on指令用于监听DOM事件,并在触发时运行一些JavaScript代码。
v-on指令的表达式可以是一段JavaScript代码,也可以是一个方法名字或方法调用语句。

  • ①click事件直接使用JavaScript语句:
	<button v-on:click="count+=1">Add 1</button>
  • ②click事件直接绑定一个方法:
	<button v-on:click="greet">Greet</button>
	<--缩写语法-->
	<button @click ="greet">Greet</button>
  • ③click事件使用内联语句调用方法:
<button v-on:click="say('hi')">hi</button>

在选项对象的methods属性对象中定义方法:

methods:{
    
    
	greet:function(){
    
    
		alert(this.message)
	},
	say(msg){
    
    
		alert(msg)
	}
}   //方法内this指向vm

方法在选项对象的methods属性中定义,该属性是对象属性,其中的方法可以通过Vue实例来访问。

	vm.greet();
	vm.say("zhangsan");

注意:1、不要使用箭头函数来定义methods方法,(如:plus:()=>this.a++)。因为箭头函数绑定的是父级作用域的上下文,所以this不会按照期望指向Vue实例,this.a是undefined。

2、实际开发中,不常用直接书写JavaScript语句(①),常用②③形式。

3、在页面渲染的结果中不会渲染出调用事件后的JavaScript事件属性。

4、在Vue实例销毁时,所有事件处理器都会被自动删除。

如果在事件绑定的方法中需要访问原始的DOM事件,可以使用特殊变量$event把它传入方法。

根据条件决定是否跳转时,如果条件不足,可以调用事件对象的preventDefault()方法组织跳转。

在使用v-on指令绑定事件处理器时,就可以使用$event传入原始的DOM事件对象,然后在事件处理器方法中访问原生的事件对象。

	<a href="/login" v-on:click="login($event)">登录</a>
	methods:{
    
    
		login(event){
    
      
			...  
			event.preventDefault();
		}
	}

使用事件修饰符.prevent效果一样

	<a href="/login" v-on:click.prevent="login">登录</a>
	methods:{
    
    
		login(event){
    
      
			...  
		}
	}

如果要访问事件绑定的原始DOM原始节点对象,可以调用event.currentTaarget得到。

1. 事件修饰符

在事件处理程序中调用event.preventDefault()或event.stopPropagation()是非常常见的需求。Vue.js提供事件修饰符,专注数据逻辑,不需考虑如何处理DOM事件细节。

修饰符由圆点(.)开头的指令后缀表示,紧接在事件名称后。

针对v-on指令提供的修饰符:

	.stop:调用event.stopPropagation()
    .prevent:调用event.preventDefault()
    .capture:添加事件监视器时使用capture模式
    .self:只当事件是从侦听器绑定的元素本身触发时才触发回调。
    .{
    
    keyCode|keyAlias}:只当事件是从特定按键触发时才触发回调。
    .native:监听组件根元素的原生事件。
    .once:只触发一次回调
    .left:只当按鼠标左键时触发
    .right:只按右键触发
    .middle:只按鼠标中键触发
    .passive:以{
    
    passive:true}模式添加侦听器

用法:

	<--阻止单击事件继续传播:-->
	<a v-on:click.stop="dothis"></a>
	<--提交事件不再重新加载页面:-->
	<form v-on:submit.prevent="dothis"></form>
	<--修饰符可以串联:-->
	<a v-on:click.stop.prevent="dothis"></a>
	<--只有修饰符:--><form v-on:submit.prevent></form>
	<--添加事件监听器时使用事件捕获模式:即内部元素触发的事件先在该事件处理函数中处理,然后交由内部元素进行处理。-->
	<div v-	on:click.capture="dothis">...</div>
	<--只当在event.target是当前元素自身时触发处理函数:即事件不是从内部元素触发的:-->
	<div v-on:click.self="dothat">...</div>
	<--单击事件处理函数只处理一次:-->
	<a c-on:click.onc="do"></a>

说明:
1、DOM事件规范支持两种事件模型,即捕获型事件和冒泡型事件,捕获型事件从最外层的对象(大部分兼容标准的浏览器使用window对象作为最外层对象)开始,直到引发事件的对象;冒泡型事件从引发事件的对象开始,一直向上传播,直到最外层的对象结束。任何发生在DOM事件模型中的事件,首先进入捕获阶段,直到达到目标阶段,再进入冒泡阶段。v-on指令提供的.stop和.capture修饰符即与此有关。
2、修饰符可以串联在一起使用,但顺序很重要,例如,v-on:click.prevent.self会阻止所有的单击,而v-on:click.self.prevent只会阻止对元素自身的攻击。
3、如果某个事件只需要响应一次,可以使用.once修饰符。

2. 按键修饰符

在监听键盘事件时,经常需要检查详细的按键,为此,可以在v-on监听键盘事件时添加按键修饰符。

	<--只有在按键是回车键时调用submit()方法:-->
	<input v-on:keyup.enter="submit">
	<--使用回车键的按键码:-->
	<input v-on:keyup.13="submit">

常用按键码别名

  • .enter

  • .tab

  • .delete

  • .esc

  • .space

  • .up

  • .down

  • .left

  • .right

系统修饰键:实现仅在按下相应按键才能触发鼠标或键盘事件的监听器

  • .ctrl

  • .alt

  • .shift

  • .meta(Windows系统键盘对应Windows徽标键,Mac系统键盘对应command键,Sun操作系统键盘对应实心宝石键)

例如:

	<--Alt+C:-->
	<input @keyup.alt.67="clear">
    <--Ctrl+C:-->
    <input @keyup.ctrl.67="clear">

注意:修饰键与常规按键并不相同,在和keyup事件一起使用时,事件触发时修饰键必须处于按下状态。上述代码中,当同时按下Ctrl+c时,do()方法并不会被调用,只有在按住Ctrl键同时释放字母C键才能触发方法调用。要想同时按下Ctrl+某个键时触发keyup.ctrl,需要使用Ctrl的虚拟键代码17来代替Ctrl修饰键。如下:

	<input @keyup.17.67="clear">
	<--Ctrl+click:-->
	<div @click.ctrl="do"></div>

3. .exact修饰符

.exact修饰符是2.5.0.版本中新增的,用于精确控制系统修饰符组合触发的事件。

	<--即使同时按下Alt键或shift键,也会触发:-->
	<button @click.ctrl="onClick">A</button>
    <--只有在按住Ctrl键而不按其他键时才会触发:-->
    <button @click.ctrl.exact="onClick">A</button>
    <--只有在没有按下系统修饰键时才会触发:-->
    <button @click.exact="onClick">A</button>

4. 鼠标按钮修饰符(2.2.0版本新增)

  • .left 鼠标左键

  • .right 鼠标右键

  • .middle 鼠标中键

	<input @click.right="do"> //只有在鼠标右键单击时才会触发事件处理函数

7、v-text(更新元素的文本内容)

v-text指令用于更新元素的文本内容

	<span v-text="message"></span>

等价于:

	<span v-text>{
    
    {
    
    message}}</span><span>{
    
    {
    
    message}}</span>

如果只是更新部分文本内容,还是用{ {Mustache}}插值形式。

v-text和插值表达式{ { }}的区别

v-text会覆盖元素中原本的内容,插值表达式{ { }}只会替换自己的这个占位符,不会把整个元素内容替换。

8、v-html(更新元素的innerHTML)

v-html指令用于更新元素的innerHTML,也就是说,插入的符合HTML代码格式的文本会被编译。

	<div v-html="html"></div>
	<script>
		var vm=new Vue({
    
    
			el:'#app',
			data:{
    
    
				html:"<h1>123456</h1>"
			}
		})
	</script>

相当于

	<div><h1>123456</h1></div>  

注意:在网站上动态渲染任意的HTML是非常危险的,因为很容易导致XSS攻击。切记,只在可信的内容上使用v-html,永远不要在用户提交的内容上使用v-html。

9、v-once(让元素或组件只渲染一次)

v-once指令可以让元素或组件只渲染一次,该指令不需要表达式。之后再渲染时,元素、组件及其所有子节点将被视为静态内容并跳过。可以用于优化更新性能。

	<a v-for="n in navs" href="nav.url" v-once>{
    
    {
    
    n.name}}</a>

使用了v-once指令后,修改数组navs的内容后不会引起视图更新,因为只渲染一次,渲染结果在之后会作为静态内容存在

10、v-pre

v-pre指令也不需要表达式,用于跳过这个元素和它的子元素的编译过程。
v-pre指令可以用来显示原始Mustache标签。
对于大量没有指令的节点使用v-pre指令可以加快编译速度。

<h1 v-pre> {
    
    {
    
    message}}</h1>

输出结果为{ {message}}

11、v-cloak

不需要表达式。该指令保持在元素上直到关联实例编译结束,编译结束后该指令被移除。
和CSS规则如[v-cloak]{display:none}一起使用时,可以隐藏未编译的Mustache标签直到实例准备完毕。

	<html>
 		<head>
 			<style>[v-cloak]{
    
    display:none;}</style>
 		</head>
 		<body>
 			<h1 v-cloak>{
    
    {
    
    message}}</h1>
 		</body>
 	</html>

浏览器在加载页面时,如果网速慢或页面大,那么会直接先显示{ {message}}字样,直到Vue的JS文件加载完毕,Vue实例创建、模板编译后,才会被替换为数据对象的内容,页面就会有闪烁,这对用户体验不好。加上CSS规则[v-cloak]{display:none},配合v-cloak指令一起使用,就可以解决这个问题。
在vue.js独立版本的页面开发中,使用v-cloak解决初始化慢导致的页面闪烁非常有效。
但在较大项目中,采用模块化开发,项目主页只有一个空的div元素,剩下的内容都是由路由去挂载不同的组件来完成的,就没有必要使用v-cloak指令。

12、v-slot

v-slot指令用于提供命名的插槽或需要接受prop的插槽。

二、自定义指令

某些情况下,希望对普通的DOM元素进行底层操作,需要用到自定义指令。只应该用于封装对DOM的操作。简化代码编写,提高重用性。

1、自定义指令的注册

自定义指令需要注册后才能使用,Vue提供两种注册方式

1. 全局注册

使用Vue.directive()方法来注册一个全局自定义指令。
该方法接受两个参数,第一个参数是指令的ID(即名字),第二个参数是一个定义对象或函数对象,指令要实现的功能在这个对象中定义。
语法形式:Vue.directive(id, [definition])
如:Vue.directive('focus',{...})
全局指令可以在任何Vue实例的模板中使用。

	<div id="app"><input v-focus></div>
    <div id="app2"><input v-focus></div>
    <script>
    	Vue.directive('focus',{
    
    ...})    
    	new Vue({
    
    el:'#app'})   
    	new Vue({
    
    el:'#app2'})
    </script>

2. 局部注册

在Vue实例的选项对象中使用directives选项进行注册

	new Vue({
    
    
		el:'#app',    
		directives:{
    
    
			//注册局部自定义指令    
			focus:{
    
    ...}
		}
	})

局部注册的自定义指令只能在该实例绑定的视图中使用。

2、钩子函数

自定义指令的功能是在定义对象中实现的,而定义对象则是由钩子函数组成的。
Vue提供的钩子函数:

  • bind:只调用一次,指令第一次绑定到元素时调用。指令如果需要一些一次性的初始化设置,可以放到这个钩子函数。
  • inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document对象中)
  • update:所在组件的VNode更新时调用(无论指令的值是否发生了变化),但是可能发生在其子VNode更新之前。可以通过比较更新前后的值来忽略不必要的模板更新。
  • componentUpdate:指令所在组件的VNode及其子VNode全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

根据指令要实现的功能不同,选择相应的钩子函数来编写代码。

如实现v-focus指令,在元素插入父节点时自动获得焦点,可以在inserted钩子函数中编写自动聚焦的代码:

	Vue.directive('focus',{
    
    
		//当绑定元素插入父节点时    
		inserted: function(el){
    
    
			//聚焦元素    
			el.focus()
		}
	})

指令的钩子函数可以带有一些参数,如:

  • el:指令所绑定的元素,可以直接操作DOM。
  • binding:一个对象,包含属性:
  • name:指令名,不包括v-前缀。
  • value:指令的绑定值。如v-my-directive=“1+1”中,value值为2。
  • oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。
  • expression:字符串形式的指令表达式。如v-my-directive=“1+1”中,expression值为“1+1”。
  • arg:传给指令的参数,可选。例如v-my-directive:foo中,arg值为“foo”。
  • modifiers:一个包含修饰符的对象。如v-my-directive.foo.bar中,modifiers对象的值为{foo:true,bar:true}。
  • vnode:Vue编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。

注意:除了el参数外,其他参数都应该是只读的,切勿进行修改。

3、动态指令参数

自定义指令也可以使用动态参数。如v-my-directive:[argument]="value"中,argument参数可以根据组件实例数据进行更新。

4、函数简写

如果自定义指令在bind和update钩子函数中的行为一致,且只需要用到这两个钩子函数,可以只传递一个函数对象作为参数。

	Vue.directive('focus', 
		function(el){
    
    
			el.focus()
		}
	)

5、对象字面量

如果指令需要多个值,可以传入一个JavaScript对象字面量。指令可以接受所有合法的JavaScript表达式。

	<div v-demo="{color:'white', text:'hello!'}"></div>

猜你喜欢

转载自blog.csdn.net/qq_45484237/article/details/121081241