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
    }
]

 

posted @ 2021-03-05 14:19  全情海洋  阅读(3611)  评论(1编辑  收藏  举报