Detailed explanation and in-depth application of vue component development - Part 2 (recommended intensive reading)
The Vue 2.0 series is based on component development. This part is divided into two parts. The first part is mainly about concepts, global components and local components, the application of components in modularization, and parent-child component communication. The next part is non-parent-child component communication, the use of component slots, dynamic components and handling of component boundary conditions. Please read patiently, I believe it will give you a deeper understanding and better application of component-based development.
Detailed explanation and in-depth application of vue component development - Part 1: Part 1 address
1. Non-parent-child component communication
The specific demo codes have been uploaded to GitHub, and those who are interested are welcome to download and experience
Sibling component pass value
- State management through
Vuex
in large-scale projects (I will write a detailed application blog of Vuex later when I have time) - First son to father, then father to son, that is, child component 1 sends a custom event to the parent component through this.$emit, and at the same time passes the data to be passed to child component 2 as a parameter to the parent Component, the parent component assigns the data to data after receiving the event of sub-component 1 and passes it to sub-component 2, and sub-component 2 receives the data passed by the parent component through props, which is the data of sub-component 1
- Using the central event bus, that is, sub-component 1 sends an event through the Bus central event bus with
$emit
and carries the data to be passed, in the life cycle of sub-component 2 inmounted()
or before Use$on
to listen to the event sent in component 1 and trigger a callback function defined by yourself. When the component that triggers the event is destroyed, it is recommended to destroy the event bus through $off in the destroyed life cycle. Note that this method has been abolished in Vue3
After downloading the code, annotate chlid1 and child2 in App.vue under the cpncommunication folder to see the use of child-to-parent and then to another child; comment to child3 and child4 to experience the value-passing method of event bus brother components
Ancestor components access deeply nested subcomponents (multiple levels of components)
- Vuex
- use central event bus
- provide / inject
This pair of options needs to be used together to allow an ancestor component to inject a dependency to all its descendants, no matter how deep the component hierarchy is and always take effect as long as its upstream and downstream relationships are established. The parent component exports the data to be passed throughprovide
(the provide option should be an object or a function that returns an object), and any descendant component can get the data throughinject:['property name']
Tip: provide and inject bindings are not responsive. This is deliberate. However, if you pass in a listenable object, the object's property is still responsive
- $attrs/ $listeners can be used to pass values between ancestor components and subcomponents
$attrs
inherits all the properties of the parent component except the properties passed by prop, class and style, and the child component can be obtained by using this.$attrs
. v-bind='$attrs'
is bound to the grandson component, which is applicable to the problem of passing values from the ancestor component to the child component in the deep nesting scenario of the component
The $listeners attribute is an object that contains all the listeners that act on this component. You can use v-on="$listeners" to pass all the event listeners into the internal components of this component - creating a higher level s component
You can decide which method to use in son.vue under the deepcpn folder, the code has been commented in detail
2. Use of slots in components
Using the slot slot
can make the encapsulated component more extensible, and the user can customize the content to be displayed inside the component through the slot
When packaging components, commonality can be extracted into components, and different parts can be set as slots.
Basic use of slots:
<div id="app">
<demo></demo>
<demo>
<div>I will replace the default content</div>
</demo>
</div>
<script>
Vue.component("demo", {
template: `
<div>
<slot><h3>I am a slot, the default content when no value is passed</h3></slot>
</div>
`,
});
const app = new Vue({
el: "#app",
});
</script>
The effect is as follows:
Named slots: (used syntax updated since 2.6.0)
Use named slots to replace specific parts of the content
Before Vue2.6.0
use a named slot: add a name attribute to the slot tag of the component template: <slot name='slot name'></slot>
, when using it, add it to the content tag to be replaced by the component slot='slot name'
Vue2.6.0 and later versions
use named slots: the child component remains the same, the use form remains the same, and the parent component uses the v-slot: slot name
directive on the <template> element
, You can also use (v -slot:) is replaced by the characters #shorthand
<div id="app">
<!-- Versions before Vue 2.6.0 -->
<!-- <cpn></cpn>
<cpn><span v-slot="center">replaces the middle</span></cpn>
<cpn>
<template slot="left">
<button>Click to return</button>
</template>
</cpn> -->
<!-- Use syntax after Vue 2.6.0 -->
<cpn></cpn>
<cpn>
<template #center>
<span>replaced the middle</span>
</template>
</cpn>
<cpn>
<template v-slot:left>
<button @click="btn">Click to return</button>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot name="left"><span>left</span></slot>
<slot name="center"><span>middle</span></slot>
<slot name="right"><span>right</span></slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
components: {
cpn: {
template: '#cpn',
},
},
methods: {
btn(){
console.log('hello')
}
},
});
</script>
Slot scope (used syntax updated since 2.6.0)
Everything in the parent component template will be compiled in the parent scope; everything in the child component template will be compiled in the child scope
Sometimes it is useful to have the slot content have access to data that is only available in the child component
Updated since 2.6.0. Deprecated use of slot-scope
That is, the data in the parent template comes from the data of the parent component, and the data of the child component template comes from the data of the child component itself
Slot scope (used syntax updated since 2.6.0)
Everything in the parent component template will be compiled in the parent scope; everything in the child component template will be compiled in the child scope
Sometimes it is useful to have the slot content have access to data that is only available in the child component
Updated since 2.6.0. Deprecated use of slot-scope
That is, the data in the parent template comes from the data of the parent component, and the data of the child component template comes from the data of the child component itself
<div id="app">
<!-- Use isShow: true of the parent component (here is the data value of new Vue, so the child component will display -->
<cpn v-show="isShow"></cpn>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: true,
},
components: {
cpn: {
template:`
<div>
<h4>I am a child component</h4>
<p v-show="isShow">I use isShow: false of the subcomponent, it will not be displayed</p>
</div>
`,
data() {
return {
isShow: false,
}
}
}
}
})
</script>
Result: The page shows that I am a child component, that is, the subcomponent can be displayed successfully, but the p tag cannot be displayed
The parent component wants to access the data in the child component:
-
First bind the data of the child component as an attribute of the
<slot>
element, and the attribute bound to the<slot>
element is called the slot prop -
Use
v-slot
with a value to define the name of the slot prop in the parent scope<div id="app"> <cpn></cpn> <!--The method before Vue 2.6.0, use slot-scope--> <!-- <cpn> <template slot-scope="sonSlot"> <div>{{sonSlot.mes}}</div> <span>{{sonSlot.data.join(' - ')}}</span> </template> </cpn> --> <!-- From Vue2.6.0 slot-scope changed to use v-slot --> <cpn> <template v-slot:default="slotProps"> <div>{{slotProps.mes}}</div> <span>{{slotProps.data.join(' - ')}}</span> </template> </cpn> </div> <!-- subcomponent template --> <template id="cpn"> <div> <!-- pLanguages, mes are the values of subcomponent data --> <slot :data="pLanguages" :mes="mes"> <ul> <li v-for="item in pLanguages">{{item}}</li> </ul> </slot> </div> </template>
3. Dynamic components
Dynamic components are often used in tab pages to achieve dynamic switching between different components, by dynamically binding multiple components to the is attribute
of the component
tag.
<body>
<div id="app">
<!-- Assign the currently clicked component name to currentTab through the click event -->
<button
v-for="item in tabs"
:key="item.cpn"
@click="currentTab = item.cpn"
>
{{ item. value }}
</button>
<!-- is is bound to currentName, that is, the component that displays currentName -->
<component :is="currentName" class="tab"></component>
</div>
<script>
const one = {
template: '<div>label page 1<input type="text"></div>',
};
const two = {
template: '<div>label page 2<input type="text"></div>',
};
const three = {
template: '<div>label page 3<input type="text"></div>',
};
new Vue({
el: '#app',
data: {
//Display label 1 when the page just loads
currentTab: 'one',
tabs: [ { cpn: 'one', value: 'label 1' }, {cpn: 'two', value: 'label 2'}, { cpn: 'three', value: 'label 3'}],
},
components: {
one,
two,
three,
},
computed: {
currentName() {
// Return the current component name currentTab
return this.currentTab;
},
},
});
</script>
</body>
At this time, when the component is switched, the original input data of the component will disappear. If you want to keep the state of these components to avoid performance problems caused by repeated re-rendering, you can use <keep-alive>
, <keep-alive>
package dynamic Components, cache inactive component instances instead of destroying them
keep-alive
has two unique life cycles activated
and deactivated
. Components wrapped with keep-alive
will not be destroyed when switching, but cached in memory and execute the deactivated
hook function, and the activated
hook function will be executed after hitting the cache rendering (that is, the second or more entry The component executes only one life cycle activated
)
<keep-alive>
<component :is="currentName" class="tab"></component>
</keep-alive>
If there are multiple conditionally judged sub-components, <keep-alive> requires only one sub-element to be rendered
<!-- Multiple subcomponents in keep-alive If no conditions are added, the first subcomponent will be displayed by default -->
<keep-alive>
<one v-if="currentTab == 'one'"></one>
<two v-else-if="currentTab == 'two'"></two>
<three v-else></three>
</keep-alive>
4. Component edge case handling
- Access the root instance new Vue:
this.$root
This is handy for demos or very small applications with a small number of components. However, it is not the case when this model is extended to medium and large applications. Therefore, in most cases, we strongly recommend using Vuex to manage the state of the application
-
Programmatic event listeners
Listen to an event via$on(eventName, eventHandler)
Listen to an event at a time through$once(eventName, eventHandler)
Stop listening to an event via$off(eventName, eventHandler)
You wouldn't normally use these, but they come in handy when you need to manually listen to events on a component instance. For example, before destroying a certain component, the third-party library referenced by this component is also destroyed. Through the programmatic listener, we can programmatically clean up all the things created<div id="app"> <input ref="dateInput" /> </div> <script> new Vue({ el: "#app", mounted: function() { var picker = new Pikaday({ field: this.$refs.dateInput, format: "YYYY-MM-DD" }); //The date selector picker of the third-party library is also destroyed before the component is destroyed this.$once("hook:beforeDestroy", function() { picker.destroy(); }); } });
-
Recursive components:
Components can call themselves through the name option in their own templates. When you useVue.component
to register a component globally, the global ID will be automatically set as the name option of the component. If you use a local component, you must automatically Define the value of name, remember to set the termination condition when triggering the recursive component, otherwise it will cause stack overflow<template id="globalCpn"> <div> <global-cpn></global-cpn> <p>Global components directly use the component name</p> </div> </template> <script> var localCpn = { template: ` <div> <loacl></loacl> <p>Partial components need to set name</p> </div> `, name: "loacl", }; // 1. Register a global component Vue.component("global-cpn", { template: "#globalCpn", }); const app = new Vue({ el: "#app", //Register local components under app components: { "local-cpn": localCpn, }, }); </script>
The complete code involved in the blog
All the code involved in this article has been uploaded to Github: A-sketch123 VueComponent-eng