1

installation

Stand-alone installation

You can download the latest version directly from the official website of Vue.js and import it with the script tag

independent installation

CDN installation

Directly use script to introduce <script src="https://unpkg.com/vue@next"></script>

npm installation

npm version must be greater than 3.0

npm install vue@next

Command line tools:

The package name has changed from the previous version, from vue-cli to @vue/cli. If you have previously installed vue-cli1.x or vue-cli2.x globally. First need

Use the command npm uninstall vue-cli -g or yarn global remove vue-cli uninstall the previous version and install it

Note for Node version:

Vue CLI 4.x requires NodeJs version >=8.9

npm install -g @vue/cli

or

yarn global add @vue/cli

Note: vue-cli 3.x and vue-cli 2.x use the same vue command. If you have installed vue-cli 2.x before, it will be replaced with Vue-cli 3.x. Install @vue/cli-int:

npm i -g @vue/cli-init

Create project

Vue CLI

Use the command vue create project name to create the project

Then wait for the corresponding template and dependencies to be downloaded.

run :

cd project name

npm run serve

Vite

Vite is a web development and construction tool. Thanks to its native ES module import method, it can achieve lightning-like cold server startup.

You can use Vite to quickly build Vue projects by running the following commands in the terminal.

Install create-vite-app globally:

npm i -g create-vite-app

Create project:

npm init vite-app <项目名>

run:

cd project name

npm install

npm run dev

Vue3 directory structure

Command line tool @vue/cli

Directory analysis

Catalog fileDescription
publicPublic resource directory
srcHere is the directory we want to develop, basically everything we need to do is in this directory
.xxxx fileThese are some configuration files, including syntax configuration, git configuration, etc.
package.jsonProject configuration file
README.mdProject documentation, markdown format

Vue3- basic points

Get started

All the notes below are based on the @vue/cli method to create a project for explanation

Composition API

Why do you need Composition API

Composition API is the biggest feature of Vue3, and it can also be clearly seen that it is inspired by React Hooks
  • Solve the problem that the readability of the code becomes worse as the component becomes larger
  • Every way of code reuse has flaws
  • TS support is limited

setup

setup is a newly added option in Vue3.x, it is the entrance Composition API
Timing of setup execution

Based on a comparison VueJs life cycle, found setup earlier than beforeCreate execution .

<script>
import { defineComponent } from '@vue/composition-api'

export default defineComponent({
  beforeCreate() {
    console.log('beforeCreate')
  },
  created() {
    console.log('created')
  },
  setup() {
    console.log('setup')
  },
})
</script>
setup parameters

When using setup , it accepts two parameters:

  1. {Data} props
  2. {SetupContext} context

props accepted in setup is responsive and will be updated in time when new props are passed in. Because it is responsive, cannot use ES6 to deconstruct . Deconstruction will eliminate its responsiveness. Error code example:

<script>
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
  setup(props) {
    const { name } = props
    console.log('prop name', name)
  },
})
</script>

Getting a value from the props in root scope of setup() will cause the value to lose reactivity vue/no-setup-props-destructure

setup type
interface Data {
  [key: string]: unknown
}

interface SetupContext {
  attrs: Data
  slots: Slots
  emit: (event: string, ...args: unknown[]) => void
}

function setup(props: Data, context: SetupContext): Data

As can be seen from the two interfaces defined by ts above, the second parameter context setup function has three attributes, namely:

  • attrs: corresponding to the attribute $attr
  • slots: corresponding to the slot slot in vue2.x
  • emit: corresponds to the $emit sending event in vue2.x

The purpose of this design is that we cannot access this setup function. And these attributes are automatically synchronized with the latest value, so we get the latest value every time we use it.

Life cycle

Vue3生命周期
Change the source of the picture, other bloggers

The life cycle hook function can be registered by directly importing the onX

<script>
import { defineComponent, onMounted, onUpdated, onUnmounted } from 'vue'
export default defineComponent({
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
})
</script>

