preface

Since the release of Vue3 in September 2020, so far it has been half a year, all the time, brush the document, read the article, peep at the video, is not a code ~ so ~ today ~ now ~ here ~ a

The differences between Vue3 and Vue2 are explained in the official website. The most direct explanation is the coding style. The fragmented logic in Vue2 can be combined with the newly provided CompositionAPI for maintenance, and the functions of individual logic can be broken down into separate files. Perfect solution to mixins in Vue2 (name conflicts, logical reuse, etc.)

Enter the Composition API

Composition Api

setup

Setup, a new option in Vue3, is the entry function to the Composition API. It is executed before beforCreate. Practice is the only test of truth

export default {
	 beforeCreate () {
	    console.log(`----beforeCreate----`);
	 },
	 created () {
	    console.log(`----created----`);
	 },
	setup () {
	    console.log(`----setup----`);
	    // This is similar to the vue2 lifecycle, but is written with the prefix on
	    onBeforeMount(() = > console.log(`----onBeforeMount----`));
	    onMounted(() = > console.log(`----onMounted----`));
	    onBeforeUpdate(() = > console.log(`----onBeforeUpdate----`));
	    onUpdated(() = > console.log(`----onUpdated----`));
	    onBeforeUnmount(() = > console.log(`----onBeforeMount----`));
	    onUnmounted(() = > console.log(`----onUnmounted----`)); }}Copy the code

There is no this in the setup option because the component instance was not created when setup was performed.

Note that the declaration cycle of Vue3 is slightly different from that of Vue2. In Vue3, it is put in the setup function and has a prefix on. Then the component lifecycle becomes more semantically before and after the instance is destructed

The setup parameters

Setup takes two parameters, props and context, the properties and context passed in by the component. The props in this case is responsive. Because it is responsive, you cannot use ES6 destruct

So what does context do?

When setup executes, there is no this object, so the context provides three attributes commonly used by Vue, attrs, slot and emit. The attR attribute slot slot and attR attribute in Vue2 respectively correspond to the attR attribute slot slot and attR attribute slot slot and emit emit events, and these attributes are automatically synchronized with the latest values

The default props and context

Ref, toRefs and reactive

Reactive data initialized in Vue2 is stored in data, but Vue3 makes a small change. Reactive data is declared through reactive data and reactive data

Reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive = reactive

<template>
  <div class="about">
    <h3>I'm About</h3>
    <p>
      {{ refValue }}
      <br />{{reactiveValue. Name}} : {{reactiveValue. Age}}<br />{{name}} : {{age}}</p>
</template>

