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