These lifecycle hook functions setup function, because they rely on the internal global state to locate the current active instance (the component instance of setup Calling them without the currently active instance will cause an error.

The component instance context is also set during the synchronous execution of the life cycle hook. When the component is uninstalled, the observation program watch and the calculated attribute computed created synchronously in the life cycle hook will also be deleted automatically.

  • Comparison of Options API and Composition API life cycle

    • beforeCreate -> use setup()
    • created -> use setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeUnmount -> onBeforeUnmount
    • unmounted -> onUnmounted
    • errorCaptured -> onErrorCaptured
    • renderTracked -> onRenderTracked
    • renderTriggered -> onRenderTriggered
    • activated -> onActivated
    • deactivated -> onDeactivated
    Options APIHook inside setup
    beforeCreateNot needed*
    createdNot needed*
    beforeMountonBeforeMount
    mountedonMounted
    beforeUpdateonBeforeUpdate
    updatedonUpdated
    beforeUnmountonBeforeUnmount
    unmountedonUnmounted
    errorCapturedonErrorCaptured
    renderTrackedonRenderTracked
    renderTriggeredonRenderTriggered
    activatedonActivated
    deactivatedonDeactivated

    It can be seen from the above comparison:

    1. beforeCreate and created are replaced setup
    2. The hook naming has been increased by on
    3. Added hook functions onRenderTriggered and onRenderTricked
    4. beforeDestroy name of 060aa35bfd7221 in beforeUnmount ; the destroyed table is unmounted

provide and inject

Provide and inject start dependency injection, both of which can only be called using the current component instance during setup
Types of
interface InjectionKey<T> extends Symbol {}

function provide<T>(key: InjectionKey<T> | string, value: T): void

// without default value
function inject<T>(key: InjectionKey<T> | string): T | undefined
// with default value
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
// with factory
function inject<T>(
  key: InjectionKey<T> | string,
  defaultValue: () => T,
  treatDefaultAsFactory: true
): T

reactive、ref、toRefs

In vue2.x, the definition of data is in the data function, but in vue3.x, you can use reactvie and ref to define the data
reactive

Return a responsive copy of the object.

const obj = reactive({ count: 0 })

Features: Responsive conversion is "deep". It will affect all nested attributes. Based on Proxy , the returned proxy object is not equal to the original object. It is recommended to use it only with the responsive proxy object to avoid relying on the original object.

Types of
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

The function reactive accepts an object as a parameter and returns a responsive object. Here, a generic constraint method is used to make the parameter types of the reactive

example:

<template>
  <div>
    <p>{{user.name}}</p>
    <p>{{user.hobby}}</p>
    <p v-for="item in user.sexs" :key="item.id">{{item.label}}</p>
  </div>
</template>
<script>
import { defineComponent, reactive } from 'vue'
export default defineComponent({
  setup() {
   const user = reactive({
     name: 'DarkCode',
     hobby: '篮球',
     sexs: [
       {
         id: 1,
         label: '男'
       },
       {
         id: 2,
         label: '女'
       }
     ]
   })

   return {
     user
   }
  }
})
</script>

combined with toRefs using deconstruction

As you can see in the above example, we use user.name , user.hobby etc. on the page to be more cumbersome. So can you user object and get its related attributes directly? This is not possible, because it will eliminate its responsiveness. But we can use toRefs . toRefs is used to convert a reactive object into a common object .

<template>
  <div>
    <p>{{name}}</p>
    <p>{{hobby}}</p>
    <p v-for="item in sexs" :key="item.id">{{item.label}}</p>
  </div>
</template>
<script>
import { defineComponent, reactive,toRefs } from 'vue'
export default defineComponent({
  setup() {
   const user = reactive({
     name: 'DarkCode',
     hobby: '篮球',
     sexs: [
       {
         id: 1,
         label: '男'
       },
       {
         id: 2,
         label: '女'
       }
     ]
   })

   return {
     ...toRefs(user)
   }
  }
})
</script>

Note: reactive will "unlock" all deep refs , while keeping ref as a responsive

<script>
import { defineComponent, reactive, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref(1)
    const obj = reactive({ count })

    // ref will be unwrapped
    console.log(obj.count === count.value) // true

    // it will update `obj.count`
    count.value++
    console.log(count.value) // 2
    console.log(obj.count) // 2

    // it will also update `count` ref
    obj.count++
    console.log(obj.count) // 3
    console.log(count.value) // 3
  }
})
</script>

When assigning ref to the response attribute, the ref will be automatically unpacked

<script>
import { defineComponent, reactive, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref(1)
    const obj = reactive({})

    // assigning a ref to a reactive property
    obj.count = count

    console.log(obj.count) // 1
    // ref will be automatically unwrapped.
    console.log(obj.count === count.value) // true
  }
})
</script>
reactive essence
  • It is a proxy , and the return value is a response object of proxy
  • The parameter of the function is the object type. For the basic data type, reactive
  • Ability to monitor responsive object properties in depth
