preface

The purpose of this series is to record the problems and solutions encountered in the process of learning vue3 setup syntax sugar.

A, problem,

When I read this article by chance, I felt that useSWR was very nice. The last update to vue-SWR was two years ago, Emmmm.

Second, the implementation

The process is not explained, stepped on a lot of pits, but also because vuE3 is not familiar enough, briefly talk about the attention point.

1. Ref, reative, toRefs

Although ref was only used in the final implementation, I was confused about which one of ref, reative and toRefs should be used together in the implementation process, which made me confused. So let’s talk about the relationship and the difference between them.

  • ref

It is used to define a reactive variable. Value is used to obtain the value.

import { ref } from 'vue'

let count = ref(0)

let userInfo = ref({
  name:'Joe'.age:18
})
Copy the code

Take a look at the console print

The reactive object created by ref isRefImplobject

  • reative

Reative is usually used to define a responsive array or object. The value can be the same as a normal object or array.

import { reactive } from 'vue'

let userInfo = reactive({
  name: 'Joe'.age: 18,})Copy the code

Take a look at the console print

The responsive object created by reative isProxyobject

  • toRefs

Converts the reactive object created by Reative to a normal object with the reactive value created by REF

import { toRefs, reactive } from 'vue'

let userInfo = reactive({
  name: 'Joe'.age: 18,})let userInfoRefs = toRefs(userInfo)
Copy the code

Take a look at the console print

By modifying theuserInfoRefsThe corresponding property value of userInfo is also changed synchronously

userInfoRefs.age.value = 88
Copy the code

2. Realize the idea and start the body

  • Since the setup syntax sugar can only be used in templates at the top level, the setup syntax sugar is first used in theSetup the topDefines a reactive expression inlistInfo. Note:listInfoDo not point to any other data (including other reactive variables), otherwise it will disconnect from the template’s reactive response.
<script setup>
import { ref } from 'vue'

// Define the top-level variable
let listInfo = ref({
  loading: false.data: null.error: null,
})

</script>

<template>
  <p>loading:{{ listInfo.loading }}</p>
  <p>data:{{ listInfo.data }}</p>
  <p>error:{{ listInfo.error }}</p>
</template>
Copy the code

The following page is displayed

  • Analog acquisition data
<script setup>
import { ref, onMounted } from 'vue'
import { useRequest } from '@/hooks/request'

// Simulate the interface
let api = (params) = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('11111111111')},1000)})}// Get the data method
function fetchData() {
  let params = {}
  let res = useRequest(api, params)
}

// Get data after the page is mounted
onMounted(() = > {
   fetchData()
})

</script>
Copy the code
  • It’s going to happen nowuseRequest. Think about it,useRequestIs aSynchronized methodsHow do I asynchronously update data for top-level variables? In fact, as long asuseRequestReturn reactive data and point the top-level variable to the reactive number.
// src/hooks/request.js

import { ref } from 'vue'

export function useRequest(api, params) {
  let loading = ref(false)
  let error = ref()
  let data = ref()

  // loading-start
  loading.value = true
  // Get data
  api(params)
    / / success
    .then((res) = > {
      //set data
      data.value = res

      // loading-end
      loading.value = false
    })
    / / fail
    .catch((e) = > {
      // set error
      error.value = e
      console.error(e)

      // loading-end
      loading.value = false
    })

  return {
    loading,
    data,
    error,
  }
}

Copy the code

UseRequest is now implemented to return responsive data, and you just have the top-level variable point to that data.

<script setup>
import { ref, onMounted } from 'vue'
import { useRequest } from '@/hooks/request'

// Define the top-level variable
let listInfo = ref({
  loading: false.data: null.error: null,})// Get the data method
function fetchData() {
  let params = {}
  let res = useRequest(api, params)
  
  // Direct assignment (*)
  // listInfo = res 
  As mentioned earlier, pointing listInfo to any value will result in a responsive break
  
  // Since useRequest returns a plain object made up of reactive variables generated by ref
  // Just assign the res value to the corresponding userInfo attribute
    Object.assign(listInfo.value, res)
}

</script>
Copy the code

Third, optimize

Because the format of the value obtained by the interface is not always the same as the format rendered by the template, a format conversion function is required.

<script setup>
import { ref, onMounted } from 'vue'
import { useRequest } from '@/hooks/request'


// Get the data method
function fetchData() {

  // Data formatting, for example
  let fmt = (data) = > {
    return 'data' + data
  }
  let params = {}
  let res = useRequest(api, params, fmt)
}
</script>
Copy the code
// src/hooks/request.js

import { ref } from 'vue'

export function useRequest(api, params,fmt) {... api(params)/ / success
    .then((res) = > {
      //set data
      data.value = fmt ? fmt(res) : res
    })
...
}
Copy the code

Fourth, integrate

<script setup>
import { ref, onMounted } from 'vue'
import { useRequest } from '@/hooks/request'

// Define the top-level variable
let listInfo = ref({
  loading: false.data: null.error: null,})// Simulate the interface
let api = (params) = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('11111111111')},1000)})}// Get the data method
function fetchData() {
  // Data formatting, for example
  let fmt = (data) = > {
    return 'data' + data
  }
  let params = {}
  let { loading, data, error } = useRequest(api, params)
  Object.assign(listInfo.value,{ loading, data, error })
}

// Get data after the page is mounted
onMounted(() = > {
   fetchData()
})

</script>

<template>
  <p>loading:{{ listInfo.loading }}</p>
  <p>data:{{ listInfo.data }}</p>
  <p>error:{{ listInfo.error }}</p>
</template>
Copy the code
// src/hooks/request.js
import { ref } from 'vue'

export function useRequest(api, params, fmt) {
  let loading = ref(false)
  let error = ref()
  let data = ref()

  //loading-start
  loading.value = true
  // Get the previous data
  api(params)
    / / success
    .then((res) = > {
      //set data
      data.value = fmt ? fmt(res) : res

      //loading-end
      loading.value = false
    })
    / / fail
    .catch((e) = > {
      //set error
      error.value = e
      console.error(e)

      //loading-end
      loading.value = false
    })

  return {
    loading,
    data,
    error,
  }
}
Copy the code