Table of Contents
Transparent transmission Attributes
Attributes inheritance?
Merging class and style
v-on listener inheritance
Deep component inheritance
Disable Attributes inheritance
Attributes inheritance for multi-root nodes
vue2 $attrs and $listeners
$attrs Concept Description
$attrs case
$listeners Concept Description
$listeners case
vue3 $attrs and $listeners
Usage of attrs in template
When there is only 1 root element
When there are 2 root elements
$listeners deprecated
Usage of attrs in js
Options API
Syntax of Composition API 3.0
Syntax of Composition API 3.2
Summarize
Remove $listeners
Overview
2.x syntax
3.x syntax
$attrs contains class & amp; style
Overview
2.x Behavior
3.x Behavior
(1) $props: The props object received by the current component. The Vue instance delegates access to its props object properties.
(2) $attrs: Contains attribute bindings (except class and style) that are not recognized (and obtained) as props in the parent scope.
(3) $listeners: Contains v-on event listeners in the parent scope (without .native modifier). He can pass v-on=”listeners” into internal components
Fallthrough Attributes
Transparent transmission of Attributes refers to attributes and event handlers that are passed in from the parent component and are not declared as props by the child component or as component custom events.
Attributes inheritance?
“Transparent attributes” refer to attributes that are passed to a component but are not declared as props or emits or v-on
event listeners by the component. The most common examples are class
, style
and id
.
When a component is rendered with a single element as the root, the transparent attributes are automatically added to the root element. For example, if we have a
component, its template looks like this:
<!-- Template for <MyButton> --> <button>click me</button>
A parent component uses this component and passes in class
:
<MyButton class="large" />
The final rendered DOM result is:
<button class="large">click me</button>
Here,
does not declare class
as a prop it accepts, so class
is regarded as a pass-through attribute and is automatically passed through. Passed to the root element of
.
Merging of class
and style
If a child component’s root element already has a class
or style
attribute, it will be merged with the value inherited from the parent component. If we change the template of the previous
component to this:
<!-- Template for <MyButton> --> <button class="btn">click me</button>
Then the final rendered DOM result will become:
<button class="btn large">click me</button>
v-on
Listener inheritance
The same rules apply to v-on
event listeners
<MyButton @click="onClick" />
The click
listener will be added to the root element of
, which is the native element. When the native
is clicked, the
onClick
method of the parent component will be triggered. Similarly, if the native button
element itself has an event listener bound to it through v-on
, both this listener and the listener inherited from the parent component will be triggered. .
Deep component inheritance
There are cases where a component renders another component on the root node. For example, let’s refactor
so that it renders
on the root node:
<!-- Template for <MyButton/>, just render another component --> <BaseButton />
At this time, the transparent attribute received by
will be directly passed to
.
Please note:
Transparent attributes will not include props declared on
or
v-on
listening functions for events declared inemis
. In other words, the declared props and listener functions are “consumed” by.
If the transparently transmitted attributes comply with the declaration, they can also be passed into
as props.
Disable Attributes inheritance
If you don’t want a component to automatically inherit attributes, you can set inheritAttrs: false
in the component options.
The most common scenario where attribute inheritance needs to be disabled is when the attribute needs to be applied to other elements than the root node. By setting the inheritAttrs
option to false
, you have full control over how passed-in attributes are used.
vue2
Vue 2’s virtual DOM implementation has some special handling of the
class
andstyle
attributes. Therefore, unlike all other attributes, they are not included in$attrs
.The above behavior has side effects when using
inheritAttrs: false
:
- Attributes in
$attrs
will no longer be automatically added to the root element, but the developer will decide where to add them.- But
class
andstyle
do not belong to$attrs
, they will still be applied to the root element of the component:
<template> <label> <input type="text" v-bind="$attrs" /> </label> </template> <script> export default { inheritAttrs: false } </script>
vue3
Starting from 3.3 you can also use defineOptions directly in :
<script setup> defineOptions({ inheritAttrs: false }) // ...setup logic </script>
These transparently passed attributes can be directly accessed using $attrs
in template expressions.
<span>Fallthrough attribute: {<!-- -->{ $attrs }}</span>
This $attrs
object contains all attributes other than the props
and emis
declared by the component, such as class
, style
, v-on
listener, etc.
There are a few points to note:
Unlike props, pass-through attributes retain their original case in JavaScript, so an attribute like
foo-bar
needs to pass$attrs['foo-bar']
to visit.A
v-on
event listener like@click
will be exposed as a function$attrs.onClick
under this object.
Attributes inheritance of multi-root nodes
Different from single-root node components, components with multiple root nodes do not have automatic attribute transparent transmission behavior. If $attrs
is not explicitly bound, a runtime warning will be thrown.
<CustomLayout id="custom-layout" @click="changeValue" />
If
has a multi-root node template like the following, Vue will throw a warning because it does not know where to transparently pass the attribute.
<header>...</header> <main>...</main> <footer>...</footer>
If $attrs
is explicitly bound, there will be no warning:
<header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer>
vue2 $attrs and $listeners
$attrs concept description
- Contains attribute bindings (except class and style) in the parent scope that are not recognized (and obtained) as props
- $attrs contains the attribute name and attribute value passed to a component; that is, a JSON object
- Internal components can be passed in via
v-bind="$attrs"
$attrs case
parent component
<template> <SlotContainer ref="slotContainer" name="huangbiao" :isOk="false" :option="{ a: 1, b: true, c: 'ddd' }" > </SlotContainer> </template> <script> import SlotContainer from "./SlotContainer" export default { data() { return {}; }, components: { SlotContainer, } }; </script> <style lang="scss" scoped></style>
Subassembly
<script> export default { data() { return {}; }, props: { option: { type: Object, default: function() { return {}; } } }, mounted() { console.log(this.$attrs); }, methods: {} }; </script>
result
Do not comment out the props of the subcomponent, the value of $attrs
Comment out the props of the subcomponent and the value of $attrs
inheritAttrs: false and $attrs; what problems can be solved by using them together?
- You can manually decide which element these attributes will be assigned to
- The inheritAttrs: false option will not affect the binding of style and class
- This pattern allows you to work with base components more like raw HTML elements without worrying about which element is the real root element
$listeners concept description
- Contains v-on event listeners in the
parent scope (without .native decorators)
. - You can use
v-on="$listeners" to point all event listeners to a specific sub-element of this component
- It is an object that contains all the listeners acting on this component.
$listeners case
<template> <div class> <SlotContainer ref="slotContainer" v-on:m1="m1" v-on:m2="m2" @m3="m3" @m4="m4" @click.native="testJiami" > </SlotContainer> </div> </template> <script> import SlotContainer from "./SlotContainer"; import CryptoJS from "crypto-js"; export default { data() { return {}; }, components: { SlotContainer, }, methods: { testJiami() { this.m1(); this.m2(); this.m3(); this.m4(); }, m1() { console.log("Encryption result one MD5:" + CryptoJS.MD5("Hello")); //The corresponding API for adding salt console.log("Encryption result one MD5:" + CryptoJS.HmacMD5("Hello", "salt")); console.log(CryptoJS.SHA256("123456").toString()); //The corresponding API for adding salt console.log(CryptoJS.HmacSHA256("123456", "salt").toString()); }, m2() { var pwd = "passwor"; console.log("Encryption result two Hmac-MD5: " + CryptoJS.HmacMD5("Hello", pwd)); }, m3() { var salt = CryptoJS.enc.Utf8.parse("salt"); //salt var iter = 1000; //Number of iterations var mi = CryptoJS.PBKDF2("Hello", salt, { keySize: parseInt(4), iterations: parseInt(iter), }); console.log("Encryption result three: " + mi); }, m4() { var pswd = "My password"; var mi = CryptoJS.AES.encrypt("Hello", pswd); console.log("Encryption result four" + mi); //Decrypt var result = CryptoJS.AES.decrypt(mi, pswd).toString(CryptoJS.enc.Utf8); console.log("Decryption result: " + result); }, }, }; </script> <style lang="scss" scoped></style>
Subcomponent(SlotContainer.vue)
<script> export default { data() { return {}; }, mounted() { console.log(this.$listeners); }, methods: {} }; </script> <style lang="scss" scoped></style>
result
The method of @click.native="testJiami" is not in $listeners
vue3 $attrs and $listeners
To put it simply, attrs mainly receives attributes that are not defined in props but passed from the parent component.
<!-- Parent component ParentCom.vue --> <template> <ChildCom msg="Thunder Monkey" data="123" /> </template> <script setup> import ChildCom from './ChildCom.vue' </script> <!-- Child component ChildCom.vue --> <template> <div> {<!-- -->{ msg }} - {<!-- -->{ $attrs }} </div> </template> <script setup> defineProps({ msg: { type: String } }) </script>
As you can see, in the child component, msg uses props to receive, so {{ msg }} can directly output the content received in props.
All content not received in props is placed in $attrs and stored in an object.
Next, we will explain how to use attrs in different situations.
How to use attrs in template
In the previous simple example, we already roughly know the usage of attrs in template. But in Vue3, template no longer requires only one root element. So attrs is used in template in two situations.
When there is only one root element
When there is only one root element, any properties in the subcomponent that are not received by props will be bound to the root element.
<!-- Parent component ParentCom.vue --> <template> <ChildCom msg="Thunder Monkey" data="123" name="Shark Chili" style="color: red;" /> </template> <script setup> import ChildCom from './ChildCom.vue' </script> <!-- Child component ChildCom.vue --> <template> <div> {<!-- -->{ msg }} </div> </template> <script setup> defineProps({ msg: { type: String } }) </script>
As you can see, the properties not received by props are bound to the root element.
Even the style passed in style is executed, and the text turns red.
When there are 2 root elements
When a child component has 2 root elements, properties not received by props will not be bound to the elements of the child component.
<!-- Parent component ParentCom.vue --> <template> <ChildCom msg="Thunder Monkey" data="123" name="Shark Chili" style="color: red;" /> </template> <script setup> import ChildCom from './ChildCom.vue' </script> <!-- Child component ChildCom.vue --> <template> <div> {<!-- -->{ msg }} </div> <div> {<!-- -->{ msg }} </div> </template> <script setup> defineProps({ msg: { type: String } }) </script>
At this time, even the style passed in by the parent component will not take effect.
If we want the second element to bind all attributes not received by props at this time, we can use the v-bind="$attrs" method to achieve this
<!-- Parent component ParentCom.vue --> <template> <ChildCom msg="Thunder Monkey" data="123" name="Shark Chili" style="color: red;" /> </template> <script setup> import ChildCom from './ChildCom.vue' </script> <!-- Child component ChildCom.vue --> <template> <div> {<!-- -->{ msg }} </div> <div v-bind="$attrs"> {<!-- -->{ msg }} </div> </template> <script setup> defineProps({ msg: { type: String } }) </script>
$listeners Deprecated
The $listeners
object has been removed in Vue 3. Event listeners are now part of $attrs
:
{ text: 'This is an attribute', onClose: () => console.log('close event is triggered') }
How to use attrs in js
In addition to being accessible in template, $attrs can also be accessed in JS.
Vue 3 is actually compatible with most Vue 2 syntax, that is, Options API. The usage of attrs in Options API and Composition API is slightly different. The Composition API is divided into syntax before Vue 3.2 and syntax after 3.2.
These 3 situations will be discussed separately next.
Options API
<!-- Parent component ParentCom.vue --> <template> <ChildCom msg="Thunder Monkey" data="123" name="Shark Chili" style="color: red;" /> </template> <script setup> import ChildCom from './ChildCom.vue' </script> <!-- Child component ChildCom.vue does not focus on the template part yet --> <script> export default { props: { msg: { type: String } }, mounted() { console.log(this.$attrs) } } </script>
At this time, the console will output properties that are not received by props.
Composition API 3.0 syntax
<!-- Parent component ParentCom.vue --> <template> <ChildCom msg="Thunder Monkey" data="123" name="Shark Chili" style="color: red;" /> </template> <script setup> import ChildCom from './ChildCom.vue' </script> <!-- Child component ChildCom.vue does not focus on the template part yet --> <script> export default { props: { msg: { type: String } }, setup(props, context) { console.log('props: ', props) console.log('attrs: ', context.attrs) } } </script>
The writing method before Vue 3.2 needs to be in the setup method It receives 2 parameters, and attrs is in the context parameter.
Composition API 3.2 syntax
With the syntax after Vue 3.2, you can add the setup attribute on the