<script>
import { defineComponent, reactive } from 'vue'
export default defineComponent({
  setup() {
    const obj = reactive({
      name: 'DarkCode',
      age: 22
    })
    console.log(obj)
  }
})
</script>
ref

Accepts an internal value and returns a responsive and variable ref object. ref object has a single attribute (.value) pointing to an internal value

Such as:

<script>
import { defineComponent, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref(0)
    console.log(count.value)

    count.value++
    console.log(count.value)
  }
})
</script>

或

<template>
  <div>
    {{count}}
  </div>
</template>
<script>
import { defineComponent, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref(0)
    console.log(count.value)

    setInterval(() => {
      count.value++
      console.log(count.value)
    },1000)
    
    return {
      count
    }
  }
})
</script>

If an object is assigned ref value, in response to the object will approach reactive imparting depth responsive

Types of
interface Ref<T> {
  value: T
}

function ref<T>(value: T): Ref<T>

A Ref generic interface is defined, a value variable is defined in the interface, and the type is determined by generics. The return value of ref is of type Ref

Such as:

<script>
import { defineComponent, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref({
      name: 'Darkcode',
      age: 22
    })
    console.log(count.value.age)

    count.value.age++
    console.log(count.value.age)
    return {
      count
    }
  }
})
</script>
ref summary
  • Method ref accepts one parameter, the type can be any type
  • The return value of the method is an interface (object) type
  • To access or modify the value, it needs to be implemented in the form of .value
<script>
import { defineComponent, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref({
      name: 'Darkcode',
      age: 22
    })
    console.log(count)
  }
})
</script>
toRefs

Able to convert a responsive object into a normal object, where each attribute of the result object points to the corresponding attribute of the original object ref

Such as:

<script>
import { defineComponent, reactive, toRefs } from 'vue'
export default defineComponent({
  setup() {
    const state = reactive({
      foo: 1,
      bar: 2
    })
    /*
    Type of stateAsRefs:

    {
      foo: Ref<number>,
      bar: Ref<number>
    }
    */
    const stateAsRefs = toRefs(state)
    
    // The ref and the original property is "linked"
    state.foo++
    console.log(stateAsRefs.foo.value)// 2

    stateAsRefs.foo.value++
    console.log(state.foo)// 3
  }
})
</script>

When returning a responsive object from a composite function, toRefs is very useful, so that when using components, the returned object can be deconstructed/expanded without losing the "responsive" .

<script>
import { defineComponent, reactive, toRefs } from 'vue'
function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  return toRefs(state)
}
export default defineComponent({
  setup() {
    // can destructure without losing reactivity
    const { foo, bar } = useFeatureX()
    return {
      foo,
      bar
    }
  }
})
</script>

toRefs ref references for attributes contained in the source object. ref reference for a specific attribute, use toRef

toRefs summary
  • Can convert a responsive object into a normal object
  • The attribute of the obtained object has an implicit value attribute
  • Each attribute of the obtained object can be regarded as one by one ref
  • toRefs is useful when deconstructing objects

readonly

can send an object (responsive or ordinary object) or a ref , and return the original read-only proxy proxy. The read-only proxy proxy is "very deep": any nested attributes accessed will also be read-only .

Such as:

<script>
import { defineComponent, reactive, readonly, watchEffect } from 'vue'
export default defineComponent({
  setup() {
    const original = reactive({
      count: 0
    })

    const copy = readonly(original)
    console.log(copy)
    watchEffect(() => {
      // works for reactivity tracking
      console.log(copy.count) // 0、1
    })

    // mutating original will trigger watchers relying on the copy
    original.count++

    // mutating the copy will fail and result in a warning
    copy.count++ //warning
  }
})
</script>

As with the reactive type, if any attribute uses ref , the attribute will be automatically unpacked when the attribute is accessed through the proxy

<script>
import { defineComponent, ref, readonly } from 'vue'
export default defineComponent({
  setup() {
    const raw = {
      count: ref(123)
    }

    const copy = readonly(raw)

    console.log(raw.count.value) // 123
    console.log(copy.count) // 123
  }
})
</script>

isProxy

Check whether the object is proxy proxy reactive response or readonly read-only mode.

