preface

Why should there be componentization and modularization

In the process of business development of the company, the volume of the website is getting bigger and bigger, which is stacked with a large number of business logic codes. The codes of different business modules call each other and nest each other. The coupling between the codes is getting higher and higher, and the call logic will become more and more chaotic.

When a function needs to be upgraded, it often affects the whole body at the same time. Whether it is in DOM or JS logic level, we need a lot of energy to take into account the modification and adjustment of the old code, resulting in increased workload.

Overall, the sheer number of code stacks made readability, maintainability, and collaboration between different engineers very poor, and as the business grew more complex, we needed a way to keep it simple.

Change numerous for brief

The triviality of a page is reduced to the independent function of each block, and each separate block is combined, nested, and works together, which is the idea of componentization.

We can think of a page as a car, where components are individual parts that perform their own functions and are combined to form a car.

We can greatly reduce the coupling of various functions of the system, and improve the integration of internal functions. This greatly increases our readability and maintainability, reduces the coupling, reduces the complexity of our development, and improves the development efficiency. We’ll have more time to do what we love!

Designing components follows a principle: a component focuses on doing one thing and doing it well. (In steelmaking parlance: One is all, all is one)

Let’s learn the componentized development of Vue

What is a component

Components, like the js functions we normally write, have

  1. encapsulation
  2. reusability
  3. Single responsibility

It can extend HTML elements to encapsulate reusable HTML code, and we can think of components as custom HTML elements.

The component registration

Global registration

  • throughcomponentDirective registers a component directly
  • The first parameter is the component name, and the second parameter is the component namenew VueOptions, including data, etc
  • componentAn internal field is missingtemplateUsed to writehtmlstructure
  • We can introduce the component as a label
    • It is worth noting that we habitually name components with the big camel name
    • And for the tag writing, we need to use the kebab naming convention (HTML is case insensitive)
<div id="app">
    <component-a></component-a>
</div>
Copy the code
Vue.component("ComponentA", {
    template: `<div>ComponentA</div>`
});
const app = new Vue({
    el: '#app'
});
Copy the code
  • We can also pass fieldstemplateThe introduction of
    • At this point we can just use the big hump, there is no naming restriction
    • usetemplateIt will also cause the objects within the target to be destroyedtemplateReplace the contents of
<div id="app">I'm going to get killed</div>
Copy the code
Vue.component("ComponentA", {
    template: `<div>ComponentA</div>`
})

const app = new Vue({
    el: '#app'.template: `
      
{{msg}}
`
.data: { msg: 'hello world'}});Copy the code

Local registration

  • We can declare an object through a fieldcomponentsImporting component usage
<div id="app"></div>
Copy the code
const ComponentB = {
    template: `
      
ComponentB {{msg}}
`
.data() { return { msg: 0}},methods: { handleClick() { this.msg++ } }, } Vue.component("ComponentA", { components: { ComponentB }, template: `
ComponentA
`
}) const app = new Vue({ el: '#app'.template: `
{{msg}}
`
.data: { msg: 'hello world'}});Copy the code

Pay attention to the point

  • Components must have root nodes (not required for Vue3)
  • Data must be a function (because components are reusable, use functions to decouple them)

Component life cycle

What is the life cycle

The life cycle of a component is a set of hooks, also called hooks, which are common functions, from pre-mount to post-unmount

Why lifecycle

We often need to request data before mounting the component, or after adding the component, we need to enable functions under the global (window), such as timer, and we need to know when the component is unloaded. At this time, we need to use the life cycle of the component. We can think of it as the built-in component listening for events

What is the life cycle

  • It consists of four sets: Create, mount, Updata, destroy, and before and after
  • Through the following small case, we can clearly see how the execution order of the life cycle is, how does it work
  • Components in themountedAdd it to view => we can see it
