vue3 setup语法糖学习之路(一)-- useRequest
前言
本系列仅为记录个人学习vue3
的setup语法糖
过程中遇到的问题及解决方法和思路。
一、问题
在一个偶然的机会看到了这篇文章,感觉useSWR就很nice。结果在npm上找到vue-swr后,发现该插件最后更新是两年前,Emmmm,算了还是自己手动实现吧。
二、实现
过程就不详诉了,踩了很多坑,也是因为对vue3不够熟悉,简单讲下注意点吧。
1. ref、reative、toRefs
虽然最后实现只用到了ref
,但是在实现过程中一直搞不清用ref、reative、toRefs
的哪一个还是要混着用,晕头转向的。所以还是拎出来讲清楚他们之间的关系和区别。
ref
用来定义一个响应式变量
,取值时需要用.value
获取。
import { ref } from 'vue' let count = ref(0) let userInfo = ref({ name:'张三', age:18 }) 复制代码
看一下控制台打印的
通过ref创建的响应式对象为RefImpl
对象
reative
reative
通常用来定义一个响应式数组或对象,取值按照普通对象、数组的取值方式即可,
import { reactive } from 'vue' let userInfo = reactive({ name: '张三', age: 18, }) 复制代码
看一下控制台打印的
通过reative创建的响应式对象为Proxy
对象
toRefs
将reative
创建的响应式对象转换成一个属性值为ref
创建的响应式值的普通对象
import { toRefs, reactive } from 'vue' let userInfo = reactive({ name: '张三', age: 18, }) let userInfoRefs = toRefs(userInfo) 复制代码
看一下控制台打印的
通过修改userInfoRefs
的属性值也会同步修改userInfo的相应属性值
userInfoRefs.age.value = 88 复制代码
2. 实现思路,正文开始
由于setup语法糖只有在顶层的变量才能在模板中使用,因此先在
setup顶层
中定义一个响应式listInfo
。注意:listInfo
不可指向其他任何数据(包括其他的响应式变量),否则跟模板的响应式会断开。
<script setup> import { ref } from 'vue' // 定义顶层变量 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> 复制代码
此时页面展示如下
模拟获取数据
<script setup> import { ref, onMounted } from 'vue' import { useRequest } from '@/hooks/request' // 模拟接口 let api = (params) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('11111111111') }, 1000) }) } // 获取数据方法 function fetchData() { let params = {} let res = useRequest(api, params) } // 页面挂载后获取数据 onMounted(() => { fetchData() }) </script> 复制代码
现在就要实现
useRequest
。思考一下,useRequest
是个同步方法
,如何异步更新顶层变量的数据?其实只要useRequest
返回响应式数据,并且将顶层变量指向这个响应式数即可。
// 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 // 获取数据 api(params) // 成功 .then((res) => { //set data data.value = res // loading-end loading.value = false }) // 失败 .catch((e) => { // set error error.value = e console.error(e) // loading-end loading.value = false }) return { loading, data, error, } } 复制代码
此时实现了useRequest
返回响应式数据,只要让顶层变量指向这个数据就大公告成。
<script setup> import { ref, onMounted } from 'vue' import { useRequest } from '@/hooks/request' // 定义顶层变量 let listInfo = ref({ loading: false, data: null, error: null, }) // 获取数据方法 function fetchData() { let params = {} let res = useRequest(api, params) // 直接赋值(×) // listInfo = res // 前面提到过 如果将listInfo指向任意值都会导致响应式断开 // 由于useRequest返回的是由ref生成的响应式变量组成的普通对象 // 只要将res的值赋值给对应的userInfo的属性即可 Object.assign(listInfo.value, res) } </script> 复制代码
三、优化一下
因为平时接口获取到值的格式与模板渲染的格式不一定一致,所以需要一个转换格式的功能。
<script setup> import { ref, onMounted } from 'vue' import { useRequest } from '@/hooks/request' // 获取数据方法 function fetchData() { //数据格式化、举个栗子 let fmt = (data) => { return 'data' + data } let params = {} let res = useRequest(api, params, fmt) } </script> 复制代码
// src/hooks/request.js import { ref } from 'vue' export function useRequest(api, params,fmt) { ... api(params) // 成功 .then((res) => { //set data data.value = fmt ? fmt(res) : res }) ... } 复制代码
四、整合一下
<script setup> import { ref, onMounted } from 'vue' import { useRequest } from '@/hooks/request' // 定义顶层变量 let listInfo = ref({ loading: false, data: null, error: null, }) // 模拟接口 let api = (params) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('11111111111') }, 1000) }) } // 获取数据方法 function fetchData() { //数据格式化、举个栗子 let fmt = (data) => { return 'data' + data } let params = {} let { loading, data, error } = useRequest(api, params) Object.assign(listInfo.value,{ loading, data, error }) } // 页面挂载后获取数据 onMounted(() => { fetchData() }) </script> <template> <p>loading:{{ listInfo.loading }}</p> <p>data:{{ listInfo.data }}</p> <p>error:{{ listInfo.error }}</p> </template> 复制代码
// 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 //获取前数据 api(params) //成功 .then((res) => { //set data data.value = fmt ? fmt(res) : res //loading-end loading.value = false }) //失败 .catch((e) => { //set error error.value = e console.error(e) //loading-end loading.value = false }) return { loading, data, error, } }
作者:Xyuan_
链接:https://juejin.cn/post/7021451205641502734