isReactive

Check whether the object is a proxy proxy object reactive

Such as:

<script>
import { defineComponent, reactive, isReactive } from 'vue'
export default defineComponent({
  setup() {
    const user = reactive({
      name: 'DarkCode'
    })

    console.log(isReactive(user)) // true
  }
})
</script>

If you create a proxy readonly , it will also return true . But wrap another proxy proxy object reactive

import { reactive, isReactive, readonly } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'John'
    })
    // readonly proxy created from plain object
    const plain = readonly({
      name: 'Mary'
    })
    console.log(isReactive(plain)) // -> false

    // readonly proxy created from reactive proxy
    const stateCopy = readonly(state)
    console.log(isReactive(stateCopy)) // -> true
  }
}

isReadonly

Whether the object is detected by the readonly read only created proxy agent.

toRaw

Return the original object represented by reactive or readonly It is just an escape port that can be used for temporary reading without proxy access/tracking overhead, and it can also be used for writing without triggering changes. It is not recommended to keep a persistent reference to the original object, and be cautious when using it.

Such as:

<script>
import { defineComponent, reactive, toRaw } from 'vue'
export default defineComponent({
  setup() {
    const foo = {
      name: 'DarkCode'
    }
    const user = reactive(foo)

    console.log(toRaw(user) === foo) // true
  }
})
</script>

markRaw

Mark an object so that it will never be converted to a proxy, but return the object itself.

<script>
import { defineComponent, reactive, markRaw, isReactive } from 'vue'
export default defineComponent({
  setup() {
    const foo = markRaw({})
    console.log(isReactive(reactive(foo))) // false

    // also works when nested inside other reactive objects
    const bar = reactive({ foo })
    console.log(isReactive(bar.foo)) // false
  }
})
</script>

be careful:

markRaw and the following shallowXXX API allow us to selectively choose the default deep responsive/read-only conversion and embed original, non-proxy objects in the state diagram for the following reasons:

  • Some values should not be responsive, such as responsible third-party class instances or Vue component instance objects.
  • When rendering large lists with immutable data sources, skip proxy conversion can improve performance.

shallowReactive

Create a responsive agent that tracks the responsiveness of its own attributes, but does not perform deep responsive transformations of nested objects. Similar to our shallow copy.

Such as:

<script>
import { defineComponent, shallowReactive, isReactive } from 'vue'
export default defineComponent({
  setup() {
    const state = shallowReactive({
      foo: 1,
      nested: {
        bar: 2
      }
    })

    // mutating state's own properties is reactive
    state.foo++
    // ...but does not convert nested objects
    isReactive(state.nested) // false
    state.nested.bar++ // non-reactive
  }
})
</script>

shallowReadonly

Similar to the above shallowReactive , not much to say.

toRef

ref for the attribute on the source responsive object, and then you can pass ref to keep the responsive link with its source attribute.

Such as:

<script>
import { defineComponent, reactive } from 'vue'
export default defineComponent({
  setup() {
    const state = reactive({
      foo: 1,
      bar: 2
    })

    const fooRef = toRef(state, 'foo')

    fooRef.foo++
    console.log(state.foo) // 2

    state.foo++
    console.log(fooRef.foo) // 3
  }
})
</script>

toRef is useful when passing the attribute ref to the combination function.

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  }
}

customRef

Create a custom ref and explicitly control its dependency tracking and trigger an update. It needs a factory function, which receives track and trigger functions as parameters and returns objects get and set

Such as:

<template>
  <div>
    <input type="text" v-model="text" />
  </div>
</template>
<script>
import { defineComponent, customRef } from 'vue'
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 defineComponent({
  setup() {
    return {
      text: useDebouncedRef('hello')
    }
  }
})
</script>
Types of
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>

type CustomRefFactory<T> = (
  track: () => void,
  trigger: () => void
) => {
  get: () => T
  set: (value: T) => void
}

computed and watch

computed

Use getter function, and is getter returns a responsive immutable value returned ref object.

Such as:

<script>
import { defineComponent, computed, ref } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    const plusOne = computed(() => count.value + 1) // 2

    console.log(plusOne.value)

    plusOne.value++ //error
  },
})
</script>

In addition, it can also use get and set functions to create writable ref objects.

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0
Types of
// read-only
function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>>

// writable
function computed<T>(options: { get: () => T; set: (value: T) => void }): Ref<T>

