Vue3. X starts from scratch (2) — Rethinking Vue components

Time:2021-2-26

Vue 3 updates the syntax of many components, including lifecycle, filter, setup, teleport, etc

In order to introduce these features, you need to understand the basic play of Vue components

The content of this article is basically based on the syntax of Vue 2, only some details are adjusted

 

1、 Single file component

adoptVue.createApp()Create an application that you can usecomponentMethod to create a custom component

const app = Vue.createApp({});
app.component('my-component', {
  data() {
    return {
      name: 'Wise.Wrong'
    }
  },
  template: `hello {{name}}`,
});

But in the actual project, they are all used“Single file component”In other words.vueForm development of document

For example, the above component is rewritten as a single file component

hello {{ name }}



< span style = "background color: RGBA (245, 245, 245, 1); color: RGBA (0, 128, 0, 1)" > / * < / span > < span style = "background color: RGBA (245, 245, 245, 1); color: RGBA (0, 128, 0, 1)" > current file path: Src / components / my- component.vue  </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0,  1)">*/</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">
import { defineComponent } from </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">vue</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;<br>
export </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">default</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> defineComponent({
  name: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">MyComponent</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
  data() {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">return</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      name: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">Wise.Wrong</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
    };
  },
});
</span>

Here, data must return an object in the form of a function, instead of using the object directly

//2. X can receive objects, but 3. X’s data can only be function

 

Suppose there’s another component now, we hope toComponent

Can be directly in theIntroduced inmy-component.vueFile, and then throughcomponentsProperty registration component

In the above code, I used the camel named component in component components, while inShort line naming is used in

This is because HTML itself is not case sensitive, and Vue suggests that we use it in DOMUse all lowercase short lines for namingThe way

 

2、 Props

Now we have oneParent component AndSubcomponents

If I want to pass parameters to the child component in the parent component, I can use theProps

First of allSubcomponentsdefinitionprops

Then the parent component passes in the corresponding props attribute value

final result

In addition to defining array props, you can also use object types, so you can make more restrictions on props

export default defineComponent({
  // ...
  props: {
    Likes: string, // type only
    years: {
      type: [String, Number],
      Required: true, // required
    },
    info: {
      type: Object,
      Default: () = > ({// the default values of object and array types can only be returned by functions
        Title: 'here is the prop of the object type',
      }),
    },
    type: {
      //Custom check function
      validator: (val: string) => (
        ['success', 'warning', 'danger'].indexOf(val) !== -1
      ),
    },
  },
  // ...
});

If the parameters passed by the parent component do not conform to the rules defined by props in the child component, Vue will throw an error in the console

More about the use of props can be found atOfficial documents

 

3、 Stateless component

In the subcomponent, props and data are directly mounted under this object, which can be accessed through the this.xxx How to choose the value

For some functional components, you only need props, not data. For example, a prompt box component like this:

{{content}}
  


<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">
import { defineComponent } from </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">vue</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;

export </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">default</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> defineComponent({
  name: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">Alert</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
  props: [</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">type</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">content</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">],
});
</span>

This method only receives props and does not define data, methods, computed, etcResponse dataIs called stateless component

In Vue 2, the initialization of stateless components is much faster than that of ordinary components, so it is often used as a consideration of performance optimization

In order to create a stateless component, theStatement infunctional

Vue 2

However, in Vue 3, stateless components no longer need to declare “functional”

You just need to define it like a normal component, just don’t add data and other response data, just like the prompt box component above

The official document explains this:

However, in Vue 3, the performance of stateful components has improved to the point that the difference is negligible.

However, in Vue 3, the performance of stateful components has been improved to the level of stateless components.

Emmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm

But I like it~

 

4、 Mixin

Corresponding to stateless components are stateful components, that is, components with data and other response data. Most of the components we write in our work are stateful components

With the continuous expansion of business, the whole application becomes very bloated. At this time, component sharing and logic extraction are particularly important, which can be used at this timemixin

 

If we have three components, their JS parts all contain the following contents:

<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">
export </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">default</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
  </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> ...</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">  data() {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">return</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> ...</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      company: {
        name: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">Perfect World</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
        address: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">Chongqing China</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
      },
      loading: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">false</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
    }
  },
  props: {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> ...</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">    title: String
  },
  methods: {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> ...</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">    fetchCompanyInfo(data) {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.loading </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">true</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">return</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> fetch(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">/api</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">)
        .then(res </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=></span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> res.json())
        .then(res </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=></span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.company </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> { ...res.data };
        })
        .</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">finally</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">(_ </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=></span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> { 
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.loading </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">false</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
        })
    }
  },
};
</span>

The three components all contain response data loading and company, corresponding query methods fetchinfo, and a parameter (props) title from the parent component

The logic is exactly the same. If you want to write exactly the same code in each of the three components, it will be very uncomfortable and not conducive to maintenance

With mixin, we can write the logic in an object and export it

/* mixin-test.js */
export default {
  data: () => ({
    loading: false
  }),
  props: {
    title: String
  },
  methods: {
    fetchCompanyInfo(data) {
      // ...
    }
  }
}

Then it is introduced into the components that need to use this part of logic, and registered to mixins

Mixins receive an arrayThat is to say, multiple mixins can be registered at the same time

<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">
import MixinTest from </span><span style="background-color: rgba(245, 245, 245, 1)">"<span style="background-color: rgba(245, 245, 245, 1)">./mixin/mixin-test.js</span></span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;

export </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">default</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
  </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> ...</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">  mixins: [ MixinTest ],
};
</span>

Vue initializes the component by comparing the mixin object with the current componentmerge

When merging, if you encounter the same name option, you will have different strategies for different types (data, props, methods)

1. If yesIf props, methods, components and directives conflict, mixin will be overridden by the key value pair of the component itself

2. aboutIf the hook function conflicts in the lifecycle, the hook function in the mixin will be triggered first, and then the hook function in the component will be triggered

3. IfData conflicts, shallow merge will be performed:

const Mixin = {
  data: ()=> ({
    user: { name: 'Jack', id: 1 }
  })
}

const Comp = {
  mixins: [Mixin],
  data: () => ({
    user: { id: 2 }
  })
}

//The final result is:
{
  user: { id: 2 }
}

And in Vue 2In, data will perform deep level merging, and the result of the above example will become:

{
  user: { id: 2, name: 'Jack' }
}

In the era of Vue 2, mixin is a common method to encapsulate common logic, but this method has been questioned by many developers

The first is the problem of duplicate names of mixins, although Vue has a set of merge policies (which can be customized) to deal with the problem of duplicate names of mixins

However, it is impossible to know which fields are provided by mixin inside the component. If later maintenance personnel are not familiar with mixin in the component, it is easy to cause bugs due to duplicate names when maintaining code

Moreover, mixin can use the data in components. If the data on which mixin depends is changed when maintaining components, it will also lead to bugs

To solve these problems, Vue 3 launchedComposition APITo encapsulate component logic with the idea of functional programming

Want to know Composition APIHow about next time~