1. sync
我们都知道vue中有单项数据流的概念,即数据只能有父组件传递给子组件,在子组件中,不能修改父组件的数据。如果非要这么做,那么可以在子组件中触发($emit)一个事件,然后再父组件中监听。再然后触发改变,最后又经过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>
以上例子相当简单,如果碰到稍微复杂的改值操作,使用emit这种方式就非常繁琐。这时,.sync就派上用场了。其实它只是emit的语法糖而已,不过,对我们来说,却实实在在的简化了代码。
<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', '新标题') // 固定写法“updata:”+要修改的props
}
}
})
new Vue({
el: '#app',
data: {
doc: {
title: '标题'
}
}
})
</script>
//其实:propName.sync="xxx"就相当于“:propName="xxx" @update:propName="xxx=$event"”,即在父组件模板中监听数据更新事件。
2. $attrs
在使用vue时,如果在父组件模板中,给子组件添加了属性,这些属性一般会自动加在子组件的根元素上,比如:
<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默认加在了根元素上。可是如果我们要加在特定元素上呢?比如一个input标签上,我们需要传入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>
`
})
如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false。 ——vue中文文档
注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。
inheritAttrs一般用来配合 $attrs
来完成特殊属性传递。
就拿上边的例子来说,vue会把 placeholder="输入用户名" title="userName" type="text" v-model="name"
拆解成一个对象,并且赋值给$attrs:
{
placeholder: '输入用户名',
title: 'userName',
type: 'text',
value: 'name' // v-model相当于bind+input,大致就相当于于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相当于v-model的另一半。$attrs接收了福组件模板传递来的属性,并且转为对象-->
</div>
`
})
let vm = new Vue({
el: '#app',
data: {
name: 'John'
}
})
属性正确地被绑在了需要绑的元素上(包括value)!
需要注意的是,props和$attrs是互斥的,如果给一个子组件传递了很多属性,如果子组件中没有props,那么这些属性都会出现在子组件的$attrs上,相应的,如果某个属性已经被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">点我</button> //只能触发父组件的click
<button @click="$emit('click')">点我1</button> //只能触发父组件的click
<button v-on="$listeners">点我2</button> //相当于 v-on:click="$listeners.click" + v-on:mouseup="$listeners.mouseup"
</div>
`,
mounted() {
console.log(this.$listeners)
}
}
},
methods: {
change(a) {
alert(a)
}
}
})
</script>
所以,$listeners可以用来批量触发事件