watchEffect

Run a function immediately when tracking its dependencies responsively, and re-run the function when the dependencies change.

Such as:

<script>
import { defineComponent, watchEffect, ref } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)

    watchEffect(() => console.log(count.value)) // 0、1

    setTimeout(() => {
      count.value++
    }, 100)
  },
})
</script>
Types of
function watchEffect(
  effect: (onInvalidate: InvalidateCbRegistrator) => void,
  options?: WatchEffectOptions
): StopHandle

interface WatchEffectOptions {
  flush?: 'pre' | 'post' | 'sync' // default: 'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}

interface DebuggerEvent {
  effect: ReactiveEffect
  target: any
  type: OperationTypes
  key: string | symbol | undefined
}

type InvalidateCbRegistrator = (invalidate: () => void) => void

type StopHandle = () => void

watch

The watch function is used to listen to a specific data source and execute side effects in the callback function. The default is lazy, which means that the callback is executed only when the source data that is being listened to changes.

watch(source, callback, [options])

Parameter Description:

  • source: can support string, Object, Function, Array; used to specify the responsive variable to be listened to
  • callback: callback function executed
  • options: supports deep, immediate and flush options.

Next, I will separately introduce how these three parameters are used. If you don't understand the use of watch, please read on:

Monitor data defined by reactive

Such as:

<script>
import { defineComponent, reactive, watch } from 'vue'

export default defineComponent({
  setup() {
    const state = reactive({
      nickname: 'DarkCode',
      age: 22
    })

    setTimeout(() => {
      state.age++
    }, 1000)

    // 修改age的值时会触发watch的回调
    watch(
      () => state.age,
      (curAge, preAge) => {
        console.log('new value:', curAge, 'old value:', preAge)
      }
    )
  },
})
</script>
Monitor the data defined by ref
<script>
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(2021)

    setTimeout(() => {
      count.value++
    }, 1000)

    watch(count, (curCount, preCount) => {
      console.log('new Value and old Value are:', curCount, preCount) // 2022,2021
    })
  },
})
</script>
Monitor multiple data

grammar:

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

For example, to monitor the data defined by reactive and ref above:

<script>
import { defineComponent, reactive, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(2021)
    const state = reactive({
      nickname: 'DarkCode',
      age: 22
    })
    setTimeout(() => {
      count.value++
      state.age++
    }, 1000)

    watch([() => state.age, count], ([curAge, curCount], [preAge, preCount]) => {
      console.log("新值:", curAge, "老值:", preAge); console.log("新值:", curCount,"老值:", preCount)
    })
  },
})
</script>
Listen to complex nested objects

Such as:

<script>
import { defineComponent, reactive, watch } from 'vue'

export default defineComponent({
  setup() {
    const state = reactive({
      house: {
        id: 11,
        attrs: {
          area: 111.2,
          height: 12,
          label: '学区房',
          owner: 'DarkCode'
        }
      }
    })

    setTimeout(() => {
      state.house.id++
    }, 1000)

    watch(
      () => state.house,
      (newT,oldT) => {
        console.log('new value and oldT are:', newT, oldT)
      },
      { deep: true }
    )
  },
})
</script>

If the third parameter deep:true is not used, the data change cannot be monitored. We mentioned earlier that default 160aa35bfd80a5, watch is lazy , then under what circumstances is it not lazy, can the callback function be executed immediately? In fact, it is very simple to use, just set immediate: true third parameter.

stop stop listening

watch monitor we created in the component will automatically stop when the component is destroyed. If we want to stop a certain monitor before the component is destroyed, we can call watch() function, the operation is as follows:

<script>
import { defineComponent, reactive, watch } from 'vue'

export default defineComponent({
  setup() {
    const state = reactive({
      house: {
        id: 11,
        attrs: {
          area: 111.2,
          height: 12,
          label: '学区房',
          owner: 'DarkCode'
        }
      }
    })

    setTimeout(() => {
      state.house.id++
    }, 1000)

    setTimeout(() => {
      stopWatchHouse()
    }, 3000)

    const stopWatchHouse = watch(
      () => state.house,
      (newT,oldT) => {
        console.log('new value and oldT are:', newT, oldT)
      },
      { deep: true }
    )
  },
})
</script>

