This is the 30th day of my participation in the Wenwen Challenge
preface
Today is the last day of the challenge. After thinking for a long time, I finally decided to write a personal summary about Vue3, not only as a perfect ending in June, but also as a preparation for July!
The preview contents are as follows:
The difference between Vue2 and Vue3
- Refactoring responsive systems, using
Proxy
replaceObject.defineProperty
.Proxy
Advantage:
- You can listen directly for changes in array type data
- The target of listening is the object itself, not the image
Object.defineProperty
Iterate through each property the same, with some performance improvement - More things can be intercepted, provided
apply
,ownKeys
,has
Etc. 13 interceptor methods
- new
Composition API
, better logic reuse and code organization - refactoring
Virtual DOM
- Template compile-time optimizations to compile some static nodes into constants
slot
Optimization,slot
Compiled intolazy
Function,slot
Rendering decisions are left to child components- Extracting and reusing inline events in templates (originally regenerating inline functions for each rendering)
- Code structure adjustment, more convenient
Tree shaking
, making the volume smaller - use
Typescript
replaceFlow
Why did Vue3 introduce composition API
First we use the form Vue2. X to implement a simple function
Requirements:
1. Delete the item when clicking on the item list
2. When the Add button is clicked, a row will be added to the list of goods according to the data in the form
The code is as follows:
<template>
<div class="wrap">
<section>
<h6>List of goods</h6>
<table>
<thead>
<td>The serial number</td>
<td>The name of the</td>
<td>The unit price</td>
<td>discount</td>
<td>After the discount price</td>
</thead>
<tbody>
<tr
v-for="(fruit, index) in fruits"
:key="fruit.id"
@click="remove_item(index)"
>
<td>{{ fruit.id }}</td>
<td>{{ fruit.fruit_name }}</td>
<td>{{ fruit.price }}</td>
<td>{{ fruit.discount }}</td>
<td>{{(fruit.price * fruit.discount).tofixed (2)}} yuan/kg</td>
</tr>
</tbody>
</table>
<br />
</section>
<section>
<h6>To add an item, type:</h6>
<form>Product Serial number:<input type="text" v-model="f.id" /><br />Product Name:<input type="text" v-model="f.fruit_name" /><br />Unit Price:<input type="text" v-model="f.price" /><br />Discount discount:<input type="text" v-model="f.discount" /><br />
<button @click="add_item">add</button>
</form>
</section>
</div>
</template>
<script>
export default {
name: "App".data: function () {
return {
fruits: [{id: 1.fruit_name: "apple".price: 10.discount: 0.8 },
{ id: 2.fruit_name: "banana".price: 3.discount: 0.7 },
{ id: 3.fruit_name: "orange".price: 5.discount: 0.5 },
{ id: 4.fruit_name: "durain".price: 50.discount: 0.8},].f: {
id: 5.fruit_name: "".price: "".discount: "",}}; },methods: {
remove_item(index) {
this.fruits = this.fruits.filter((item, key) = >index ! == key); },add_item(e) {
e.preventDefault();
let temp = Object.assign({}, this.f);
this.fruits.push(temp);
this.f.id = this.fruits.length + 1;
this.f.fruit_name = "";
this.f.price = "";
this.f.discount = ""; ,}}};</script>
Copy the code
Simple dot style
.wrap{
width: 600px;
margin: 10px auto;
display: flex;
justify-content:space-around;
background-color:rgb(253.247.247);
border-radius:4px;
}
.wrap table thead{
background-color: deepskyblue;
font-weight: bold;
font-size: 0.9 em;
}
.wrap table tr:nth-of-type(2n+1) {background-color:pink;
}
.wrap form{
font-size: 0.9 em;
}
.wrap button{
margin-top: 10px;
width: 100%;
color: rgb(224.43.31);
font-weight: 700;
}
Copy the code
The following output is displayed:
In the above example, we can see that using the option API of 2.x, whenever a function is implemented, some data is added to data and business logic is added to methods. Fortunately, there are only two simple functions, but in practice, when many functions are added, it becomes very difficult to find the corresponding data and business logic of a function, and the business logic may not necessarily be in methods, but may also be scattered in alternatives such as computed and watch. Therefore, Vue3.0 introduced composition API to solve the problem of function, data, and business logic decentralization, making the project more modular development and later maintenance
Vue3 uses the V-Model more elegantly
We know how to implement two-way data binding in Vue2.0: v-Model and sync. Typically, a component can only be used for one V-Model, but some components need to have multiple data that can respond in both directions, and that’s where.sync comes in
In Vue3.0, in order to achieve uniformity, a component can have multiple V-models, and.sync was removed. In Vue3.0, v-Model needs to be followed by a modelValue, that is, the attribute name to be bidirectionally bound. Vue3.0 implements multiple V-Models by assigning different Modelvalues to different V-Models
The difference between Composition API and React Hook
From the perspective of React Hook implementation, React Hook determines which useState state comes from the next rendering according to the order of useState calls, so the following restrictions appear
- Cannot be called in a loop, condition, or nested function
Hook
- Make sure to always be in your
React
The top-level call to a functionHook
useEffect
,useMemo
Such functions must manually determine dependencies
The Composition API is based on Vue’s responsive system implementation, compared to React Hook’s
Composition API
The statement insetup
A component instantiation is called only once within a functionsetup
And theReact Hook
This is called every time you rerenderHook
,React
theGC
thanVue
More pressure, more performance thanVue
It’s also slower to speakCompositon API
Can also be used in loops, conditions, and nested functions- Reactive systems automatically implement dependency collection, which in turn optimizes the performance of component parts
Vue
The interior does it by itself, whileReact Hook
Dependencies need to be passed in manually, and the order of dependencies must be guaranteed so thatuseEffect
,useMemo
Such functions correctly capture dependent variables, otherwise the component performance will degrade due to incorrect dependencies
The Compositon API looks better than React Hook, but the Compositon API is designed with React Hook in mind
Ref function
As we know, the Setup function is at the heart of the composition API introduced in Vue3.0
In the setup function, you can use the ref function, which creates a responsive data. When the data changes, the Vue automatically updates the UI. For example, use the ref function to define a variable count
import { ref } from 'vue';
function useChangeCount() {
let count = ref(0);
function change_count() {
count.value += 1;
}
return { count, change_count }
}
export default useChangeCount;
Copy the code
Import useChangeCount from “./composition_tiny_js/count” and use it in the setup of the component, exposing variables and functions that need to be used by the outside world via a return
setup() {
let { count, change_count } = useChangeCount();
return { count, change_count };
}
Copy the code
This allows the change_count method, the count variable exposed above, to be used externally
<template>
<div>
<h1>{{ count }}</h1>
<button @click="change_count">Am I</button>
</div>
</template>
Copy the code
Note that:
- in
setup
All variables or methods defined in thereturn {xxx,xxx}
So the outside world can use it ref
Functions can only listen for changes to primitive types, not complex types (such as objects, arrays)
Reactive function
Reactive is used in a similar way to ref in that it turns data into reactive data and automatically updates the UI when the data changes. The difference is that ref is used for basic data types, whereas Reactive is used for complex data types, such as objects and arrays. For example, user is a variable that defines an object type
<template>
<div>
<p>{{ user }}</p>
<button @click="increase">click me! one year later</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "reactive".setup() {
const user = reactive({ name: "Alice".age: 12 });
function increase() {
++user.age
}
return{ user, increase }; }};</script>
Copy the code
As above, when the button is clicked, let the datauser.age
+ 1. When Vue finds that the data has changed,UI
It’s automatically updated so we’ve verified it. Yesreactive
The function can turn a complex data type into reactive data. We might as well putreactive
I’m going to print out the result of the function and let’s see, what does it returnAnd what we found is,reactive
The result is to wrap the passed objectproxy
object
So let’s test that out. What if we pass primitive data types?
<template>
<div>
<p>{{ userAge }}</p>
<button @click="increase">click me! one year later</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "reactive".setup() {
let userAge = reactive(12);
function increase() {
console.log(userAge);
++userAge;
}
return{ userAge, increase }; }};</script>
Copy the code
The runtime finds that the basic data is passed to Reactive, which does not wrap it as a Porxy object, and the interface does not change when the data changes
Note that the parameters passed in Reactive must be json objects or arrays. If other objects (such as new Date()) are passed, the interface will not update automatically by default. If reactive values are required, they can be reassigned
Note about the setup function
As we know, the setup function is the core entry function to the composite API. There are a few things to note when using it:
setup
The execution time of the function is inbeforeCreate
andcreated
between- Due to the
setup
The execution time is atcreated
Between, so the component has just been created, whiledata
andmethods
It’s not initialized yet, so it can’tsetup
The use ofdata
andmethods
setup
In thethis
Point to theundefined
setup
It can only be synchronous, not asynchronous
Recursive and non-recursive listening
We know that the REF and Reactive functions are used to turn an ordinary data into a reactive data. When the data changes, the interface updates immediately. In fact, there is another rule, is to deeply listen to each layer of data, we call recursive listening
import { reactive } from "vue";
export default {
setup() {
const alice = {
name: "Alice".age: 80.sex: 'female'.child: {name:'Tom'.sex: 'male'.age:59.child: {name:'Frank'.sex: 'male'.age:30.child: {name:'Blue'.sex: 'male'.age:3}}}}const AliceFamily = reactive(alice );
return{ AliceFamily }; }};Copy the code
In the above example, VUE packages each layer of Alice, the object passed by us, into a proxy object by means of reactive function, and deeply monitors every data of each layer of the object. When any data of any layer changes, VUE will detect and update the corresponding UI ref, similarly. Because ref is reactive by nature
Ref (12) reactive({value:12})Copy the code
The benefit of recursive listening is obvious, you can listen to every change of data, but because you need to listen to every data, when the data is very complex, VUE needs to tell every data to be wrapped by Proxy once, this process is very costly for large data volume. So to solve this problem, VUe3 provides two functions to create shallow listening data, i.e., non-recursive listening. The two functions are:
shallowRef
shallowReactive
Note:
- If it’s through
shallowRef
Create data, thenvue
Listening is.value
Not the first layer - In addition
vue3
Also providestriggerRef
Function to manually update the interfaceUI
. But triggerReactive is not provided, so there is no way to manually trigger interface updates if the data is reactive.
So one might wonder: when do you use recursive listening and when do you use non-recursive listening? In fact, just remember that non-recursive listening is designed to solve the problem of listening to a large amount of data. Therefore, shallowRef and shallowReactive are only considered when the data is very large. Generally, ref and Reactive are used
The toRaw and markRaw functions
In the setup function, we create reactive data through the ref and Reactive functions, which update the UI every time the data is changed. This problem is very costly in performance
Therefore, if there are some operations that do not need to update the UI interface every time we modify the data, we can obtain the original data before Proxy packaging through the toRaw method provided by VUe3, and then modify the original data to modify the internal data of the corresponding Proxy object. This is changed through raw data modification and does not trigger UI updates
<script>
import { reactive, toRaw } from "vue";
export default {
setup() {
const obj1= {name:'alice'};
const user = reactive(obj1);
const obj2 = toRaw(user);
console.log(obj1 === obj2);//true
function change() {
obj2.name = 'Frank';
}
return{ user, change}; }}; </script>Copy the code
In the example above, the changes are made through the wrapped reactive object User, and the interface is updated immediately. But if you modify the data by modifying the original data obj2, the interface will not be updated. Obj2 ===obj1 shows that toRow is used to fetch raw data from a Proxy object
Get raw data from Reactive. Get raw data from REACTIVE
Reactive ({value: obj}); reactive({value: obj}); reactive({value: obj}); reactive({value: obj}); Because the.value is the original data that was passed in when it was created
import { ref, toRaw } from "vue";
export default {
setup() {
const obj1= {name:'alice'};
const user = ref(obj1);
const obj2 = toRaw(user.value);
console.log(obj1,user,obj2);
return{ user }; }};Copy the code
In cases where we want data to never be traced in future operations, VUe3 provides a method: markRaw
setup() {
const obj= {name:'alice'.age:18};
obj= markRaw(obj);
const user= reactive(obj);
function change(){
user.age=19
}
return { user , change};
}
Copy the code
In this code, obj is marked by markRaw. When we change obJ into a proxy object, we find that the interface does not update when we call change to change data.
ToRef and toRefs functions
We know that ref can be used to create a responsive data, and toRef can create a responsive data, so what’s the difference between them?
In fact, if the ref function is used to change attributes in an object to reactive data, modifying the reactive data does not affect the original data.
import {ref} from 'vue';
export default {
name:'App'
setup(){
let obj = {name : 'alice'.age : 12};
let newObj= ref(obj.name);
function change(){
newObj.value = 'Tom';
console.log(obj,newObj)
}
return {newObj,change}
}
}
Copy the code
In the above code, when change is executed, the reactive data changes, but the raw data obJ does not.
And the reason is,ref
Is a copy of the nature, and the original data has no reference relationship
Ref (obj.name) = ref(‘ Alice ‘) = reactive({value:’ Alice ‘}
If toRef is used to turn attributes in an object into reactive data, modifying the reactive data will affect the original data. Note, however, that modifying reactive data created through toRef does not trigger an update to the UI interface.
So, toRef is essentially a reference, associated with the original data
import {toRef} from 'vue';
export default {
name:'App'
setup(){
let obj = {name : 'alice'.age : 12};
let newObj= toRef(obj, 'name');
function change(){
newObj.value = 'Tom';
console.log(obj,newObj)
}
return {newObj,change}
}
}
Copy the code
In the above code, when change is executed, the reactive data changes, the original data OBJ does not change, but the UI interface is not updated
Summary:
Ref and toRef
(1). Ref is essentially copy, and modifying responsive data will not affect the original data; The nature of toRef is referential, and modifying reactive data affects the original data
(2). If the ref data changes, the interface will update automatically; ToRef The interface does not update automatically when data changes
(3). ToRef pass participating ref is different; ToRef takes two arguments, the first which object and the second which property of the object
So if you want reactive data to be associated with previous data, and you want to update reactive data without updating the UI, use toRef
Sometimes, we want to change multiple attributes of an object to reactive data, and require the reactive data to be associated with the original data, and update the reactive data without updating the interface. ToRefs can be used to batch set multiple data to reactive data. (toRef can only set one data at a time)
ToRefs takes an object as an argument, iterates through all the attributes of the object, and then calls toRef one by one
For example,
import {toRefs} from 'vue';
export default {
name:'App'
setup(){
let obj = {name : 'alice'.age : 12};
let newObj= toRefs(obj);
function change(){
newObj.name.value = 'Tom';
newObj.age.value = 18;
console.log(obj,newObj)
}
return {newObj,change}
}
}
Copy the code
Use the customRef function to customize a ref
As we know, the REF function can create a reactive data that updates the UI as the data is updated
Sometimes we want to be able to display the control dependency trace and trigger the response, so we can use the customRef function to customize a ref
Return customRef()
Note:
The customRef function takes a factory function with two arguments, track for tracing and trigger for triggering the response, and returns an object with get and set methods
Official example: Implementing v-Model with stabilization using custom ref:
<input v-model="text" />
Copy the code
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) = > {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() = > {
value = newValue
trigger()
}, delay)
},
}
})
}
export default {
setup() {
return {
text: useDebouncedRef('hello'),}}}Copy the code
In the code above, displaying track() in get means that the data needs to be tracked, and calling trigger() in set means that the UI needs to be updated when the data is modified.
To customize a ref function using the customRef function:
1. The function returns customRef()
2. CustomRef accepts two parameters track and trigger
3. CustomRef returns an object
4. The object returned by customRef must implement the set and GET methods
5. The call track() displayed in GET means that the data needs to be tracked, and the call trigger() displayed in set means that the UI needs to be updated when the data is modified
How do I get elements from the ref attribute
In vue2. X, you can add the ref=’ XXX ‘attribute to the element and then get the corresponding element in the code through this.$refs.xxx
However, there is no such thing as $refs in VUe3, so elements obtained by the ref attribute in VUe3 cannot be retrieved in vue2’s way
Vue3 requires lifecycle methods for the simple reason that the elements in template are not yet mounted to the page when setup is executed, so they must be retrieved after Mounted.
<template>
<div ref='box'>I am DIV</div>
</template>
<script>
import {ref,onMounted}
export default{
setup(){
let box = ref(null);
onMounted(() = >{
console.log(box.value)
});
return {box}
}
}
</script>
Copy the code
As shown in the code above, in VUe3, all lifecycle methods are removed and need to be imported directly. I’m importing onMounted here
When the interface is mounted, the onMounted callback is automatically executed to retrieve dom elements
summary
1. How to use lifecycle functions in compositionAPI?
Import the import of any lifecycle function you need and call it in setup
2. How does VUe3 get elements on the interface through ref attribute?
Template is written the same way as vue2. Add a ref=' XXX 'to the element.
In Setup, you create and expose reactive data
When the element is created to fit, the corresponding response data is assigned a value
Once the reactive data is assigned, you can use the lifecycle method to get the corresponding reactive data, the DOM element, in the lifecycle method
The readonly and shallowReadonly functions
Vue3 provides read-only functions: readonly, shallowReadonly, and isReadonly
Their uses and differences are obvious:
Readonly: Used to create a read-only data that is deep read-only or recursive read-only
ShallowReadonly: Also used to create a read-only data, but this read-only is layer 1, not deep read-only
IsReadonly: Checks whether a data is read-only
We know that variables defined by const cannot be changed. What’s the difference between readonly and const?
-
Const is assignment protected. We use a variable defined by const that cannot be reassigned. But if const is assigned to an object, then the contents of the object can be changed. The reason is that a variable defined by const cannot be changed. That is, the address of the object cannot be changed
-
Readonly protects properties and cannot reassign values to properties
Teleport components
Teleport, known as a portal component, is used to provide a concise way to specify the parent element of its contents
For example, define a modal box that is originally inside a component, but I want it to be under the body when it pops up, level with #app. The following code
<template>
<button @click="modalsOpen = true">Popup button</button>
<teleport to='body'>
<div v-if="modalsOpen" class="modals">
<div>
<p>This is a pop-up box that is passed to the body</p>
<button @click="modalsOpen = false">Shut down</button>
</div>
</div>
</teleport>
</template>
<script>
import { ref } from "vue";
export default {
name: "Modals".setup() {
let modalsOpen = ref(false);
return{ modalsOpen }; }};</script>
<style scoped>
.modals{
background-color: rgba(0.0.0.5);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modals div{
border-radius: 4px;
width: 300px;
height: 100px;
background-color:#fff;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
font-size:.9em;
color: rgba(0.0.0.9);
}
button{
padding: 5px;
}
</style>
Copy the code
The following information is displayed:
We found that the Teleport component is also relatively simple to use, with the to attribute being the element selector to send its contents to the specified element
Fragments and emits options
Vue3 components can have multiple roots
We know that in vue2. X, each component can have only one root, which means that we write each component template with a parent element. Vue3 has introduced Fragments to make it easier to write component templates, which means that components in VUe3 can have multiple roots
For example, by creating a fragments.vue, the component template can write multiple roots
<template>
<div>
<h1>Fragments</h1>
</div>
<div>
<p>Vue3 introduces Fragments, meaning Fragments</p>
<p>In other words, vuE3 components can have multiple roots</p>
</div><! -- You can have more roots --> </template>Copy the code
As above, the change from single roots to multiple roots is not a bit Fragmens, which is where the meaning of Fragmens comes from —-
Vue3 adds the emits option for custom events
Vue3 added the emits option. User-defined events sent by components must be defined in the emits option. One is to avoid situations where custom events fire multiple times when they have the same name as native events, such as the Click event. Second, it gives a better indication of how components work
For example, to create a Emits. Vue, we customize a click event
<template>
<button @click="$emit('click')">Am I</button>
</template>
<script>
export default {
//emits:['click']
};
</script>
Copy the code
If the custom event is not written to the emits option, the event will be fired multiple times when the custom event name conflicts with the native event name
<template>
<Emits @click="onClick"></Emits>
</template>
<script>
import Emits from './components/Emits.vue'
export default {
name: 'App'.components: {
Emits
},
methods: {onClick(){console.log('clicked!!!!! ')}}}</script>
Copy the code
Therefore, when vue3 writes custom events, it is recommended to write custom events in the emits option
The global API in Vue3 is instead called by the application instance
There are some disruptive changes in VUe3, such as the Global API being changed to application instance calls
There are many global apis in Vue2 that change the behavior of vue. Global apis can cause some problems:
Vue2
There is noapp
The concept of,new Vue()
The resulting root instance acts asapp
, so that all created root instancesapp
All share the same global configuration, which can contaminate other test cases during testing, making testing difficult- Global configuration also makes it impossible to create multiple different global configurations on a single page
app
The instance
So Vue3 uses the createApp function to return the application instance APP and expose a set of global apis from that app instance
For example, Vue.component in Vue2 is changed to the following format
import { createApp, h } from 'vue'
import App from './App.vue'
import './index.css'
const app = createApp(App)
.component('comp', { render: () = > h('div'.'i am comp!!! ') })
.mount('#app')
Copy the code
As you can see, the global component created in Vue2 through a constructor call to Component from Vue is now in the form of an app instance call
Similar changes (vue.component changed to App.component) include:
1. Vue.directive was changed to app.directive
2. Vue.mixin is changed to app.mixin
3. Vue.use is changed to app.use
4. Vue. Config is changed to app.config
5. Vue. Config. IgnoredElements change to app. Config. IgnoredElements
Note: vue.config. productionTip and vue.filter are removed from Vue3
On the Tree – shaking
We know that something called tree-shaking in Vue3 is not new. Some people call it “Tree shaking”.
Tree-shaking, in Judah’s own words, is “pruning” useless modules, so that unused apis don’t end up in the final package
Tree-shaking is not really Vue3 stuff, it’s the functionality of those packaging tools. Just a Vue3 code structure change. When a package tool like Webpack is used to package a project, webPack will not pack the unused code into the final project, which makes the project smaller
Principle: Rely on es6 modular syntax, dead-code will be removed!
Lifecycle function changes in Vue3
As we know, each component goes through a series of initialization procedures when it is created, such as setting up data listeners, compiling templates, mounting instances, and triggering DOM updates when data changes.
Vue2 provides lifecycle hook functions such as beforeCreate, created, beforeMount, Mounted, beforeUpdate, updated, beforeDestory, deStoryed, etc. Used to perform some of the actions we want to perform at different stages of the component
By Vue3, something changed
beforeCreate ---->setup
created ---->setup
beforeMount ---->onBeforeMount
mounted ---->onMounted
beforeUpdate ---->onBeforeUpdate
updated ---->onUpdated
beforeDestory ---->onBeforeUnmount
destoryed ---->onUnmounted
As you can see, the setup function replaces the beforeCreate and Created lifecycle functions, so we consider the setup execution time to be between beforeCreate and Created
For tree-shaking purposes, Vue3’s lifecycle functions are imported from VUE and called directly
// Introduce multiple lifecycle functions from vUE
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onBeforeUnmount, unMounted} from 'vue'
export default {
name: 'App'.setup() {
onBeforeMount(() = > {
// Execute before mounting
})
onMounted(() = > {
// Execute after mount
})
onBeforeUpdate(() = > {
// Execute before update
})
onUpdated(() = > {
// Execute after update
})
onBeforeUnmount(() = > {
// Execute before component destruction
})
unMounted(() = > {
// Execute after the component is destroyed
})
return{}}}Copy the code
END~
Above some personal study notes, not necessarily correct! Hope to learn Vue well next month
June, perfect end!! Then take ~