Summarize and learn Vue3. X, write not timely, you can see, while collecting, weekend will supplement the rest.
If you find any problems or feel that there is something commonly used but not written, please leave a comment and discuss. I will correct and add in time
About Composition Api
Vue3. X brings a new feature — Composition API (composite API), understanding is to achieve function aggregation logic, logic reuse, and generated.
Review the Option Api
Defects in the Option Api
- With the increasing complexity of the business, the amount of code will continue to increase;
- Because the code of the related business needs to follow the configuration of option to write to a specific area;
- Without very good constraints, subsequent maintenance can be very complex and code reusability is not high.
Composition Api
Composition Api makes code for related functions more organized together, aggregation logic!
According to the official website classification includes:
- setup
- Lifecycle hook: in addition to the
beforeCreate
withcreated
Except for setup, everything else is preceded by aon
Setup, for example:onMounted
- Provide / Inject
- getCurrentInstance
Vue3. X added composition API, but also backwards compatible with the Option API, can be mixed, but from a conceptual point of view, it is more recommended to write our components in a setup way.
The reason is as follows: the existence of Vue3 itself is to solve the problem of Vue2, the problem of Vue2 is that the lack of convergence will lead to more and more bloated code! Setup allows data, method logic, and dependencies to be aggregated together for easier maintenance.
setup
- In the implementation
setup
Function has not been executed yetbeforeCreated
And so onsetup
Function, cannot be useddata
和methods
Variables and methods of,setup
In thethis
Point to theundefined
- The setup parameters:
{ Data } props
.{ attrs, emit, slots, expose } context
-
Emit: this.$emit…. for vue2.x Method used to trigger the parent component
-
Attrs: this.$attrs for vue2.x.. Is a property mounted by the component itself.
-
Slots: this.$slots for vue2. X. Records slot information.
-
Expose: similar to the return{} in Setup. After using the template render function, use expose to expose the property if the return is occupied.
To type the arguments passed to setup(), you need to use defineComponent.
export default defineComponent ({
// In setup, you need to declare props
props: {
title: String
},
setup(props, { attrs, emit, slots, expose }) {
// Attribute (non-responsive object, equivalent to $attrs)
console.log(attrs)
// slots (non-responsive object, equivalent to $slots)
console.log(slots)
// Trigger event (method, equivalent to $emit)
console.log(emit)
// Expose public property (function)
console.log(expose)
const reset = () = > {
// Some logic
}
expose({
reset
})
console.log(props.title)
return{}}})Copy the code
Logical aggregation, separation of concerns (consensus)
Should have two meanings:
The first meaning, Vue3 setup itself puts the relevant data and processing logic together. This is a kind of aggregation of concerns, which makes it easier to look at the business code.
The second level of separation of concerns is that as setup becomes larger, we can extract relevant pieces of business from within setup.
import { defineComponent, ref, computed } from 'vue'
import useMerchantList from './merchant.js'
export default defineComponent({
name: 'Gift'.setup() {
const counter = ref(0)
const onClick = () = > {
router.push({ name: "AddGift"})}// In this example, the related business of merchantList is separated. Merchant.ts below
const { merchantList } = useMerchantList()
return {
counter,
onClick,
merchantList
}
}
})
Copy the code
UseMerchant. Ts (hooks)
import { getMerchantlist } from "@/api/gift"
import { ref, onMounted } from "vue"
export default function useMerchantList() {
const merchantList = ref([])
const fetchMerchantList = async() = > {let res = awaitgetMerchantlist({}) merchantList.value = res? .data? .child } onMounted(fetchMerchantList)return {
merchantList
}
}
Copy the code
This approach avoids the problem of stacking all the functionality logic in setup and allows you to write separate functionality as separate functions
<script setup>
Script setup has been released on vue3.2
// Implicit setup script
<script setup>
import {reactive} from "vue";
const states = reactive({
name: 'xiaoming'.age: 18.fun(){
console.log('xxxxx~')}})// Can also be defined separately
const { name,age,fun } = states
</script>
// Advantages: Less template code, simplicity
// Disadvantage: all defined variables are exposed (is that necessary?)
Copy the code
What is the script setup experience of Vue3 now? The video is a good introduction, but update a little bit, Vue3.2
If you know of any other pros and cons, or why you want to add this
However, we should avoid mixing the two in one document:
// bad
<script setup>
</script>
// Explicit setup script
<script>
export default {
setup(){
return{
// The object exposed here cannot be read by temp}}}</script>
Copy the code
Periodic function
Except BeforeDestroy becomes onBeforeUnmount; Destroyed should also be added on in addition to onUnmounted
Use async/await in lifecycle hooks
setup() {
const users = ref([])
onBeforeMount(async() = > {const res = await loadData()
users.value = res.data
console.log(res)
})
return {
users,
}
},
Copy the code
With async/await,
setup() {
async function handleSubmit() {
try {
// this parse may fail
const values = JSON.parse(await validate())
setModalProps({ confirmLoading: true })
// TODO custom api
console.log(values)
} catch (err) {
console.log(err)
}
}
return {
handleSubmit
}
}
Copy the code
Using async/await, you can also use Suspense to wrap your components with caution/avoid using them
<template>
<suspense>
<router-view></router-view>
</suspense>
</template>
export default {
async setup() {
// Use 'await' inside 'setup' with care
// Because most combinatorial API functions will only be executed before the first 'await'
const data = await loadData()
// It is wrapped implicitly in a Promise
// Because the function is async
return {
// ...}}}Copy the code
About the Reactivity API
reactive && ref
Ref is used to define basic data types as reactive data, which is essentially implemented by redefining attributes based on Object.defineProperty()
Reactive is used to define reference types as reactive data, essentially implementing object proxies based on proxies
<template>
<p> {{ name }} </p>
</template>
<script >
import { reactive, toRefs } from "vue";
export default {
setup(){
const obj = reactive({
name: 'xiaoming'.gender: 'male'.age: 18
})
return{
// Direct deconstruction is equivalent to... obj ==> name:obj.name. toRefs(obj) } } } </script >Copy the code
If you deconstruct it directly, name is a string, so it’s not responsive
Using toRefs you can see on the website that name is a reactive string. You can deconstruct it without losing responsiveness
ToRefs will turn our reactive object into a normal object, and then turn every property in the normal object into a reactive data
Extended idea
In this case, we can define variables together, or even methods, that deal with the same piece of business logic
<script >
import { reactive, toRefs } from "vue";
export default {
setup(props){
// Process the same business logic
const state = reactive({
isMoving: false.distance: ' '.startTime: 0.recordList: [].wrapper: {},
// Even
xxCallBack(){
// do ...}})// You can do that, depending on whether you want to
const handerFuns = reactive({
handerEdit(val){
// Can remain responsive
state.isMoving = val
},
handerCancel(res){
// ...
},
handerCreate(data){
// This is the object
this.handerCancel(data)
}
})
return{
...toRefs(state),
// Avoid a bunch of variables here. toRefs(handerFuns) } } } </script>Copy the code
readonly
Readonly A read-only proxy that accepts an object (reactive or pure) or ref and returns the original object. A read-only proxy is deep: any nested property accessed is also read-only.
<script >
import { readonly, reactive } from "vue";
export default {
setup(){
const original = reactive({ count: 0 })
const copy = readonly(original)
// Changing copies will fail and cause a warning
copy.count++ / / warning!
return{ }
}
}
</script >
Copy the code
Computed & watch
Computed
Computed in Vue 3.x also supports getters and setters
import { reactive, ref, toRefs, computed } from 'vue'
export default {
setup () {
const state = reactive({
count: 0.double: computed(() = > {
return state.count * 2})})const num = ref(0)
const addCount = function () {
state.count++
}
const addNum = function () {
num.value++
}
// only getter
const totalCount = computed(() = > state.count + num.value)
// getter & setter
const doubleCount = computed({
get () {
return state.count * 2
},
set (newVal) {
state.count = newVal / 2}})return {
...toRefs(state),
num,
totalCount,
doubleCount,
addCount,
addNum
}
}
}
Copy the code
watch watchEffect
watch(source, callback, [options])
3. X supports immediate and deep options, as does 2.x’s watch, but does not support obj.key1.key2. 3. Watch in X supports listening on either a single attribute or multiple attributes
By default, watch is lazy, so when is it not lazy to execute the callback immediately? Set the third parameter to immediate: true to be invoked immediately after registration
import { reactive, ref, toRefs, computed, watch } from 'vue'
export default {
setup () {
const state = reactive({
count: 0.double: computed(() = > {
return state.count * 2
}),
midObj: {
innerObj: {
size: 0}}})const num = ref(0)
const addCount = function () {
state.count++
}
const addNum = function () {
num.value++
}
// Listen for a single attribute
watch(() = > totalCount.value, (newVal, oldVal) = > {
console.log(`count + num = ${newVal}`)})// Listen for a single attribute, immediate
watch(() = > totalCount.value, (newVal, oldVal) = > {
console.log(`count + num = ${newVal}, immediate=true`)}, {immediate: true
})
// Listen for a single attribute, deep
watch(() = > state.midObj, (newVal, oldVal) = > {
console.log(`state.midObj = The ${JSON.stringify(newVal)}, deep=true`)}, {deep: true
})
setTimeout(() = > {
state.midObj.innerObj.size = 1
}, 2000)
// Listen for multiple attributes
watch([num, () = > totalCount.value], ([numVal, totalVal], [oldNumVal, OldTotalVal]) = > {
console.log(`num is ${numVal}, count + num = ${totalVal}`)})return {
...toRefs(state),
num,
totalCount,
addCount,
addNum
}
}
}
Copy the code
WatchEffect listens for functions
import { defineComponent, ref, reactive, toRefs, watchEffect } from "vue"
export default defineComponent({
setup() {
const state = reactive({ nickname: "xiaoming".age: 20 })
let year = ref(0)
setInterval(() = >{
state.age++
year.value++
},1000)
watchEffect(() = > {
console.log(state)
console.log(year)
})
return {
...toRefs(state)
}
},
})
Copy the code
The state and year values are printed first. Then, every second, print the state and year values. As you can see from the code above, unlike Watch, dependencies are not passed in first. WatchEffect collects them automatically by specifying a callback function. At component initialization, dependencies are collected once, and then callbacks are executed again when the data in the collected dependencies changes.
So the comparison is as follows:
- WatchEffect does not require manually passing in dependencies
- WatchEffect is executed once to automatically collect dependencies
- WatchEffect does not get the pre-change value, only the post-change value
In response to loss
There are some operations that cause us to lose the responsiveness of the object
Deconstruction props
Data transfer between components
Parent component to child component
Emits options
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 a component that passes native events to its parent, this causes two events to be fired:
<template> <button v-on:click="$emit('click', $event)">OK</button> </template> <script> export default { emits: [] // do not declare events} </script>Copy the code
When a parent component has a listener for the click event:
<my-button v-on:click="handleClick"></my-button>
Copy the code
The event will now be triggered twice:
- At a time from
$emit()
. - The other comes from the native event listener applied to the root element.
However, we defined props in contrast to emit and should avoid the following definition of props
// This is only acceptable when prototyping
props: ['status']
Copy the code
The child component receives props
<script>
import { defineComponent } from "vue";
export default defineComponent({
props: {name:String,},setup(props){
console.log(props);
}
})
</script>
/ * * * or * * * /
<script setup>
import { defineProps } from "vue";
let props=defineProps({
name:String
})
</script>
/ * * * or * * * /
<script>
import { getCurrentInstance } from "vue";// This API works in all of the following methods
let instance=getCurrentInstance();
console.log(instance.props);
</script>
Copy the code
The child component calls emit
<script>
import { defineComponent } from "vue";
export default defineComponent({
setup(props,ctx){
ctx.emit('myClick'.'This is the value passed to the parent.');
}
})
</script>
Copy the code
The parent component calls the child component method
/ / child component
const myChild = (val) = > {
console.log(val, '* * *')
}
expose({
myChild,
})
Copy the code
<test ref="childs"></test>
const childs = ref()
onMounted(() = > {
Ref () is an asynchronous method
childs.value.myChild(111)})Copy the code
Provide / Inject
By default, provide/ Inject binding is not reactive.
We can change this behavior by passing a Ref property or Reactive object (or using Computed) to provide.
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
// Add responsiveness between provide and Inject values
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90.latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
</script><! -- Use inject --><script>
import { inject } from 'vue'
export default {
setup() {
Inject The default value of the second parameter, optional
const userLocation = inject('location'.'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
Copy the code
Provide/Inject handle reactive type
Vuex && Router
Vuex 3.x supports Vue 2, while Vuex 4.x supports Vue 3
Vuex is a part of the Vue family bucket, which is not necessary. The Vue Core Team also produced Pinia, which can replace Vuex.
Rudex could also be replaced with a lighter mobx-React.
- In Vue2, you can actually go straight through
this.$store
In Vue3, this is used:
import { useStore } from 'vuex'
import { defineComponent, computed } from 'vue'
export default defineComponent({
name: 'Gift'.setup() {
const store = useStore()
const storeData = computed(() = > store) // With computed, get the value of store.
return {
storeData,
}
},
})
Copy the code
- In Vue2, it is through
this.$router
For functional programming of routing, Vue3:
import { useRouter } from "vue-router"
import { defineComponent, computed } from 'vue'
export default defineComponent({
name: 'Gift'.setup() {
const router = useRouter()
const onClick = () = > {
router.push({ name: "AddGift"})}return {
onClick
}
}
})
Copy the code
CSS variable injection
<template>
<span>Jerry</span>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({
color: 'red'
})
</script>
<style scoped>
span{// Use v-bind to bind variables in statecolor: v-bind('state.color');
}
</style>
Copy the code
Other
nextTick
import { createApp, nextTick } from 'vue'
const app = createApp({
setup() {
const message = ref('Hello! ')
const changeMessage = async newMessage => {
message.value = newMessage
await nextTick()
console.log('Now DOM is updated')}}})Copy the code
Volar
Volar is a VS Code plugin that I personally think is most useful for solving the TEMPLATE TS prompt problem.
Note that when using it, remove the Vetur first to avoid collisions.
mitt
If mitt is needed, please refer to Github for details
import mitt from 'mitt'
const emitter = mitt()
// listen to an event
emitter.on('foo'.e= > console.log('foo', e) )
// listen to all events
emitter.on(The '*'.(type, e) = > console.log(type, e) )
// fire an event
emitter.emit('foo', { a: 'b' })
// clearing all events
emitter.all.clear()
// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo) // listen
emitter.off('foo', onFoo) // unlisten
Copy the code
TS
JSX
High order
Teleport
The Vue defineComponent
Fragments
Refer to the following linked content snippets in part, thanks to 🙏
- Gitee.com/jishupang/v…
- Github1s.com/anncwb/vue-…
- Mp.weixin.qq.com/s/rJM9OaDI5…
- Juejin. Cn/post / 689054…