In Vue, different options have different merge strategies. For example, data,props,methods are overwritten merge with the same name, while others are merged directly, while lifecycle hook functions put functions with the same name into an array and call them as they are called []

In the Vue, provides an API, Vue. Config. OptionMergeStrategies, can through the API to custom options merge strategy.

Print in code

console.log(Vue.config.optionMergeStrategies)
Copy the code

Customize life cycle functions by merging policies

background

I realized that the page had a lot of timers, Ajax polling, and animations, and that opening one browser TAB wasn’t a problem. If I opened too many tabs, the browser would get stuck. Search up and down in Baidu and find an event visibilitychange, which can be used to judge whether the browser page is displayed.

If you have a solution, just write it

Export default {created() {window.adDeventListener ('visibilitychange', this.$_hanldeVisiblityChange) You can refer to this. Small make up the previous article $on (' hook: beforeDestroy '() = > {window. The removeEventListener (' visibilitychange', this.$_hanldeVisiblityChange ) }) }, methods: $_devisiblityChange () {if (document.visibilityState === 'hidden') {// Stop that stuff} if (document.visibilityState === 'visible') {// open up that bunch}}}}Copy the code

Through the above code, you can see in every on processing of documents are required to write a bunch of event listeners, to determine whether a page displays the code, one can also be in two places, more documents will have a headache, then small make up a whim, define a page shows hidden life cycle hooks, encapsulate all these judgments

Custom lifecycle hook functions

Define the life cycle functions pageHidden and pageVisible

Import Vue from 'Vue' // to notify all components that the page state has changed const notifyVisibilityChange = (lifeCycleName, vm) => { $options[lifeCycleName] const lifeCycles = vm.$options[lifeCycleName] So it's an array if (lifeCycles && lifecycles.length) {// Go through the list of lifeCycleName's lifecycle functions, Lifecycles.foreach (lifecycle => {lifecycle. Call (vm)})} // walk through all the child components, If (vm.$children && vm.$children. Length) {vm.$children. notifyVisibilityChange(lifeCycleName, Child)})}} / / add life cycle function export function init () {const optionMergeStrategies = Vue. Config. OptionMergeStrategies / / Defines two life cycle function pageVisible, pageHidden. / / : why do you want to assign a value to optionMergeStrategies created? / / this is equivalent to specify pageVisible, PageHidden merger strategy and created the same (other lifecycle functions) optionMergeStrategies. PageVisible = optionMergeStrategies. BeforeCreate OptionMergeStrategies. PageHidden = optionMergeStrategies. Created} / / on / / the event change is bound to the root node rootVm vue root node instance export function bind(rootVm) { window.addEventListener('visibilitychange', () => {let lifeCycleName = undefined if (document.visiBilityState === 'hidden') {lifeCycleName = 'pageHidden' } else if (document.visibilityState === 'visible') { lifeCycleName = 'pageVisible' } if (lifeCycleName) { NotifyVisibilityChange (lifeCycleName, rootVm)}})}Copy the code

application

  1. inmain.jsThe main entry file is imported
Import {init, bind} from './utils/custom-life-cycle' // Init () const vm = new Vue({router, render: $mount('# App '); // Bind (vm)Copy the code
  1. Listen for life cycle functions where needed
Export default {pageVisible() {console.log(' page is displayed ')}, pageHidden() {console.log(' page is hidden ')}}Copy the code

providewithinject, not only father-child transmission value, ancestral value can also be

In Vue interviews, we will often be asked how to transmit values between Vue and its parent. We usually answer: props, $emit event, vuex, eventBus, etc. Today, we add provide and inject values, which is a step closer to offer. (Oh, there’s another one in the next section.)

React has a Context. Components can use Context to transmit values to any descendant. Vue provides the same as inject value into Context

Let me give you an example

The following code will be familiar to those of you who have used Elemment-UI

<template> < EL-form :model="formData" size="small"> <el-form-item label=" name" prop="name"> < EL-input V-model =" formdata.name "/> </el-form-item> <el-form-item label=" age" prop="age"> <el-input-number v-model=" formdata.age" /> </el-form-item> <el-button> Submit </el-button> </el-form> </template> <script> export default {data() {return { formData: { name: '', age: 0 } } } } </script>Copy the code

Look at the above code, it seems nothing special, write every day ah. On the el-form we specified size=”small”. Then we noticed that the size of all the form elements and buttons changed to small. How did this happen? Let’s write a form to simulate it

Write your own form

Custom formscustom-form.vue

<template> <form class="custom-form"> <slot></slot> </form> </template> <script> export default { props: {// Control the size of form elements size: {type: String, default: Validator (value) {return ['default', 'large', 'small', 'mini'].includes(value)}}, // Controls the disabled state of form elements. Disabled: {type: Boolean, default: False}}, // Pass the current form instance to all descendant components by providing () {return {customForm: this}}} </script>Copy the code

In the code above, we pass an instance of the current component to the descendant component by providing, which is a function that returns an object

Customize form itemscustom-form-item.vue

Nothing special, just add a label, element-UI a little bit more complicated, okay

<template>
  <div class="custom-form-item">
    <label class="custom-form-item__label">{{ label }}</label>
    <div class="custom-form-item__content">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    label: {
      type: String,
      default: ''
    }
  }
}
</script>
Copy the code

Custom input boxcustom-input.vue

<template> <div class="custom-input" :class="[ `custom-input--${getSize}`, getDisabled && `custom-input--disabled` ]" > <input class="custom-input__input" :value="value" @input="$_handleChange" /> </div> </template> <script> /* eslint-disable vue/require-default-prop */ export default { props: {// use custom v-model value: {type: String, default: "}, size: {type: String}, disabled: {type: Boolean}}, // Add an instance injected by the Form component by inject Inject: ['customForm'], computed: {// Get the size of the component by counting the component, if the current component is passed in, use the current component's, Or whether the form components of getSize () {return this. The size | | this. CustomForm. Size}, GetDisabled () {const {disabled} = this if (disabled! == undefined) { return disabled } return this.customForm.disabled } }, methods: {// Custom v-model $_handleChange(e) {this.$emit('input', e.trade.value)}}} </script>Copy the code

  

In the form, we return an object by providing. In the input, we get the items in the returned object by inject, as shown in inject:[‘customForm’], You can then call the properties and methods on the form instance from within the component via this.customForm

For use in projects

<template> <custom-form size="small"> <custom-form-item label=" name" > <custom-input V-model =" formdata.name "/> </custom-form-item> </custom-form> </template> <script> import CustomForm from '.. /components/custom-form' import CustomFormItem from '.. /components/custom-form-item' import CustomInput from '.. /components/custom-input' export default { components: { CustomForm, CustomFormItem, CustomInput }, data() { return { formData: { name: '', age: 0 } } } } </script>Copy the code

Execute the above code and the result is:

<form class="custom-form"> <div class="custom-form-item"> <label class="custom-form-item__label"> Name </label> <div class="custom-form-item__content"> <! --> <div class="custom-input custom-input--small"> <input class="custom-input__input"> </div> </div> </div> </form>Copy the code

  

As you can see from the code above, the Input component has the component style set to Custom-Input –small

injectFormat specification

Inject can also be an object in addition to the inject:[‘customForm’] script used in the code above. You can specify a default value

To modify the previous example, if there is no custom form outside of custom-input, the customForm will not be injected. In this case, specify the default value for customForm

{inject: {customForm: {// For non-raw values, like props, need to provide a factory method Default: () => ({size: 'default'})}}}Copy the code

  

Use restrictions

  • 1. The binding of provide and Inject is not responsive. However, if you pass in a listener object, such as customForm: This, then the object’s properties are still responsive.

  • 2.Vue official website recommends provide and inject mainly for the development of high-level plug-ins or component libraries. Not recommended for normal application code. Since provide and Inject are not traceable in the code (CTRL + F can be searched), Vuex is recommended instead. However, it is not to say that can not be used, in the local function sometimes used the effect is larger.

slot

Slot, I believe that every Vue has been used, but how to better understand the slot, how to customize the slot, today xiaobian for you to bring more image of the explanation.

The default slot

<template> <! -- This is a one-bedroom --> <div class="one-bedroom"> <! <slot></slot> </div> </template>Copy the code

  

<template> <! <one-bedroom> <! -- Put the furniture in the room, and inside the components is the default slot space provided above -- put a crib first, you don't have a girlfriend anyway, put a computer desk, </span> </one-bedroom> </template> <script> import OneBedroom from '.. /components/one-bedroom' export default { components: { OneBedroom } } </script>Copy the code

A named slot

<template> <div class="two-bedroom"> <! This is the master bedroom --> <div class="master-bedroom"> <! -- Master bedroom uses default slot --> <slot></slot> </div> <! <div class="secondary-bedroom"> <! - the second lie to use named slot - - > < slot name = "secondard" > < / slot > < / div > < / div > < / template >Copy the code

  

<template> <two-bedroom> <! </span> <span> put a closet, the wife too many clothes </span> <span> forget it, or put a computer desk, also need to write bug</span> </div> <! V-slot :secondard specifies which named slot to use. #secondard--> <template V-slot :secondard> <div> <span> Soft bed is not good for waist </span> <span> put a wardrobe </span> </div> </template> </two-bedroom> </template> <script> import TwoBedroom from '.. /components/slot/two-bedroom' export default { components: { TwoBedroom } } </script>Copy the code

Scope slot

<template> <div class="two-bedroom"> <! <div class="toilet"> <! <slot name="toilet" V-bind ="{washer: true}"></slot> </div> </div> </template>Copy the code

  

<template> <two-bedroom> <! -- Other ellipses --> <! <template V-slot :toilet="scope"> <! <span v-if="scope. Washer "> </span> </template> </two-bedroom> </template>Copy the code

Slot Default value

<template> <div class="second-hand-house"> <div class="master-bedroom"> <! </span> <span> </span> </slot> </div> <! <div class="secondary-bedroom"> <! </span> </slot> </div> </div> </template>Copy the code

  

<second-hand-house> <! <span> <span> Put a closet, my wife's clothes are too much </span> <span> Forget it, or put a computer desk. </span> </div> </second-hand-house>Copy the code

dispatchandbroadcast

Dispatch and broadcast are component communication methods with a history. Why are they historical? They were provided by Vue1.0 and abandoned in Vue2.0. But just because it’s deprecated doesn’t mean we can’t implement it manually, as many UI libraries have implementations in-house. This article is based on the Element-UI implementation. $parent,$children,$options

Methods to introduce

$DISPATCH: $Dispatch will fire an event up, passing the name and parameters of the ancestor component to fire, triggering the event listener on the component when the event is passed up to the corresponding component, and the propagation will stop.

$broadcast: $broadcast will spread to all the offspring of the component, an event at the same time, pass to trigger the offspring of the component with the name of the parameter, when the event is passed to the corresponding component, the offspring of the trigger event listener on the component, transmission will stop at the same time, because is passed down the tree, so can only stop one leaf branch transfer).

$dispatchImplementation and Application

1. Code implementation

// @param {*} componentName Name of the component that receives events // @param {*}... Function Dispatch (eventName, componentName,... Params) {// If there is no parent, Will take $root let the parent = this. $parent | | this. $root while (the parent) {/ / component name is stored in the components above $options.com ponentName const name = Parent.$options.name // If (name === componentName) {// If (name === componentName) $emit. Apply (parent, [eventName,...params]) break} else {parent = parent.$parent}}} Export default {methods: {$dispatch: dispatch}}Copy the code

2. Code application

  • Events are raised in child components via $Dispatch

    import emitter from '.. /mixins/emitter' export default {name: 'Chart', // [Emitter], Mounted () {this.$dispatch('register', 'Board', this)}}Copy the code
  • Listen on the Board component for events to be registered via $on

$broadcastImplementation and Application

1. Code implementation

// @param {*} componentName // @param {*} componentName // @param {... Function Broadcast (eventName, componentName,... params) { this.$children.forEach(child => { const name = child.$options.name if (name === componentName) { child.$emit.apply(child, [eventName, ...params]) } else { broadcast.apply(child, [eventName, componentName, Params])}})} // Export default {methods: {$broadcast: broadcast}}Copy the code

2. Code application

Events are emitted down in the parent component via $broadcast

import emitter from '.. /mixins/emitter' export default {name: 'Board', // [Emitter], methods:{// Refresh children (params) {this.$broadcast('refresh', 'Chart', params)}}Copy the code

Listen for refresh events through $on in the descendant component

Export default {name: 'Chart', created() {this.$on('refresh',(params) => {// refresh event})}}Copy the code

conclusion

$dispatch and $broadcast should be familiar to you, but why did Vue2.0 abandon both methods? The official explanation: “Because the event-flow approach based on the component tree structure is difficult to understand and becomes more and more fragile as the component structure expands. This is not a good way of doing things, and we don’t want to cause developers too much pain in the future. Also, $dispatch and $broadcast do not solve the communication problem between sibling components. “

Indeed, as stated on the official website, this kind of event flow is not easy to understand, and the later maintenance cost is relatively high. However, in the opinion of xiaobian, no matter black or white cat, the cat that can catch mice is a good cat. In many specific business scenarios, due to the complexity of business, it is likely to use such communication mode. But use belongs to use, but can not abuse, xiaobian has been used in the project.

Follow the official account for more selected articles ~