Vue.component("ComponentA", {
    template: `
      
ComponentA {{count}}
`
.data() { return { count: 0}},methods: { handleAdd() { this.count++ } }, beforeCreate() { console.log('Before creation -a'); }, created() { console.log('after creation -a'); }, beforeMount() { console.log('Pre-mount -A'); }, mounted() { console.log('After mount -a'); }, beforeUpdate() { console.log('Pre-update -a'); }, updated() { console.log('After update -a'); }, beforeDestroy() { console.log('Pre-unloading -A'); }, destroyed() { console.log('After uninstallation - A'); }})const app = new Vue({ el: '#app'.template: `
.data: { handleShow: true }, beforeCreate() { console.log('Before creation'); }, created() { console.log('After creation'); }, beforeMount() { console.log('Pre-mount'); }, mounted() { console.log('After mount'); }});Copy the code

Communication between components

How do we pass parameters inside a component? How do you return parameters? In other words, how do components work together? Let’s talk about communication between components

props

  • When we need to pass parameters to a component, we use props to pass parameters
  • Through the fieldpropsReceives parameters that are passed when referencing componentsattributeIncoming parameters
  • propsCan be defined as an array or object
  • analogytypeScriptpropsYou can also restrict the type by the key field of the object, and you can also customize the rules
  • Limiting types and rules prevents random parameter transmission and reduces interconnection costs
Vue.component("ComponentA", {
    1 / / way
    props: ['title'].2 / / way
    props: {
        title: {
            // Define the type
            type: String.// Define default values
            default: 'title-default'.// Define whether a value must be passed
            required: true
        },
        student: {
            // Define the type
            type: Object.// Custom rules
            validator(val) {
                console.log(val);
                // return true passes, return false does not pass
                return val.name === 'Ming'}}},template: `
      
ComponentA
{{title}}
`
}) const app = new Vue({ el: '#app'.template: ` < div > < component a title = "123" : student = "{name:" Ming "} "> < / component a > < / div > ` }); Copy the code

$emit

  • emitThe output of the js function is used by the component to emit values
  • callthis.$emitThe first parameter is the name of the outer received event, received as an event
Vue.component("ComponentA", {
    template: `
      
`
.methods: { handleClick() { this.$emit('change'.'Clicked')}}});const app = new Vue({ el: '#app'.template: `
`
.methods: { handleChange(val) { console.log(val); }}});Copy the code

v-model

  • usev-modelTo pass a value in a component, you need to create a value in the componentprops
  • On a componentv-modelBy default, names are usedvalueProp and namedinputIn the event
  • To avoid input controls such as checkboxes, checkboxes, etcvalue attributeFor different purposes, availablemodelChanging the Default name
Vue.component("ComponentA", {
    // model is used to change the default keyword
    model: {
        prop: 'milk'.event: 'change'
    },
    // The default is value
    // props: ['value'],
    props: ['milk'].template: `<div> <! -- {{value}} --> {{milk}} <button @click="handleClick">ComponentA</button> </div>`.data() {
        return {
            countA: 0}},methods: {
        handleClick() {
            // Default is input
            // this.$emit('input', ++this.countA)
            this.$emit('change'The + +this.countA)
        }
    }
});

const app = new Vue({
    el: '#app'.template: `<div> {{count}} <! This count will be passed to the milk of props, and when the $emit change in the component fires and returns with a new value, Count will be updated --> <ComponentA V-model ="count"></ComponentA> </div> '.data: {
        count: 0}});Copy the code

.sync

  • The abovev-modelYou can bind the incoming count to the component, passing$emitChange count, but the component needs an intermediate value countA to transfer, so if we need to bind a lot of values, can we change it directly in the componentpropsChange the external count?
  • Directly modifyingpropsYou can’t becauseVueUnidirectional data flowIn other words, data can only be transmitted in one direction, so as to keep data in order and maintain data uniformity
  • What should we do?
  1. We can just use it$emitMethod that sends the new value out and changes it externally
    • This is cumbersome to write and requires a full circle: first pass value => change value => transfer value => external change value
    • Will make our code become tedious, business will appear this logic, a large number of stacked, maintenance and reading will become poor, and components are not flexible, coupling is very strong
Vue.component("ComponentA", {
    props: ['count'].template: `
      
{{count}}
`
.methods: { handleClick() { this.$emit("upDateCount".this.count + 1)}}});const app = new Vue({ el: '#app'.template: `
{{count}}
`
.data: { count: 0 }, methods: { upDateCount(val) { console.log(val); this.count = val; }}});Copy the code
  1. When a value is passed externally, define a method that modifies the value (same as React logic).
    • This looks good, the coupling is reduced, one value corresponds to read and write
    • But I still feel inconvenient. I still don’t like it
Vue.component("ComponentA", {
    props: ['count'.'upDateCount'].template: `
      
{{count}}
`
.methods: { handleClick() { this.upDateCount(this.count + 1); }}});const app = new Vue({ el: '#app'.template: `
{{count}}
`
.data: { count: 0 }, methods: { upDateCount(val) { this.count = val; }}});Copy the code
  1. sync
    • This is aVueBuilt-in grammar sugar
    • It drastically reduces our code, makes us feel good (cross it out), reduces code, and makes it easier to read
