$attrs/$listeners的使用场景 -- vue组件通信系列
vue组件的数据通信方式很多,本篇着重讲$attrs/$listeners
,神助是v-bind="$attrs"/v-on="$listeners
。
$attrs/$listeners
的常用场景:封装第三方组件或者表单组件,从而减少组件处理成本。
$attrs
使用组件的时候,vue内部会将组件上面的属性,自动会合并到组件内部根元素上面。
<template lang="pug"> list-item(a="a" b="b" c="c" style="color:red") </template> <!-- ListItem组件 --> <template lang="pug"> section(title="hello") 我是组件内部 </template>
渲染的时候会这样
<section title="hi" a="a" b="b" c="c">组件内部</section>
但这里限制在组件根元素上,若,组件内部其他元素也需要使用属性呢,此时就用到$attrs。
<template lang="pug"> section(title="hi") 组件内部 //- { "a": "a", "b": "b", "c": "c" } div {{$attrs}} </template>
这里如果不希望根元素继承那些属性,可以在组件内部配置inheritAttrs: false
$listeners
同理,对于事件,如果组件上面的事件是native模式,组件内部的最外层元素也会自动绑定该事件。
<template lang="pug"> list-item(@click.native="()=>{ title = 1}") </template> <!-- ListItem组件 --> <template lang="pug"> section 我是组件内部 </template>
渲染的时候,事件就会自动绑定到最外层元素上面
<section @click="()=>{ title = 1}">组件内部</section>
但这里任然限制在组件根元素上,若,组件内部其他元素也需要绑定事件呢,此时就用到$listeners,其是非native事件的合集。
网络异常,图片无法展示
|
<template lang="pug"> //- 父组件 div span {{title}} list-item( @click="()=>{ title = '点了 内部绑定事件的元素'}" @mousedown="()=>{ title = 'mousedown'}" @click.native="()=>{ title='点了 内部根元素'}") </template> <script> import ListItem from "@/components/ListItem"; export default { name: "List", components: { ListItem }, data() { return { title: "点击组件的不同元素修改标题" }; } }; </script>
<template lang="pug"> section(title="hi") 组件根元素 div(@click.stop="$listeners.click") 组件绑定事件的元素 </template> <script> export default { name: "ListItem", mounted() { // {click:fn,mousedown:fn} console.log("$listeners", this.$listeners); } }; </script>
使用场景:封装第三方组件、表单组件
封装第三方组件、或者表单组件的的时候使用,往往结合v-bind="$attrs"/v-on="$listeners"
,这样就不用考虑用户给组件传入什么属性或者事件了。
比如希望封装baidu-map
组件,而baidu-map
上面的属性和事件直接用他们自己库的。
<template lang="pug"> enforced-map(:title="title" @resizeMap="...") </template>
<!-- EnforcedMap.vue --> <template lang="pug"> div baidu-map(v-bind="$attrs" v-on="$listeners") </template>
封装表单组件的时候,inputListeners
之类的计算属性通常非常有用。
始终牢记,子组件想要改变父组件的数据,需要$emit
哈。
网络异常,图片无法展示
|
<template lang="pug"> //- 父组件 div enhanced-input(v-model="title" label='标题') div {{title}} </template> <script> import EnhancedInput from "@/components/ListItem"; export default { name: "List", components: { EnhancedInput }, data() { return { title: "初始值" }; } }; </script>
组件内部如下:
<template lang="pug"> label {{label}} //- 这里记得绑定value的值,这样才能显示初始值 input(v-bind="$attrs",:value="value",v-on="inputListeners") </template> <script> export default { name: "enhanced-input", inheritAttrs: false, props: ["label", "value"], mounted() {}, computed: { inputListeners() { const newListeners = { // 我们从父级添加所有的监听器 ...this.$listeners, // 然后我们添加自定义监听器,或覆写一些监听器的行为 // 这里确保组件配合 `v-model` 的工作 input: event => this.$emit("input", event.target.value) }; return newListeners; } } }; </script>