Compare watchEffect :

  1. watchEffect does not need to manually pass in dependencies
  2. watchEffect will be executed once to automatically collect dependencies
  3. watchEffect cannot get the value before the change, only the value after the change

Vue3-Advanced

Custom hooks

If there are friends who are familiar with React, they are more familiar with hooks. In Vue3.x, custom hooks are provided for code reuse. mixins that it has better performance and readability.

Such as: constructing a common hook in the usual development to process the data package request.

We will build two combinable hooks.

  • The first hook will be used to directly interact with the rest of the API
  • The second hook will depend on the first

/hooks/api.ts

import { ref } from 'vue'

export default function useApi(url: RequestInfo, options ?: RequestInit | undefined) {
  const response = ref()
  const request = async () => {
    const res = await fetch(url, options)
    const data = await res.json()
    response.value = data
  }
  return {
    response,
    request
  }
}

/hooks/products.ts

import useApi from './api'
import { ref } from 'vue'

export default async function useProducts() {
  const { response: products, request } = useApi(
    "https://ecomm-products.modus.workers.dev/"
  )
  const loaded = ref(false)
  if(loaded.value === false) {
    await request()
    loaded.value = true
  }
  return {
    products
  }
}

/test.vue

<template>
  <div>
    <h3>Customers</h3>
    <table id="customers" >
      <tr>
        <th>ID</th>
        <th>title</th>
        <th>category</th>
      </tr>
      <tr v-for="product in products" :key="product.id">
        <td>{{product.id}}</td>
        <td>{{product.title}}</td>
        <td>{{product.category}}</td>
      </tr>
    </table>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import useProducts from "../hooks/products";
export default defineComponent({
  async setup() {
    const { products } = await useProducts()
     return { products };
  },
});
</script>

Note that setup added to async . Need to set the parent component <Suspense> wrap the child component, such as parent.vue :

<template>
  <div>
    <Suspense>
      <template #default>
        <HelloWorld></HelloWorld>
      </template>
      <template #fallback>
        <h1>Loading...</h1>
      </template>
    </Suspense>
  </div>
</template>

Let's look at an example of obtaining user information:

/hooks/user.ts

import useApi from "./api";
import { ref } from "vue";
export interface Location {
  lat: number;
  lng: number;
}
export interface Address {
  street: string;
  suite: string;
  city: string;
  zipcode: number;
  geo: Location;
}
export interface User {
  id: string;
  name: string;
  username: string;
  email: string;
  address: Address;
}
export default async function useUserss() {
  const { response: users, request } = useApi<User[]>(
    "https://jsonplaceholder.typicode.com/users"
  );
  const loaded = ref(false);
  if (loaded.value === false) {
    await request();
    loaded.value = true;
  }
  return { users };
}

/component/User.vue

<script lang="ts">
import { defineComponent } from "vue";
import useUsers from "../hooks/users";
export default defineComponent({
  name: "Users",
  async setup() {
    const { users } = await useUsers();
    return { users };
  },
});
</script>

Teleport

Why need

<teleport /> allows an element to be moved from one place to another.

With this understanding, we'll look at why you need to use Teleport characteristics of it, look at a small example: In sub-assembly Header use into Dialog components, the actual development we often use to under similar circumstances Dialog , this time Dialog is rendered into a layer of sub-components, and it becomes difficult to z-index Dialog should be an independent component. The DOM mounted by the top-level component of Vue should be completely stripped from the dom structure; at the same time, the value data or props Simply put, hopes to continue using Dialog inside the component, and hopes that the rendered DOM structure is not nested in the component's DOM . At this time, Teleport is needed to play. We can wrap Dialog <Teleport> . At this time, a portal is established, which can transmit the content rendered by Dialog Next, let’s take a small example to see how Teleport is used.

use

We hope that the dom rendered by Dialog and the top-level component are sibling nodes. Define an element for mounting index.html

  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <div id="dialog"></div>
  </body>

Dialog.vue

<template>
  <teleport to="#dialog">
    <div class="dialog">
      <div class="dialog_wrapper">
        <div class="dialog_header" v-if="title">
          <slot name="header">
            <span>{{ title }}</span>
          </slot>
        </div>
      </div>
      <div class="dialog_content">
        <slot></slot>
      </div>
      <div class="dialog_footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </teleport>
</template>
<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  setup() {
    return {
      title: '登录'
    }
  },
})
</script>

前端扫地僧
2.5k 声望1.2k 粉丝