Vue3 Composition API Tips (1)

Problems with Ref and Reactive usage scenarios

Ref
import { ref } from 'vue'
let foo = 0
let bar = ref(0)
foo = 1
bar = 1 // ts-error
Copy the code

Features:

  • Display calls, so it is obvious which values are processed by ref for reactive updates; Also, in the demo above, TS does type checking and reports an error,bar.value = 1This is the correct posture. Isn’t it a bit like the.current from React?
  • It has fewer limitations than Reactive, which we’ll talk about reactive next
  • Value is used to get the value of the reactive update
Reactive
import { reactive } from 'vue'
const foo = { prop: 0 }
const bar = reactive({ prop: 0})
foo.prop = 1
bar.prop = 1
Copy the code

The characteristics of

  • Automatic unRef (.value not required)
  • It is no different in type from ordinary objects
  • Using ES6 object deconstruction causes the responsiveness to be lost
  • Need to use the arrow function wrapper to usewatch? AnthonyFu has not expanded on this point, not quite understand what it means

In summary, use ref whenever possible.

Ref Automatically unpacks
  • Watch accepts Ref directly as a listener and returns the unpacked value in the callback function

    const counter = ref(0)
    watch(counter, count= > {
        console.log(count) // same as count.value
    })
    Copy the code
  • Ref unpacks automatically in the template

    <template>
      <button @click="counter ++">
          count is {{counter}}
      <button>
    <template>
    Copy the code
  • Unpack nested Refs using Reactive

    import { reactive, ref } from 'vue'
    const foo = ref('bar')
    const data = reactive({ foo, id: 10 })
    data.foo // 'bar'
    Copy the code
unref

Unref is the inverse of ref

  • If a ref is passed, return its value (.value)
  • Otherwise, return the original value
Accept Ref as a function argument

Pure functions:

function add(a: number, b: number) {
    return a + b
}

const a = 1
const b = 2

const c = add(a, b) / / 3
Copy the code

Take Ref as an argument and return a response:

function add(a: Ref<number>, b: Ref: <number>) {
    return computed(() = > a.value + b.value)
}
const a = ref(1)
const b = ref(2)
const c = add(a, b)
c.value // 3, which implements that c.value is a reactive value
Copy the code
MayBeRef

type MayBeRef<T> = Ref<T> | T

MayBeRef supports reactive parameters

export function useTimeAgo (
    time: Date | number | string | Ref<Date | number | string>
) {
    return computed(() = > someFormating(unref(time)))
}
Copy the code
import { computed, unref, ref } from 'vue'
export function useTimeAgo (
    time: MayBeRef<Date | number | string>
) {
    return computed(() = > someFormating(unref(time)))
}
Copy the code

MayBeRef enables compatibility with reactive parameters

useTitle
import { useTitle } from '@vueuse/core'
const title = useTitle() // useTitle builds a new ref inside
title.value = 'hello wolrd' // document.title = 'hello world'
Copy the code
import { ref, computed } from 'vue'
import { useTitle } from '@vueuse/core'
const name = ref('hello')
const title = computed(() = > {
    return `${name.value}, world`
})
useTitle(title) // Pass a ref, document.title = 'hello world'
name.value = 'hi' // document.title = 'hi world'
Copy the code

The realization of the useTitle

import { ref, watch } from 'vue'
import { MayBeRef } from '@vueuse/core'
export function useTitle (
	newTitle: MayBeRef<string, null.undefined>
) {
  const title = ref(newTitle || document.title) // If newTitle is passed, the ref will not be reref
  watch(title, t= > {
    if(t ! =null) {
      document.title = t
    }
  }, { immediate: true })
  return title
}
Copy the code

An object composed of refs

import { ref, reactive } from 'vue'
function useMouse () {
    return {
        x: ref(0),
        y: ref(0)}}const { x } = useMouse()
const mouse = reactive(useMouse())

mouse.x === x.value
Copy the code

Converting asynchronous operations to “synchronous”

asynchronous

const data = await fetch('https: //api.github.com/').then(r= > r.json())
Copy the code

Modular API

const { data } = useFetch('https: //api.github.com').json()
const user_url = computed(() = > data.value.user_url)
Copy the code
useFetch
export function useFetch<R> (url: MaybeRef<string>) {
	const data = shallowRef<T | undefined> ()const error = shallowRef<error | undefined>()
	fetch(unref(url))
            .then(r= > r.json())
            .then(r= > data.value = r)
            .catch(e= > error.value = error)
	return {
            data,
            error
	}
} // The implementation in Vueuse is actually much more complex than this
Copy the code
Side effect removal

useEventListener

import { onUnmounted } from 'vue'
export function useEventListener(target: EventTarget, name: string, fn: any) {
	target.addEventListener(name, fn)
	onUnmounted(() = > {
            target.removeEventListener(name, fn)
	})
}
Copy the code
Provide/Inject type security

Use the InjectionKey

type tool provided by VUE to share types in different contexts

// context.js
import { InjectionKey } from 'vue'
export interface useInfo {
    id: number
    name: string
}
export const injectKeyUser: InjectionKey<useInfo> = Symbol(a)Copy the code
// parent.vue
import { provide } from 'vue'
import { injectKeyUser } from './context.js'
export default {
	setup () {
            provide(injectKeyUser, {
                    name: 'xxx'.id: '7' // Type error, must be number}}})Copy the code
// children.vue
import { inject } from 'vue'
import { InjectionKey } from './context.js'
export default {
    setup () {
        const user = inject(InjectionKey)
        if (user) {
          //xxxx}}}Copy the code
State sharing

Because of the natural flexibility of composite apis, states can be created and used independently of components, which is why VUe3 can be used without vuex at all

// share.ts
import { reactive } from 'vue'
export const state = reactive({
    foo: 1.bar: 'hello'
})
Copy the code
//A.vue
import { state } from './share.ts'
state.foo ++
Copy the code
// B.vue
import { state } from './share.ts'
console.log(state.foo) / / 2
Copy the code
useVModel

A tool to make using props and EMIT easier

export function useVModel (props, name) {
    const emit = getCurrentInstance().emit
    return computed(() = > {
        get () {
            return props[name]
        },
        set (v) {
            emit(`update: ${name}`, v)
        }
    })
}	
Copy the code
export default defineComponent({
	setup(){
            const value = useVModel(props, 'value')
            return { value }
	}
})
Copy the code
<template>
    <input v-model="value">
</template>
Copy the code

The above is shared by AnthonyFu on vueConf2021