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 thebeforeCreatewithcreatedExcept for setup, everything else is preceded by aonSetup, 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 implementationsetupFunction has not been executed yetbeforeCreatedAnd so onsetupFunction, cannot be useddatamethodsVariables and methods of,setupIn thethisPoint to theundefined

  • The setup parameters:
    • { Data } props.
    • { attrs, emit, slots, expose } context
  1. Emit: this.$emit…. for vue2.x Method used to trigger the parent component

  2. Attrs: this.$attrs for vue2.x.. Is a property mounted by the component itself.

  3. Slots: this.$slots for vue2. X. Records slot information.

  4. 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 throughthis.$storeIn 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 throughthis.$routerFor 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…