The article directories
- The change of the template
- New grammar
- setup
- ref
- reactive
- toRefs
- Life cycle changes
- The change of the watch
- The change of the computed
- The new labels
- Teleport
- Suspense
- Custom HOOKS
- Develop components with TypeScript
The change of the template
In Vue2, each template node can have only one root node:
<template>
<div>123</div>
</template>
Copy the code
In Vue3, there can be multiple root nodes:
<template>
<div>123</div>
<div>456</div>
</template>
Copy the code
This change allows us to reduce unnecessary HTML tag writing during development. Take this example:
<template>
<table>
<tr>
<columns />
</tr>
</table>
</template>
Copy the code
Here, the
<template>
<div>
<td>Hello</td>
<td>World</td>
</div>
</template>
Copy the code
Get a
output:
<template>
<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>
</template>
Copy the code
The generated HTML is invalid. The Template change in Vue3 solves this problem.
New grammar
setup
Setup () replaces data() and is executed only once.
Setup () can accept two arguments, props and context(it can be omitted if it is not used).
<template> <div> <h1>num:{{num}}</h1> </div> < button@click ="add"> add 1</button> </template> <script lang="js"> export default { name: 'App', setup() { let num = 0; const add = () => { num++; }; console.log(num); return { add, num, }; }}; </script>Copy the code
The above example is a basic setup procedure. But if we try to click the + 1 button, num doesn’t change. If we look at the console, we see that the output is a value of number, but our click doesn’t change anything. To solve this problem, we will use a new API: Ref.
ref
Let’s modify the above code:
< the template > < div > < h1 > MSG: {{MSG}} < / h1 > < h1 > num: {{num}} < / h1 > < / div > < button @ click = "add" > 1 < / button > < / template > <script> import { ref } from 'vue'; export default { name: 'App', setup() { const msg = ref(0); let num = 0; const add = () => { msg.value++; num++; }; console.log(msg); console.log(num); return { msg, add, num, }; }}; </script>Copy the code
If you look at the import method of ref, you can see that the import is on demand. If we look at the console output here, we can see that MSG prints RefImpl and num prints 0. MSG can be changed, but num cannot.
What is a RefImpl? I understand it as a proxy object. For example, we know that data in Vue2 is intercepted via Object.defineProperty(). So as to achieve the purpose of data response type. Vue3 uses proxy in ES6 to achieve the purpose of data responsiveness.
As for the use of proxy, take a simple example:
const target = {
message1: "hello".message2: "everyone"
};
const handler = {
get: function(target, prop, receiver) {
return "world"; }};const proxy = new Proxy(target, handler);
console.log(proxy.message1); // world
console.log(proxy.message2); // world
Copy the code
Ref is a function that takes an argument and returns a responsive object. In this example, the 0 we initialized is wrapped around the object as a parameter, and changes can be detected and made when the value is manipulated in the future.
reactive
To create a reactive state for an object, use the reactive method. Example code is as follows:
<template> <div> <h1>count:{{ obj.count }}</h1> <h1>double:{{ obj.double }}</h1> </div> <button @click="obj.increase"> add 1</button> </template> <script> import {reactive, computed} from 'vue'; export default { name: 'App', setup() { const obj = reactive({ count: 0, increase: () => { obj.count++; }, double: computed(() => obj.count * 2), // here is computed in vue3}); return { obj, }; }}; </script>Copy the code
At this point we can click the button to change the state. {{obj. Count}} {obj. Count}}
< the template > < div > < h1 > count: {{count}} < / h1 > < h1 > double: {{double}} < / h1 > < / div > < button @ click = "happens" > 1 < / button > </template> <script lang="js"> import { reactive, computed } from 'vue'; export default { name: 'App', setup() { const obj = reactive({ count: 0, increase: () => { obj.count++; }, double: computed(() => obj.count * 2), }); return { ... obj, }; }}; </script>Copy the code
But when we click the button, it doesn’t change the state. Why? This is because deconstruction breaks the agent, turning it into a generic value. As in the ref example above, clicking the button does not change. At this point, it’s time to call out another new API, toRefs:
toRefs
It is easy to use, so you can add it when you return:
<! HTML is omitted as above. --> <script lang="js"> import { reactive, computed, toRefs } from 'vue'; export default { name: 'App', setup() { const obj = reactive({ count: 0, increase: () => { obj.count++; }, double: computed(() => obj.count * 2), }); return { ... toRefs(obj), }; }}; </script>Copy the code
This function is useful when toRefs returns a reactive object from a composite function so that the returned object can be deconstructed/extended using components without losing reactivity.
Life cycle changes
Vue2 | Vue3 |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
Example:
<script lang="js"> import { onMounted } from 'vue'; export default { setup() { // mounted onMounted(() => { console.log('Componentis mounted! '); }); }}; </script>Copy the code
The change of the watch
The use of Watch in VUe3 is similar to that in VUe2.
The change of the computed
The use of computed in VUe3 is similar to that in VUe2. In VUE3, however, it can be written inside or outside reactive.
The uses of Watch and computed are as follows:
<script lang="js"> import { reactive, computed, watch, toRefs } from 'vue'; export default { setup() { const data = reactive({ count: 0, increase: () => { data.count++; }, double: computed(() => data.count * 2), }); const conComputed = computed(() => data.count * 2); const number = ref(0); watch(data, () => { console.log(data); document.title = 'updated ' + data.count; }); watch(number, () => { console.log(number); }); return { number, conComputed, ... toRefs(data), }; }}; </script>Copy the code
In VUE3,watch and computed need to be introduced before they can be used.
The new labels
Teleport
Usually our mask layer exists under some multilevel tag, which is actually not reasonable. Teleport makes it possible to move the component we wrote below the specified tag. To is which TAB to move to, and it supports selectors.
Example code is as follows:
<template>
<teleport to="#modal">
<div id="center">
<h1>this is a modal</h1>
</div>
</teleport>
</template>
<script lang="js">
export default {
name: 'modal',
};
</script>
<style scoped>
#center {
width: 200px;
height: 200px;
background: red;
position: fixed;
left: 50%;
top: 50%;
margin-left: -100px;
margin-top: -100px;
}
</style>
Copy the code
<template> <div id="modal"> <div> <div> <modal v-if="show"></modal> </div> </div> </div> <button @click="show = ! show">show</button> </template> <script lang="js"> import { ref } from 'vue'; import Modal from './components/modal.vue'; export default { name: 'App', components: { Modal, }, setup() { const show = ref(false); return { show, }; }}; </script>Copy the code
Before clicking the button:
After clicking the button:
Looking at the DOM structure before and after the button is clicked, we see that even though the Modal component is used after multiple divs are nested under Div # Modal, it still moves under Div # Modal when displayed.
Suspense
Suspense is useful in asynchronous request scenarios. The following is an example:
// MyAsyncComponent.vue <template> <h1>I have some async work to do before I can render</h1> </template> <script> export default { name: 'MyAsyncComponent', async setup() { await someAsyncWork(); } } </script>Copy the code
// SuspenseWithError.vue
<template>
<slot v-if="error" name="error"></slot>
<Suspense v-else>
<template #default>
<slot name="default"></slot>
</template>
<template #fallback>
<slot name="fallback"></slot>
</template>
</Suspense>
</template>
<script>
import { ref, onErrorCaptured } from 'vue'
export default {
name: 'SuspenseWithError',
setup() {
const error = ref(null);
onErrorCaptured((e) => {
error.value = e;
return true;
});
return { error };
}
}
</script>
Copy the code
<template> <SuspenseWithError> <template #default> <MyAsyncComponent /> </template> <template #fallback> <span>Loading... Please wait.</span> </template> <template #error> <h1>I failed to load</h1> </template> </SuspenseWithError> </template> <script> import MyAsyncComponent from '@/components/MyAsyncComponent.vue'; import SuspenseWithError from '@/components/SuspenseWithError.vue'; export default { name: 'App', components: { MyAsyncComponent, SuspenseWithError }, } </script>Copy the code
Custom HOOKS
The following is an example:
<script lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
const useMousePosition = () = > {
const x = ref(0);
const y = ref(0);
const updateMouse = (e: MouseEvent) = > {
x.value = e.pageX;
y.value = e.pageY;
};
onMounted(() = > {
document.addEventListener('mousemove', updateMouse);
});
onUnmounted(() = > {
document.removeEventListener('mousemove', updateMouse);
});
return { x, y };
};
export default useMousePosition;
</script>
Copy the code
<script lang="js"> import useMousePosition from '@/hooks/useMousePosition'; export default { setup() { const { x, y } = useMousePosition(); return { x, y, }; }}; </script>Copy the code
Develop components with TypeScript
In VUE3, creating a component requires a defineComponent wrapper.
The following is an example:
<script lang="ts">
import { defineComponent, PropType, computed } from 'vue';
// Export interface types. Import the interface and define the interface during use
export interface ColumnProps {
id: number;
title: string; avatar? :string;
des: string;
}
export default defineComponent({
name: 'ColumnList'.props: {
list: {
type: Array as PropType<ColumnProps[]>,
required: true,}},setup(props) {
// props is used here
const ColumnList = computed(() = > {
return props.list.map((item) = > {
if(! item.avatar) { item.avatar =require('@/assets/logo.png'); // Default image
}
return item;
});
});
return{ ColumnList, }; }}); </script>Copy the code
<template> <div id="container"> <column-list :list="list"></column-list> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; import ColumnList, { ColumnProps } from '@/components/column-list.vue'; const testDate: ColumnProps[] = [ { id: 1, title: 'test1', des: 'test1', avatar: 'https://picsum.photos/id/239/200/200', }, { id: 2, title: 'test1', des: 'test1', avatar: 'https://picsum.photos/id/239/200/200', }, { id: 3, title: 'test1', des: 'test1', avatar: 'https://picsum.photos/id/239/200/200', }, { id: 4, title: 'test1', des: 'test1', avatar: 'https://picsum.photos/id/239/200/200', }, ]; export default defineComponent({ name: 'App', components: { ColumnList, }, setup() { return { list: testDate, }; }}); </script>Copy the code
The resources
- Vue3 official document
- Vue3+TS experience and development +Vite analysis
- Go async in Vue 3 with Suspense
- Taste the new front end toy Vite