.native
vue自定义组件中我们一般在父组件中定义一个事件,然后在子组件中利用emit触发这个事件。此时,如果我们的父组件中的事件用.native修饰,那么就相当于这个事件给子组件的根元素绑定了。
- <template>
- <div>
- <child @click.native='click'></child>
- </div>
- </template>
- <script>
- import child from './child'
- export default{
- name:'index',
- components:{child},
- methods:{
- click(ev){
- console.log('click');
- }
- }
- }
- </script>
- <template>
- <div>
- <input type='text' />
- </div>
- </template>
- <script>
- export default {
- name: 'child',
- }
- </script>
<!--父组件-->
<template>
<div>
<child @click.native='click'></child>
</div>
</template>
<script>
import child from './child'
export default{
name:'index',
components:{child},
methods:{
click(ev){
console.log('click');
}
}
}
</script>
<!--子组件-->
<template>
<div>
<input type='text' />
</div>
</template>
<script>
export default {
name: 'child',
}
</script>
加了.native后,相当于给子组件的根元素绑定了事件,也就是上面的input外的div元素。
$listeners
上面.native表示将事件绑定到子组件的根元素上,那如果不想绑定到根元素上,除了用emit主动触发外,还可以用v-on='$listeners',这个操作就表示把外层的事件绑定到自己这。
- <template>
- <div>
- <child @click='click'></child>
- </div>
- </template>
- <script>
- import child from './child'
- export default{
- name:'index',
- components:{child},
- methods:{
- click(ev){
- console.log('click');
- }
- }
- }
- </script>
- <template>
- <div>
- <input type='text' v-on='$listeners'/>
- </div>
- </template>
- <script>
- export default {
- name: 'child',
- }
- </script>
<!--父组件-->
<template>
<div>
<child @click='click'></child>
</div>
</template>
<script>
import child from './child'
export default{
name:'index',
components:{child},
methods:{
click(ev){
console.log('click');
}
}
}
</script>
<!--子组件-->
<template>
<div>
<input type='text' v-on='$listeners'/>
</div>
</template>
<script>
export default {
name: 'child',
}
</script>
$listeners返回从父组件上绑定的各种事件。注意,$listeners中的事件不包括.native中的事件,因为.native事件已经给了子组件的根节点。
如果父组件中使用的是v-model,那么$listeners里面会包含一个input事件的方法,如果要让这个事件正常运转(把子组件的内容传达给父组件),需要稍微改写一下。
- <template>
- <div>
- <child v-model='name'></child>
- </div>
- </template>
- <script>
- import child from './child'
- export default{
- name:'index',
- components:{child},
- data(){
- return{
- name:'张三'
- }
- },
- }
- </script>
- <template>
- <div>
- <input type='text' v-on='inputListeners'/>
- </div>
- </template>
- <script>
- export default {
- name: 'child',
- computed: {
- inputListeners() {
- var vm = this
- // `Object.assign` 将所有的对象合并为一个新对象
- return Object.assign({},
- // 我们从父级添加所有的监听器
- this.$listeners,
- // 然后我们添加自定义监听器,
- // 或覆写一些监听器的行为
- {
- // 这里确保组件配合 `v-model` 的工作
- input: function(event) {
- vm.$emit('input', event.target.value)
- }
- });
- }
- }
- }
- </script>
<!--父组件-->
<template>
<div>
<child v-model='name'></child>
</div>
</template>
<script>
import child from './child'
export default{
name:'index',
components:{child},
data(){
return{
name:'张三'
}
},
}
</script>
<!--子组件-->
<template>
<div>
<input type='text' v-on='inputListeners'/>
</div>
</template>
<script>
export default {
name: 'child',
computed: {
inputListeners() {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function(event) {
vm.$emit('input', event.target.value)
}
});
}
}
}
</script>
因为默认的v-model中的input事件传递的是事件对象本身,我们需要将$listeners中的input事件改写成传递内容的。
$attrs,$props,inheritAttrs
从父组件中通过v-bind,v-model或者直接写的属性值在子组件中都会在$attrs中(style和class不在其中),子组件中可以通过配置props定义子组件中的变量,props最大选择就是$attrs,props每选一个,$attrs中就少一个变量了。因此$props和$attrs的合集就是父组件传递下来的所有属性。
默认情况下$attrs属性(注意不是所有属性)会继承到子组件的根元素上,如果不想$attrs属性继承到子组件的根元素上,可以在子组件中设置inheritAttrs:false,同时可以用v-bind='$attrs'将$attr属性给到某个指定的元素上。(无论inheritAttrs设置true或false,class和style都不受的影响,它们会和子组件的根元素的class和style内容合并)
- <template>
- <div>
- <child :name='name' title='title'></child>
- </div>
- </template>
- <script>
- import child from './child'
- export default{
- name:'index',
- components:{child},
- data(){
- return{
- name:'张三',
- }
- },
- }
- </script>
- <template>
- <div>
- <input type='text' v-bind='$attrs'/>
- </div>
- </template>
- <script>
- export default {
- inheritAttrs: false,
- name: 'child',
- props:['name'],
- }
- </script>
<!--父组件-->
<template>
<div>
<child :name='name' title='title'></child>
</div>
</template>
<script>
import child from './child'
export default{
name:'index',
components:{child},
data(){
return{
name:'张三',
}
},
}
</script>
<!--子组件-->
<template>
<div>
<input type='text' v-bind='$attrs'/>
</div>
</template>
<script>
export default {
inheritAttrs: false,
name: 'child',
props:['name'],
}
</script>
上面的例子中,父组件传了两个属性,name和title,props中取了一个name属性,那么$attrs中还剩下title。同时我们用inheritAttrs:false不让子组件的根元素继承$attr属性,并利用v-bind='$attr'把$attrs属性放到了input上。
v-bind.sync='***'
.sync和v-model比较像,可以同时绑定属性值和一个回调方法,例如v-bind:name.sync='name',那么相当于v-bind:name='name',v-on:update:name='name=$event'。也就是说在子组件中,可以通过emit('input:name',event.target.value)给父组件传回值。
比较注意的是bind.sync='obj'这种写法,这里的obj是一个对象,内部会把对象展开,然后每个属性执行一次v-bind:[key].sync='[key]'。
- <template>
- <div>
- <child v-bind.sync='obj'></child>
- </div>
- </template>
- <script>
- import child from './child'
- export default{
- name:'index',
- components:{child},
- data(){
- return{
- obj:{
- name:'张三',
- age:20
- }
- }
- },
- }
- </script>
- <template>
- <div>
- <span>{{name}}</span>
- <span>{{age}}</span>
- <button @click='$emit("update:name","李四")'>change name</button>
- <button @click='$emit("update:age",30)'>change age</button>
- </div>
- </template>
- <script>
- export default {
- name: 'child',
- props:['name','age'],
- }
- </script>
<!--父组件-->
<template>
<div>
<child v-bind.sync='obj'></child>
</div>
</template>
<script>
import child from './child'
export default{
name:'index',
components:{child},
data(){
return{
obj:{
name:'张三',
age:20
}
}
},
}
</script>
<!--子组件-->
<template>
<div>
<span>{{name}}</span>
<span>{{age}}</span>
<button @click='$emit("update:name","李四")'>change name</button>
<button @click='$emit("update:age",30)'>change age</button>
</div>
</template>
<script>
export default {
name: 'child',
props:['name','age'],
}
</script>