Vue.component("ComponentA", {
    props: ['count'].template: `
      
{{count}}
`
.methods: { handleClick() { // Issue: update:propsName (received attribute) this.$emit('update:childCount'.this.count + 1)}}});const app = new Vue({ el: '#app'.template: `<div> {{parentCount}} <! - reception: PropsName (received attribute). Sync :data --> <ComponentA :count="parentCount" :childCount. Sync ="parentCount"></ComponentA> </div>`.data: { parentCount: 0}});Copy the code

Slot slot

  • Component transfer, in addition topropsThere areslotslot
  • slotThe default value can be written inside
  • A slot is like a punch, punched inside a component and filled with what’s passed in from the outside
  • Slots can be multiple, singlenameThe default isdefaultWhen you want to define more than one you have to give it a name
  • Slots have their own scope. If you want to call parameters in a component when calling it, you can passslotattributeEmitted as an object
Vue.component("ComponentA", {
    template: `<div> <! -- name header slot --> <p><slot name="header">headerDefault</slot></p> <! <slot>default</slot> <slot name="default">default</slot> <! -- slot with name footer --> <! -- Slot scope, Pass out childMsg and 123 via attribute --> <p><slot name="footer" : MSG ="childMsg" num="123">footerDefault</slot></p> </div> '.data() {
        return {
            childMsg: 'childMsg'}}})const app = new Vue({
    el: '#app'.template: `<div> <ComponentA> <! --> <template v-slot:header>{{parentMsg}}</template> <! --> slot1 <template v-slot:default>slot2</template> <! -- slot with name footer --> <! <template V-slot :footer="data">{{data.msg}} -- {{data.num}}</template> </ComponentA> </div> '.data: {
        parentMsg: 'parentMsg'}});Copy the code
  • Additional examples: use of scope slots, built-in for loops, external calls
Vue.component("ComponentA", {
    template: `
      
`
.data() { return { list: [{name: 'orange'.num: 12 }, { name: 'apple'.num: 0 }, { name: 'banana'.num: 3},]}}})const app = new Vue({ el: '#app'.template: ` < div > < component > < - this item is object, by {} deconstruction - > < template v - slot = "{item}" > {{item. The name}} - {{item. Num}} < span v - if = "! }); Copy the code

Mixins with

  • mixinIs for the reuse of components
  • It is just a normal object, exposed inside the componentmixinsIt will be used after being introduced
  • mixinAll arguments within the objectExactly the same as the component(including mixins)支那
  • mixindefective
    1. The dependency relationship is vague and the corresponding source cannot be found
      • becausemixinYou can apply it on the insidemixin, it is difficult for us to find the fn we need in the nested relation
    2. A, B methods ()
  • mixinVue3compositionAPIban
const mixinB = {
    mounted() {
        console.log('hei~ I\'m mixinB'); }}const mixinA = {
    mixins: [mixinB],
    template: `<div>ComponentA</div>`.mounted() {
        console.log('hei~ I\'m mixinA');
    }
}

Vue.component("ComponentA", {
    mixins: [mixinA]
});

Vue.component("ComponentB", {
    mixins: [mixinA],
    template: `<div>ComponentB</div>`
});

const app = new Vue({
    el: '#app'.template: `
      
`
}); Copy the code

ref

  • We may need the real thing sometimesDOMElement to perform operations such asinputBox gets focus and stuff like that
  • This time we can use the specialattributefindDOM
Vue.component("ComponentA", {
    template: `<div> ComponentA <! -- attribute = ref --> <input ref="ref"/> </div>.mounted() {
        // use this.$refs to find the marked ref
        console.log(this.$refs.ref);
        this.$refs.ref.focus(); }})const app = new Vue({
    el: '#app'.template: `
      
`
}); Copy the code

Custom instruction

  • Except for the one aboverefIn addition, we can also get real through custom commandsDOMThere are also richer configuration items that allow us to customize the Vue directives we need
  • Vue3 updateddirectiveIn the life cycle instruction, so that the instruction and component life cycle as convenient for us to remember, later free to write custom instructions when detailed interpretation
Vue.directive('focus', {
    // el is the real DOM
    // Binding is a configuration item that contains parameters, modifiers, etc
    // Vnode is the virtual DOM of Vue
    // Bind is called when the directive is first bound to an element
    bind(el, binding, vnode) {
        console.log(el);
        console.log(binding);
        console.log(vnode);
        console.log('bind');
    },
    // Inserted Is called when the bound element is inserted into a parent (the parent is guaranteed to exist, but not necessarily already inserted into the document)
    inserted(el, binding) {
        console.log('inserted');
        el.focus();
    },
    // Other lifecycle updates, componentUpdated, and unbind are available on the official website
});

Vue.component("ComponentA", {
    template: `<div> ComponentA <! -- a.b and 132 + 123 can be obtained in binding --> <input V-focus. A.b ="132 + 123" /> </div>
});

const app = new Vue({
    el: '#app'.template: `
      
`
}); Copy the code