這篇文章主要介紹了Vue2.4+新增屬性.sync、$attrs、$listeners的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨着小編來一起學習學習吧
sync
在vue2.4以前,父組件向子組件傳值用props;子組件不能直接更改父組件傳入的值,需要通過$emit觸發自定義事件,通知父組件改變後的值。比較繁瑣,寫法如下:
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
36
37
38
39
40
41
|
//父組件
<template>
<div class= "parent" >
<p>父組件傳入子組件的值:{{name}}</p>
<fieldset>
<legend>子組件</legend>
<child :val= "name" @update= "modify" >
</child>
</fieldset>
</div>
</template>
<script>
import Child from './Child'
export default {
components:{Child},
data () {
return {
name: 'linda'
}
},
methods:{
modify(newVal){
this .name=newVal
}
}
}
</script>
//子組件
<template>
<label class= "child" >
輸入框:
<input :value=val @input= "$emit('update',$event.target.value)" />
</label>
</template>
<script>
export default {
props:[ 'val' ]
}
</script>
|
vue2.4以後的寫法明顯舒服許多,上面同樣的功能,直接上代碼
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
36
|
//父組件
<template>
<div class= "parent" >
<p>父組件傳入子組件的值:{{name}}</p>
<fieldset>
<legend>子組件</legend>
<child :val.sync= "name" >
</child>
</fieldset>
</div>
</template>
<script>
import Child from './Child'
export default {
components:{Child},
data () {
return {
name: 'linda'
}
}
}
</script>
//子組件
<template>
<label class= "child" >
輸入框:
<input :value=val @input= "$emit('update:val',$event.target.value)" />
</label>
</template>
<script>
export default {
props:[ 'val' ]
}
</script>
|
寫法上簡化了一部分,很明顯父組件不用再定義方法檢測值變化了。其實只是對以前的$emit方式的一種縮寫,.sync其實就是在父組件定義了一update:val方法,來監聽子組件修改值的事件。
$attrs
想象一下,你打算封裝一個自定義input組件——MyInput,需要從父組件傳入type,placeholder,title等多個html元素的原生屬性。此時你的MyInput組件props如下:
1
|
props:[ 'type' , 'placeholder' , 'title' ,...]
|
很醜陋不是嗎?$attrs專門爲了解決這種問題而誕生,這個屬性允許你在使用自定義組件時更像是使用原生html元素。比如:
1
2
|
//父組件
<my-input placeholder= "請輸入你的姓名" type= "text" title= "姓名" v-model= "name" />
|
my-input的使用方式就像原生的input一樣。而MyInput並沒有設置props,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<template>
<div>
<label>輸入框:</label><input v-bind= "$attrsAll" @input= "$emit('input',$event.target.value)" />
</div>
</template>
<script>
export default {
inheritAttrs: false ,
computed: {
$attrsAll() {
return {
value: this .$vnode.data.model.value,
... this .$attrs
}
}
}
}
</script>
|
基礎掃盲
v-model是v-bind:value和v-on:input的簡寫,所以在父組件你完全可以直接寫 :value="name",@input="val => name = val"。查看文檔
疑難
引用下vue的官方api中對$attrs的說明
$attrs包含了父作用域中不作爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)
比較迷惑的一點是給子組件設置:value="name"相當於給子組件設置props:['value'],所以在MyInput中直接從$attrs獲取不到value,需要重新包裝$attrsAll,添加value屬性。所以子組件還有下面寫法,我傾向於這種寫法,因爲它更優雅
1
2
3
4
5
6
7
8
9
10
11
|
<template>
<div>
<label>輸入框:</label><input v-bind= "$attrs" :value= "value" @input= "$emit('input',$event.target.value)" />
</div>
</template>
<script>
export default {
inheritAttrs: false ,
props:[ 'value' ]
}
</script>
|
$listener
同上面$attrs屬性一樣,這個屬性也是爲了在自定義組件中使用原生事件而產生的。比如要讓前面的MyInput組件實現focus事件,直接這麼寫是沒用的
1
|
<my-input @focus= "focus" placeholder= "請輸入你的姓名" type= "text" title= "姓名" v-model= "name" />
|
必須要讓focus事件作用於MyInput組件的input元素上,最終的MyInput源碼如下:
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
|
<template>
<div>
<label>輸入框:</label><input v-bind= "$attrsAll" v-on= "$listenserAll" />
</div>
</template>
<script>
export default {
inheritAttrs: false ,
props:[ 'value' ],
computed:{
$attrsAll() {
return {
value: this .value,
... this .$attrs
}
},
$listenserAll(){
return Object.assign(
{},
this .$listeners,
{input:(event) => this .$emit( 'input' ,event.target.value)})
}
}
}
</script>
|