vue3中customRef自定义ref(系列十)
customRef
返回一个ref对象,可以显式地控制依赖追踪和触发响应
示例
<template> <div> <p>{{obj}}</p> <button @click="inc">button</button> </div> </template> <script> import { customRef } from 'vue'; // customRef用于 自定义ref // 自定义 ref 需要提供参数传值 function myRef(value) { // 自定义 ref 需要提供 customerRef 返回值 // customer 需要提供一个函数作为参数 // 该函数默认带参数 track 和 trigger ,都是方法。 return customRef((track, trigger) => { return { // customer 需要提供一个对象 作为返回值 // 该对象需要包含 get 和 set 方法。 get() { // track 方法放在 get 中,用于提示这个数据是需要追踪变化的 track(); console.log('get', value); return value; }, // set 传入一个值作为新值,通常用于取代 value set(newValue) { console.log('set', newValue); value = newValue; // 记得触发事件 trigger,告诉vue触发页面更新 trigger(); } } }) } export default { name: 'App', setup() {
// let obj = ref(18); // reactive({value: 18})
// 应用上面的自定义 ref ,使用方案和之前的 ref 是类似的。 const obj = myRef(123); function inc() { obj.value += 1; } return { obj, inc }; } } </script>
这并不是一个多么复杂的方法,如果要使用,记得是在自定义的 ref
中返回一个 customRef
,而 customRef
也要返回一个对象,相当于二重嵌套的返回。
假如我们去掉了 track
和 trigger
,那么将失去视图层追踪变化的能力(可以显示控制)。如果需要进行视图层追踪,请注意在 set
中 value
发生变化后即刻执行 trigger
。
#实例2
考虑一个通常情况下会出现的场景,我们需要发送请求,获取数据,而这个过程是异步的。
首先是数据,它放在 public 文件夹里。
// data.json [ { "name": "GuanYu", "id": 1 }, { "name": "ZhangFei", "id": 2 }, { "name": "MaChao", "id": 3 }, { "name": "ZhaoYun", "id": 4 }, { "name": "HuangZhong", "id": 5 } ]
如果我们并未使用 customRef
来执行自定义。fetch请求详情:阮一峰
<template> <ul> <li v-for="item in obj" :key="item.id"> {{item.id}} - {{item.name}} </li> </ul> </template> <script> import { ref } from 'vue'; export default { name: 'App', setup() { // 创建一个空数组 ref const obj = ref([]); // 使用 fetch 异步获取文件内容 fetch('../public/data.json') .then((res) => { return res.json(); }).then((data) => { console.log(data); obj.value = data; }).catch((err) => { console.log(err); }) return { obj, }; } } </script>
此时在setup函数中(setup函数: 只能是一个同步的函数, 不能是一个异步的函数,如async setup),有多个异步回调函数,不美观,是否可用同步效果呢,可以采用自定义ref实现
这是一个办法,但还有更加具有可复用性的方案。
<template> <ul> <li v-for="item in obj" :key="item.id"> {{item.id}} - {{item.name}} </li> <button @click="getNewObj">newObj</button> </ul> </template> <script> import { customRef } from 'vue'; function fetchRef(value) { return customRef((track, trigger) => { // 用于存储获得的数据 let ans; function getAns() { fetch(value) .then((res) => { return res.json(); }).then((data) => { console.log(data); // 将获得的数据存储起来 ans = data; // 提示触发视图层变化 trigger(); }).catch((err) => { console.log(err); }); } getAns(); return { get() { track(); //告诉vue这个数据需要追踪变化 return ans; }, set(newValue) { value = newValue; // 修改 value 的同时再次进行数据的抓取 getAns(); } } }) } export default { name: 'App', setup() { const obj = fetchRef('../public/data.json'); // 修改数据源 function getNewObj() { obj.value = '../public/data1.json'; } return { obj, getNewObj }; } } </script>
// 注意点:
// 不能在get方法中发送网络请求,会循环发送请求
// 渲染界面 -> 调用get -> 发送网络请求
// 保存数据 -> 更新界面 -> 调用get
在这个方案中,我将 获取数据的方案 封装存储在 自定义ref
中,在初始化的时候,调用get函数,发送请求,会在获取数据之后,在更新页面之前,执行 trigger
进行视图层的变化。
而在设置新值的时候,再次触发获取数据的方案,从而实现复杂的双向数据绑定。在setup函数就实现一个同步代码方式,美观
点击 button
,可以改变数据,并实现视图层的变化。
// data1.json [ { "name": "LiuBei", "id": 6 }, { "name": "CaoCao", "id": 7 }, { "name": "SunQuan", "id": 8 } ]