<script>
import { ref, reactive } from "vue";
export default {
  setup () {
    // Declare the response variable
    const refValue = ref("-");
    const reactiveValue = reactive({ name: "XiaoNa".age: 17 });
    / / return
    return{ refValue, reactiveValue, ... toRefs(reactiveValue),// Destruct reactiveValue, which returns multiple variables name,age.....}; }};</script>
Copy the code

ToRefs is a tool that deconstructs objects and returns the key behind the structure to the component instance.

In addition to Vue2’s Options API, the Composition API introduces the vUE properties that need to be used on demand, which are declared via the setup entry function. It then performs a series of operations (life cycle, listening calculation, logic processing, etc.) and returns out to the component to render.

It is important to note that all responsive data, the component needs to use, must return out

Ok, let’s follow the above logic and practice the life cycle, listening calculation, logic processing and so on

The life cycle

<template>
  <div class="about">
    <h3>I'm About</h3>
    <p>
      {{ refValue }}
      <br />{{reactiveValue. Name}} : {{reactiveValue. Age}}<br />{{name}} : {{age}}</p>
  </div>
</template>

<script>
import {
  ref, reactive, toRefs, onMounted, onBeforeUpdate, onUpdated, onRenderTriggered,
} from "vue";

export default {
  setup (props, context) {
    onBeforeUpdate(() = > console.log(`----onBeforeUpdate----`));
    onUpdated(() = > console.log(`----onUpdated----`));
    // Add life cycle: what data to listen for changes
    onRenderTriggered((event) = >
      console.log(`----onRenderTriggered----`, event)
    );

    const refValue = ref("-");
    const reactiveValue = reactive({ name: "XiaoNa".age: 17 });

    // Assign to declare variables
    const D = new Date(a); onMounted(() = > {
      console.log("Executive mounted")
      refValue.value = `${D.getFullYear()}years${D.getMonth()}month${D.getDay()}Day `;
      reactiveValue.name = "Hisen";
      reactiveValue.age++;
    });

    return{ refValue, reactiveValue, ... toRefs(reactiveValue),// Destruct reactiveValue, which returns multiple variables name,age.....}; }};</script>
Copy the code

From the perspective of code, we can know that the setup function is entered first, and then the life cycle is executed. When the variable declaration is assigned to mounted, the new life cycle onRenderTriggered in Vue3 will be triggered. This life cycle is mainly used to monitor which data has changed. At this point, if we leave the current component, we trigger the declaration cycle before and after the destruction of the component, almost in the same order as Vue2. The specific life cycle can refer to the following figure

Monitoring and calculation

These two attributes, can be said to have written vue children’s shoes are not strange, but also will use a lot of, let’s see how to use them in Vue3 Chinese

<template>
  <div class="about">
    <h3>I'm About</h3>
    <p>
      {{ refValue }}
      <br />{{reactiveValue. Name}} : {{reactiveValue. Age}}<br />{{name}} : {{age}}</p>{{computedValue}}</div>
</template>

<script>
import {
  ref, reactive, watch, toRefs, onMounted, computed
} from "vue";

export default {
  setup () {
    const refValue = ref("-");
    const reactiveValue = reactive({ name: "XiaoNa".age: 17 });

    // Copy the declaration variable
    const D = new Date(a); onMounted(() = > {
      refValue.value = `${D.getFullYear()}years${D.getMonth()}month${D.getDay()}Day `;
      reactiveValue.name = "Hisen";
      reactiveValue.age++;
    });

    // Listen for multiple variables. Note how basic types and reference types are written differently
    Options supports deep, immediate, and flush
    watch(
      [refValue, () = > reactiveValue.age],
      ([n_ref, n_rt], [o_ref, o_rt]) = > {
        console.log("refValue:new", n_ref, "old", o_ref);
        console.log("reactiveValue.key: new", n_rt, "old", o_rt); }, {});// Calculate the properties
    let computedValue = computed({
      get: () = > reactiveValue.age + 10.set: val= > {
        // Create a writable ref object
        console.log(val)
      }
    })

    return{ refValue, reactiveValue, ... toRefs(reactiveValue),// Destruct reactiveValue, which returns multiple variables name,age.....computedValue }; }};</script>

Copy the code

Similarly, from the point of view of the logic of the code, the effect is achieved, careful children may find that the writing of watch is a little bit complicated, in fact, I directly write here a multi-variable simultaneously listen, and then multiple watches execute, of course, can also be written as a single listen, the specific usage is like this

watch(source, callback, [options])
source: can support string,Object.Function.Array; Used to specify the response variable to listen oncallbackOptions: Deep, immediate, and flush options are supported.Copy the code

The other little thing that I want you to notice is that, for the ref declaration and the reactive declaration, the first parameter that watch passes is required, so you have to write the variable name for the ref declaration, but for the reactive declaration you have to use the arrow function, something like that

const a = ref(); // watch(a,,() => {},{})
const b = reactive() // watch(() => b,() => {},{})
Copy the code

Multivariable listening at the same time looks like this

// The first parameter varies according to the type of declaration
watch([one,two],([new_one,new_two],[old_one,old_two]) = > { ... },{})
Copy the code

A computed attribute is created directly by creating a new variable that can be read and written (depending on a declared variable)

The logic processing part, not too much to share, in fact, Vue2 can be seen here, basically do all the operations in the setup function, and then return the response variable.

Component communication

This may be a lot of children’s more concerned about the matter, how to communicate components in Vue3, parameter passing, of course, Vuex is still general, so in small projects, simple communication how to do? Take a look at this with a small example

The parent component

<template>
  <div class="about">
    <h3>I'm About</h3>
    <p>
      {{ refValue }}
      <br />{{reactiveValue. Name}} : {{reactiveValue. Age}}<br />{{name}} : {{age}}</p>
    <hr />
    <button @click="addList">Push list.lengrth++</button>
    <br />
    <span v-for="i in reactiveValueArray"
          :key="i">{{I}}.</span>
  </div>
  <hr />
  <h3>The About child components</h3>
  <Detail :attr="reactiveValue"
          @addList="addAgeFun" />
</template>

<script>
import {
  ref, reactive, toRefs, provide, onMounted
} from "vue";
// Import the child component
import Detail from "./Detail.vue";
export default {
  // Register the subcomponent
  components: { Detail },
  setup () {
    const refValue = ref("-");
    const reactiveValue = reactive({ name: "XiaoNa".age: 17 });
    const reactiveValueArray = reactive([1.2.3.4.5]);
    // Copy the declaration variable
    const D = new Date(a); onMounted(() = > {
      refValue.value = `${D.getFullYear()}years${D.getMonth()}month${D.getDay()}Day `;
      reactiveValue.name = "Hisen";
      reactiveValue.age++;
    });
    // Button click event
    const addList = () = > {
      reactiveValueArray.push(reactiveValueArray.length + 1);
    };
    // The parent component listens to the child component emit
    const addAgeFun = (num) = > {
      reactiveValueArray.push(num._value);
    }
    // provide/inject
    provide("provideValue", {
      title: 'from about comp'.value: refValue
    })
    return{ refValue, reactiveValue, reactiveValueArray, ... toRefs(reactiveValue),// Destruct reactiveValue, which returns multiple variables name,age.....addList, addAgeFun, }; }};</script>

Copy the code

Child components

<template>
  <div>I'm a child of Detail, About<p>Props receive parameters: {{name | | "From the App link - the router"}} {{age | | "no attrs"}}</p>
    <p>
      <! -- inject_value can be deconstructed with toRefs -->Provie /inject receive parameters: static: {{inject_value.title}} Dynamic: {{inject_value.value._value}}</p>
    <input v-model="txt"
           type="text">
    <button @click="add">Add to the parent component's num array</button>
    <hr>
    <p>{{computedValue}}</p>
  </div>
</template>
 
<script> 
import { ref, reactive, toRefs, nextTick, inject, computed } from 'vue';
export default {
  / / props to receive
  props: {
    attr: Object
  },
  setup (props, context) {
    const txt = ref();
    // Get the attR passed by the parent component
    const attrs = reactive(props.attr);
    // Receive parameters in injection mode
    const inject_value = inject("provideValue");
    // The context contains emit, slot, props
    // Use emit publishing to pass parameters to parent component
    const add = () = > {
      nextTick(() = > {
        txt.value && context.emit("addList", txt);
        txt.value = ""})}// Calculate the properties
    let computedValue = computed({
      get: () = > txt.value,
      set: val= > {
        // Create a writable ref object
        console.log(val)
      }
    })
    return {
      ...toRefs(attrs),
      add,
      txt,
      inject_value,
      computedValue
    }
  }
};
</script>

Copy the code

props : The parameters passed by the parent, as in Vue2, need to be received by the child with props. However, all operations in Vue3 are in the setup function, so we use the setup function that takes two parameters, props/context

The setup function calls for the props to return.

Provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject = provide/inject Inject () ¶ Return inject (); return inject (); return inject ();

In Vue3, emit data in the second argument of the setup function context. In Vue3, emit data in the second argument of the context. In Vue3, emit data in the context. When a child component needs to pass an argument to its parent component, it will emit context.emit, using the $emit in Vue2. The parent component will emit the same listener mode, but the steup function is defined in the parent component, so remember to return out.

Note: Once again, all variables and methods that need to be used by the component must return at the end of the setup function. Otherwise, they cannot be found!

mixin

Looking at the mixin mixin of Vue2, the simple point is code merging, there are problems such as priority, variable/method naming conflict, non-semantic, etc. In Vue3, some people say reuse logic extraction, pure function programming, and some people say custom hook. In fact, both are correct. Custom hook itself is the encapsulation of repeated logic, easy to use everywhere, so let’s practice

<template>
  <div class="about">
    <h3>I'm About</h3>
    <h3>Mixins reuse code</h3>
    <input type="number"
           v-model="number">Mixins reuse the thousandths of an input number<br>
    <h4>{{mixinValue}}</h4>
    <button @click="mixinFunc(number)">change</button>
  </div>
</template>
<script>
import { ref } from "vue"
/ / import mixins
import Mixin from "./useMixin.js"
export default {
  setup () {
    const number = ref();
    // Get the reuse properties and methods
    const { mixinValue, mixinFunc } = Mixin();
    return{ number, mixinValue, mixinFunc, }; }};</script> 

Copy the code

Usemixin.js provides a way to convert ordinary numbers into thousandths

import { ref } from "vue"
export default function () {
  const mixinValue = ref();
  const mixinFunc = (n) = > {
    mixinValue.value = n.replace(/ \ d {1, 3} (? =(\d{3})+$)/g.function (s) {
      return s + ', '})}return { mixinValue, mixinFunc }
}
Copy the code

Mixin in Vue3 is basically a single definition of common logic and methods, or even a custom reuse hook, which can be used by any other component. At the same time, it also solves the disadvantages of variable naming conflict in Vue2, which is not semantic. Compared with Vue3, I may prefer this writing method, which is more clear and clear. Better to maintain

Vue Router

Routing is an essential tool in our development process, and the official Vue itself provides router is also very friendly, here Vue3 and Vue2 on the use of a little bit different, in fact, the setup does not have this, in Vue3 how to use it, we see an example

<template>
  <div class="about">
    <h3>I'm About</h3>
    <h4>Vue Router</h4>
    <router-link to="/copy_about">Router-link Jump mode About 2</router-link>
    <button @click="routerClick">routerClick</button>
  </div>
</template>

<script>
/ / import the router
import { useRoute, useRouter } from 'vue-router'
export default {
  setup () { 
    const router = useRouter();
    const route = useRoute();
    const routerClick = () = > {
      // Two methods of the router are used
      // router.push({ path: "/", query: { params: '123456789' } });
      router.push({ name: "Home".params: { params: '123456789'}}); }return{ routerClick }; }};</script> 

Copy the code

See the code, perhaps many children have understood, is to import on demand, which components need to use the route, the separate import, there are mainly two modules router, route route instance and route object, this familiar with the Vue2 route, should be not unfamiliar, that use is the same, Call the method of the route instance directly, jump, pass parameters, etc., take a screenshot to see what the router and route have

Vuex

State management, Vue standard, Vue3 in addition to the use of different, other can be said to be a hair, not say much, directly on the code

<template>
  <div class="about">
    <h3>I'm About</h3>
    <h4>Vuex</h4>Vuex Store state count value {{storeNum}}<button @click="vuexClick">commit / dispatch count++</button>
  </div>
</template>

<script>
import { computed } from "vue"
import { useStore } from "vuex"
export default {
  setup () {
    // vuex 
    const store = useStore();
    const storeNum = computed(() = > {
      return store.state.count;
    })
    const vuexClick = () = > {
      store.commit("addCount", { attr: Mutations, mutations. });
      store.dispatch("asyncAddCount", { attr: "Ginseng, actions" });
    }

    return{ storeNum, vuexClick }; }};</script> 

Copy the code

Store code can also be distributed in modules

import { createStore } from "vuex";

export default createStore({
  state: {
    count: 1
  },
  mutations: {
    addCount (state, obj) {
      console.log(state, obj) state.count++; }},actions: {
    asyncAddCount (context, obj) {
      console.log(context, obj)
      context.commit("addCount", obj)
    }
  },
  modules: {},});Copy the code

This is very similar to Vuex in VUE2, where a useStore hook is used to introduce a store to the component to be used. The state of the store needs to be obtained using computed attribute, so that the responsive update can be triggered. The only mutation that modifs state, as usual, requires a COMMIT trigger, so in the component it is store.com MIT (), as is actions

conclusion

In retrospect, the whole Vue3 commonly used knowledge points, we are silent over, of course, also do not rule out some small details of knowledge, the rest of the need to solve by themselves.

Github instance source code

Ok feel helpful can be concerned, like, comment, mutual praise, progress together

A link to the

  • Front-end visualization platform implementation scheme
  • 10 minutes takes you seconds to vue3
  • Vue template compilation principles
  • The extends principle of vue API
  • How does VUEX state management work
  • Vue-router source analysis principle
  • How does Vue listen for array changes
  • Handwritten Vue response [object.defineProperty]
  • Vue responsive source sharing