1. sync
我們都知道vue中有單項(xiàng)數(shù)據(jù)流的概念,即數(shù)據(jù)只能有父組件傳遞給子組件,在子組件中,不能修改父組件的數(shù)據(jù)。如果非要這么做,那么可以在子組件中觸發(fā)($emit)一個(gè)事件,然后再父組件中監(jiān)聽。再然后觸發(fā)改變,最后又經(jīng)過props傳遞至子組件
<body>
<div id="app">
<child @add="addOne" :num="count"></child>
</div>
<script>
Vue.component('child', {
template: `
<div class="child">
<button @click="$emit('add')">{{num}}</button>
</div>
`,
props: ['num']
})
new Vue({
el: '#app',
data: {
count:0
},
methods: {
addOne() {
this.count += 1;
}
}
})
</script>
</body>
以上例子相當(dāng)簡單,如果碰到稍微復(fù)雜的改值操作,使用emit這種方式就非常繁瑣。這時(shí),.sync就派上用場了。其實(shí)它只是emit的語法糖而已,不過,對(duì)我們來說,卻實(shí)實(shí)在在的簡化了代碼。
<div id="app">
<child :title.sync="doc.title"></child> <!--在需要雙向綁定的prop后加.sync-->
</div>
<script>
Vue.component('child', {
template: `
<div class="child">
<button @click="doSth">{{title}}</button>
</div>
`,
props: ['title'],
methods: {
doSth() {
this.$emit('update:title', '新標(biāo)題') // 固定寫法“updata:”+要修改的props
}
}
})
new Vue({
el: '#app',
data: {
doc: {
title: '標(biāo)題'
}
}
})
</script>
//其實(shí):propName.sync="xxx"就相當(dāng)于“:propName="xxx" @update:propName="xxx=$event"”,即在父組件模板中監(jiān)聽數(shù)據(jù)更新事件。
2. $attrs
在使用vue時(shí),如果在父組件模板中,給子組件添加了屬性,這些屬性一般會(huì)自動(dòng)加在子組件的根元素上,比如:
<div id="app">
<child abc-def disabled :title.sync="doc.title"></child> <!--這里,我們添加了“abc-def”和disabled-->
</div>
<script>
Vue.component('child', {
template: `
<div class="child">
<button @click="doSth">{{title}}</button>
</div>
`,
...
})

可以看到,disabled和abc-def默認(rèn)加在了根元素上。可是如果我們要加在特定元素上呢?比如一個(gè)input標(biāo)簽上,我們需要傳入placeholder,value,disabled這些屬性,并且不能加到根元素上。像這樣:
<div id="app">
<super-input placeholder="輸入用戶名" title="userName" type="text" v-model="name"></super-input>
</div>
<script>
Vue.component('superInput', {
template: `
<div class="ipt">
<label>用戶名:<input ><label>
</div>
`
})
如果你不希望組件的根元素繼承特性,你可以在組件的選項(xiàng)中設(shè)置 inheritAttrs: false。 ——vue中文文檔
注意 inheritAttrs: false 選項(xiàng)不會(huì)影響 style 和 class 的綁定。
inheritAttrs一般用來配合 $attrs 來完成特殊屬性傳遞。
就拿上邊的例子來說,vue會(huì)把 placeholder="輸入用戶名" title="userName" type="text" v-model="name"拆解成一個(gè)對(duì)象,并且賦值給$attrs:
{
placeholder: '輸入用戶名',
title: 'userName',
type: 'text',
value: 'name' // v-model相當(dāng)于bind+input,大致就相當(dāng)于于get和set吧,這里只用bind的值
}
// 然后,子組件中要這樣使用
<div id="app">
<super-input placeholder="輸入用戶名" title="userName" type="text" v-model="name"></super-input>
</div>
<script>
Vue.component('superInput', {
inheritAttrs: false,
template: `
<div class="ipt">
<label>用戶名:<input v-bind="$attrs" @input="$emit('input', $event.target.value)"></label> <!--前文解釋過了,此處的@input相當(dāng)于v-model的另一半。$attrs接收了福組件模板傳遞來的屬性,并且轉(zhuǎn)為對(duì)象-->
</div>
`
})
let vm = new Vue({
el: '#app',
data: {
name: 'John'
}
})

屬性正確地被綁在了需要綁的元素上(包括value)!
需要注意的是,props和$attrs是互斥的,如果給一個(gè)子組件傳遞了很多屬性,如果子組件中沒有props,那么這些屬性都會(huì)出現(xiàn)在子組件的$attrs上,相應(yīng)的,如果某個(gè)屬性已經(jīng)被props接收了,那么attrs上便不存在了
3. $listeners
$listeners與$attrs類似,$attrs包含了父組件傳遞給子組件的所有屬性,而$listeners包含了父傳給子組件的所有事件
<div id="app">
<my-button @click="change(111)" @mouseup="change(222)"></my-button>
</div>
<script>
new Vue({
el: "#app",
components: {
"myButton": {
template: `
<div>
<button @click="$listeners.click">點(diǎn)我</button> //只能觸發(fā)父組件的click
<button @click="$emit('click')">點(diǎn)我1</button> //只能觸發(fā)父組件的click
<button v-on="$listeners">點(diǎn)我2</button> //相當(dāng)于 v-on:click="$listeners.click" + v-on:mouseup="$listeners.mouseup"
</div>
`,
mounted() {
console.log(this.$listeners)
}
}
},
methods: {
change(a) {
alert(a)
}
}
})
</script>
所以,$listeners可以用來批量觸發(fā)事件