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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM