1. 程式人生 > >【vue筆記2】-自定義指令

【vue筆記2】-自定義指令

基礎

除了內建指令,Vue.js也允許註冊自定義指令。自定義指令提供一種機制將資料的變化對映為DOM行為。 Vue.js用Vue.directive(id,definition)方法註冊一個全域性自定義指令,它接受兩個引數:指令ID與定義物件。也可以用元件的directives選項註冊一個區域性自定義指令(此方法相當於AngularJS restrict)屬性值為A。

  1. 鉤子函式 bind——只調用一次,在指令第一次繫結到元素上時呼叫。 update——在bind之後立即以初始值為引數第一次呼叫,之後每當繫結值變化時呼叫,引數為新值與舊值。 unbind——只調用一次,在指令從元素上解綁時呼叫。
Vue.directive('my-directive',{
	bind:function(){
		//準備工作
		//例如,新增事件處理器或只需要執行一次的高耗任務
	},
	update:function(){
		//值更新時的工作
		//也會以初始值為引數呼叫一次
	},
	unbind:function(){
		//清理工作
		//例如,刪除bind()新增的事件監聽器
	}
})

在註冊之後,便可以在Vue.js模板中這樣用(記著新增字首-v):

<div v-my-directive="someValue"></div>

當值需要update函式時,可以傳入一個函式替代定義物件:\

Vue.directive('my-directive',function(value){
	//這個函式用作update()
})
  1. 指令例項屬性 所有的鉤子函式都將被複制到實際的指令物件中,在鉤子內this指向這個指令物件。這個物件暴露了一些有用的屬性: el——指令繫結的元素。 vm——擁有該指令的上下文ViewModel. expression——指令的表示式,不包括引數和過濾器。 arg——指令的引數。 name——指令的名字,不包括字首。 modifiers——一個物件,包括指令的修飾符。 descriptor——一個物件,包括指令的解析結果。 注:我們應當將這些屬性視為只讀,不要修改它們。我們也可以給指令物件新增自定義屬性,但是注意不要覆蓋已有的內部屬性。程式碼示例如下:
<body id="example" @click=up">
	<div id="demo" v-demo:hello.a.b="msg"></div>
</body>
<sciprt>
	Vue.directive('demo',{
		bind:function(){
			console.log('demo bound');
		},
		update:function(value){
			this.el.innerHTML = 
				'name-'+this.name+'</br>'+
				'expression-'+this.expression+'</br>'+
				'argument-'+this.arg+'</br>'+
				'modifiers-'+JSON.stringify(this.modifiers)+'</br>'+
				'value-'+this.value+'</br>'+
				'vm-msg-'+this.vm.msg+'</br>'+
</script>
var demo = new Vue({
	el:'#example',
	data:{
		msg:'hello!'
	},
	methods:{
		up:function(){
			console.info('click');
		}
	}
}):

指令輸出結果: name-demo expression-msg argument-hello modifiers-{“b”:true,“a”:true} value-hello! vm-msghello!

  1. 物件字面量 如果指令需要多個值,則可以傳入一個JavaScript物件字面量。記住,指令可以使用任意合法的JavaScript表示式。程式碼示例如下:
<body>
	<div id="demo" v-demo="{color:'white',text:'hello'}"></div>
</body>
<script>
	Vue.directive('demo',function(value){
		console.log(value.color) //white
		console.log(value.text) //hello!
	})
	var demo = new Vue({
		el:'#demo'
	})
</script>

輸出結果: white hello!

  1. 字面修飾符

當指令使用了字面修飾符時,它的值將按普通字串處理並傳遞給update方法。update方法將只調用一次,因為普通字串不能響應資料變化。程式碼示例如下:

 <body>
 	<div id="demo" v-demo.literal="foo bar baz"></div>
 </body>
 <script>
	Vue.directive('demo',function(value){
		console.log(value)
	})
	var demo = new Vue({
		el:'#demo'
	})
</script>

foo bar baz

  1. 元素指令 有時我們想以自定義元素的形式使用指令,而不是以屬性的形式。這與AngularJS的E指令非常相似。元素指令可以看做是一個輕量元件。可以像下面這樣註冊一個自定義元素指令:
<body  id="demo">
	<my-directive class="hello" name="hi"></my-directive>
</body>
<script>
	Vue.elementDiective('my-directive',{
		//API同普通指令
		bind:function(){
			console.log(this.el.className)
			console.log(this.el.getAttribute("name"))
		}
	})
	var demo = new Vue({
		el:'#demo'
	})
</script>

元素指令不能接受引數或表示式,但是它可以讀取元素的特性,從而決定它的行為。 不同於普通指令,元素指令是終結性的。這意味著,一旦Vue遇到一個元素指令,它將跳過該元素及其子元素。

高階選項

Vue允許註冊自定義指令。自定義指令提供一種機制將資料的變化對映為DOM行為。

  1. params 自定義指令可以接受一個params陣列,指定一個特性列表,Vue編譯器將自動提取繫結元素的這些特性。程式碼例項如下:
<body id="demo">
	<my-directive class="hello" name="hi" a="params"></my-directive>
</body>
<script>
	Vue.elementDirective('my-directive',{
		params:['a'],
		//API同普通指令
		bind:function(){
			console.log(this.params.a);
			console..log(this.el.getAttribute("name"));
		}
	})
	var demo = new Vue({
		el:'#demo'
   })
<script>

此API也支援動態屬性。this.params[key]會自動保持更新。另外,可以指定一個回撥,在值變化的時候,程式碼如下:

<body id="demo">
	<my-directive class="hello" name="hi" a="params"></my-directive>
</body>
<script>
	Vue.elementDirective('my-directive',{
		params:['a'],
		paramWatchers:{
				a:function(val,oldVal){
					console.log('a changed!')
				}
		}
	})
	var demo = new Vue({
		el:'#demo',
		data:{
			someValue:'value'
		}
   })
<script>
  1. deep 如果自定義指令使用在一個物件上,當物件內部屬性變化時要觸發update,則在指令定義物件中指定deep:true
  2. twoWay 如果指令想向Vue例項寫回資料,則在指令定義物件中指定twoWay:true。該選項允許在指令中使用this.set(value)。程式碼示例如下:
<body id="demo">
	自定義元件:<input v-example="a.b.c"/><br/>
	父作用域:{a.b.c}
</body>
<script>
	Vue.directive('example',{
		twoWay:true,
		bind:function(){
			this.handler = function(){
				//把資料寫回vm
				//如果指令這樣繫結v-example="a.b.c"
				//這裡將會給vm.a.b.c賦值
				this.set(this.el.value);
			}.bind(this)
			this.el.addEventListener('input',this.handler);
		}
	})
		var demo = new Vue({
		el:'#demo',
		data:{
			a:{b:{c:2}}
		}
   })
</script>
  1. acceptStatement 傳入acceptStatement:true可以讓自定義指令接受內聯語句,就像v-on那樣。程式碼示例如下:
<body id="demo">
	<div v-my-directive="a++"></div>
	{{a}}
</body>
<script>
	Vue.directive('my-directive',{
		acceptStatement:true
		update:function(fn){
			//傳入值是一個函式
			//在呼叫它時將在所屬例項作用域內計算“a++”語句
			console.info(fn.toString());
			fn();
		}
	})
		var demo = new Vue({
		el:'#demo',
		data:{
			a:5
		}
   })
</script>
  1. Terminal(1.0.19及以後版本) Vue 通過遞迴遍歷DOM樹來編譯模組,但是當它遇到terminal指令時會停止遍歷這個元素的後代,這個指令將接管編譯這個元素及其後代元素的任務。v-if和v-for都是terminal指令。 編寫自定義terminal指令是一個高階話題,需要較好的理解Vue的程式設計流程,但並不是說不可能編寫自定義的terminal指令。用terminal:true指定自定義terminal指令,可能還需要用Vue.FragmentFactory來編譯partial。下面是一個自定義的terminal指令,它編譯其內容模板並將結果注入到頁面的另一個地方。
  2. priority 可以給指令指定一個優先順序。如果沒有指定優先順序,普通指令預設是1000,terminal指令預設是2000。同一個元素上優先順序高的指令會比其他指令處理得早一些,優先順序一樣的指令按照它在元素特性列表中出現的順序依次處理,但是不能保證這個順序在不同的瀏覽器中是一致的。 另外,流程控制指令v-if和v-for在編譯過程中始終擁有最高的優先順序。