iT邦幫忙

2024 iThome 鐵人賽

DAY 8
1
Modern Web

Vue 3 初學者:用實作帶你看過核心概念系列 第 8

Vue 3 用實作帶你看過核心概念 - Day 8:計算屬性應用與實踐

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • Vue Mustache 複雜邏輯情境描述
  • Vue Mustache 優化 - 使用 methods 存放邏輯
  • 定時器範例展示方法重複執行問題
  • Vue Mustache 優化 - 使用計算屬性解決重複執行問題
  • 計算屬性可寫屬性
  • 計算屬性注意事項
  • 計算屬性的經典使用案例 - 篩選(filter)
  • 總結
  • 小試身手

Day 6 在使用 Vue 的Mustache語法時,可以用來放置表達式。不過,這邊建議避免在 Mustache 中撰寫過於複雜的邏輯。這樣的建議主要有兩個原因:

  1. 依賴管理困難:當表達式過於複雜時,容易導致代碼難以理解和維護,並且不容易找出對應的依賴項。
  2. 性能問題:模板內的表達式在每次模板重新渲染時都會被重新計算。如果這些表達式包含了複雜的邏輯運算,則會影響渲染性能。

Vue Mustache 複雜邏輯的影響

下面的例子是使用 Vue Mustache放置複雜邏輯的一個典型情境。表面上看起來簡單,實際上依賴關係不明確且難以維護。更關鍵的是,這段表達式會在每次模板重新渲染時重新執行,導致潛在的性能問題。

👉 Vue3 Options API Mastache 複雜邏輯實作連結

HTML:

<div id="app">
  <div class="input-group">
    <label for="isShow">是否公開</label>
    <input id="isShow" type="checkbox" v-model="productInfo.isShow">
  </div>
  <div>
    <h2>產品清單</h2>
    <div class="content">
      {{ productInfo.productList.length > 0 && productInfo.isShow? productInfo.productList : '產品尚未公開'  }}
    </div>
  </div>
</div>

Vue Mustache 優化 - 使用 methods 存放邏輯

延續上面的案例,一個常見的做法是將複雜的邏輯從模板中移至methods,以保持模板的清潔。

這樣確實讓模板看起來更簡潔,但仍然存在一個關鍵問題:模板每次更新時,仍會重複執行showProductMsg 函數

👉 Vue3 Options API Mastache 複雜邏輯(method 優化)實作連結

HTML:

  <div>
    <h2>產品清單</h2>
    <div class="content">
      {{ showProductMsg()  }}
    </div>
  </div>

javaScript:

  methods: {
    showProductMsg() {
      return this.productInfo.productList.length > 0 && this.productInfo.isShow
        ? this.productInfo.productList
        : "產品尚未公開";
    }
  }
  ...略

定時器範例展示重複執行問題

讓我們加上一個定時器來觀察這個問題。

👉 Vue3 Options API methods 複雜邏輯實作連結

HTML:

<div id="app">
  當前時間:{{ time }}
  <div class="input-group">
    <label for="isShow">是否使用</label>
    <input id="isShow" type="checkbox" v-model="productInfo.isShow">
  </div>
  <div>
    <h2>產品清單</h2>
    <div class="content">
      {{ showProductMsg()  }}
    </div>
  </div>
</div>

javaScript:

const rootComponent = {
  data() {
    return {
      time: Date.now(),
      productInfo: {
        isShow: false,
        productList: ["Apple", "Banana", "Orange"]
      }
    };
  },
  methods: {
    showProductMsg() {
      console.log("showProductMsg 被觸發");
      return this.productInfo.productList.length > 0 && this.productInfo.isShow
        ? this.productInfo.productList
        : "產品尚未公開";
    }
  },
  // 設定定時器(300ms 更新一次 time)
  mounted() {
    setInterval(() => (this.time = Date.now()), 300);
  }
};

如下圖所示,模板因為定時器不停變更time數據,每次time改變時,模板重新渲染,這導致每次渲染都會執行一次showProductMsg 函數。即使showProductMsg方法依賴的數據productInfo.isShowproductInfo.productList並未發生變化,它仍會在每次渲染時重新執行。

Vue method 模板中重複渲染

Vue Mustache 優化 - 使用計算屬性解決重複執行問題

為了解決這個問題,可以使用 Vue 計算屬性(computed),其取值方式透過getter的方式,且具有緩存資料的特性,當依賴項沒有改動的時候不會重新觸發。

⭐ 計算屬性會在初始化時觸發一次,並執行其getter 函數來計算初始結果。

將原本methods的函式,改成計算屬性在看一次可以發現,計算函式在time變動的時候不會觸發,唯有在點擊複選框的時候,因為其依賴值productInfo.isShow改變,才會觸發計算屬性的getter

基本上computedmethods方法執行並回傳一個表達式結果相同,但特別注意這邊是屬性,因此在模板使用的時候,不再是showProductMsg()而是使用showProductMsg取讀取計算屬性。

👉 Vue3 Options API computed 複雜邏輯(computed 優化)實作連結

javaScript:

  computed: {
    showProductMsg() {
      console.log("showProductMsg 被觸發");
      return this.productInfo.productList.length > 0 && this.productInfo.isShow
        ? this.productInfo.productList
        : "產品尚未公開";
    }
  }
  ...略

計算屬性可寫屬性

計算屬性默認是只可以讀,如果要寫入必須同時提供gettersetter函式才可以,可透過setter改動依賴值,並觸發getter重新計算值。

在使用setter 函數時,需特別注意不要更改計算屬性依賴項以外的數據,否則可能會導致數據流混亂,從而影響應用的響應式系統。

透過華氏跟攝氏轉換器案例帶大家了解,這個比較特別的用法:

👉 Vue3 Options API 華氏及攝氏轉換實作連結

HTML:

<div id="app">
  <div class="card">
    <h2>我是華氏輸入框</h2>
    <input type="text" v-model="C">
  </div>
  <div class="card">
    <h2>我是攝氏輸入框</h2>
    <input type="text" v-model="F">
  </div>
</div>

javaScript:

const rootComponent = {
  data() {
    return {
      F: 0,
    };
  },
  computed: {
    C: {
      // 取值的時候,根據攝氏轉換度數
      get() {
        console.log('get 觸發');
        return (this.F * 9) / 5 + 32;
      },
      // 當我針對計算屬性寫入的時候改變原始值並觸發getter
      set(value) {
        console.log('set 觸發', value);
        this.F = (value - 32) * 5 / 9;
      }
    }
  }
}

v-model 可以理解為,當你改變輸入框中的數字時,對應的數據會自動更新,並且畫面也會同步顯示這個變更的數字。這實現了數據和界面之間的雙向綁定。在後續章節中,會詳細介紹這個雙向綁定的概念。

如下圖所示,這邊華氏輸入框綁定C(華氏)計算屬性,綁定依賴項是當F(攝氏)。

當你在攝氏輸入框中輸入數字時,會改變攝氏的值,並觸發計算屬性的get() 函數,從而重新計算並顯示當前轉換後的華氏數值。

相反地,當你在華氏輸入框中輸入數字時,會觸發set() 函數,這會更新攝氏的值,並因此觸發get() 函數,重新計算華氏的數值,實現雙向同步。

華氏跟攝氏轉換器案例 - 可寫計算屬性圖解


計算屬性注意事項

  1. Getter 不應該有副作用:在使用計算屬性的 getter 時,應避免改變其他狀態及在其中進行帶有副作用的操作,例如:發送異步 API 請求或修改 DOM 元素。計算屬性應該專注於從原始數據中派生出處理後要呈現的結果。

👉 Vue Options API 違反 Getter 副作用案例實作連結

  1. 避免直接修改計算屬性值:計算屬性的值是根據綁定的數據來源動態計算的,因此不應直接修改計算屬性的返回值。如果需要改變計算屬性的結果,應該更新它所依賴的數據源。

👉 Vue3 Options API 違反 computed 修改回傳值案例實作連結

計算屬性的經典使用案例 - 篩選(filter)

當資料量較少時,通常會選擇將資料一次性回傳至前端,並透過前端程式進行檢索或分頁。此時,計算屬性可以作為實現關鍵字檢索功能的方案。

👉 Vue3 Options API computed filter 實作連結

HTML:

<div id="app">
  <div>
    搜尋框: <input type="text" v-model="filter">
  </div>
  <ul>
    <li v-for="item of getFilterProduct" :key="item.id">{{ `${item.name} / ${item.price} ` }}</li>
  </ul>
</div>

JavaScript:

const rootComponent = {
  data() {
    return {
      filter: "",
      productList: [
        {
          id: 1,
          name: "漢堡",
          price: 30
        },
        {
          id: 2,
          name: "甜甜圈",
          price: 45
        },
        {
          id: 3,
          name: "熱狗",
          price: 25
        }
      ],
      sum: 0
    };
  },
  computed: {
    // 取得檢索項目(若空白則全部回傳)
    getFilterProduct() {
      const filterResult = this.productList.filter((product) => {
        return product.name.match(this.filter);
      });
      return filterResult;
    }
  }
}

總結

  1. 計算屬性具備緩存特性:計算屬性基於原始數據進行計算,並且會緩存計算結果。這能有效避免模板中方法綁定所帶來的重複觸發問題,從而提升應用性能。
  2. 計算屬性預設為只讀屬性:計算屬性的getter 函數應保持純粹,不應產生副作用,例如:修改非依賴的數據或發送異步請求,以確保應用的響應式數據流正常運作。
  3. 計算屬性可定義寫入邏輯:如果需要計算屬性具備寫入能力,必須同時定義gettersetter函數。setter應僅修改其依賴的原始數據,避免引入額外的副作用,否則可能會導致數據流混亂。

小試身手

這邊讓大家練習看看幣值匯率轉換,讓大家也能試試看計算屬性的setter觸發方式。

可以按照今天學到的計算屬性試著完成一個幣值轉換器,透過計算屬性可寫的setter達成目的。
(僅需調整計算屬性 setter 的部分即可)

👉 Vue Options API 匯率轉換器(computed set 應用)未完成版本連結

👉 Vue Options API 匯率轉換器(computed set 應用)完成版本連結


上一篇
Vue 3 用實作帶你看過核心概念 - Day 7: 響應式基礎 - Options API
下一篇
Vue 3 用實作帶你看過核心概念 - Day 9:Class 與 Style 的樣式綁定
系列文
Vue 3 初學者:用實作帶你看過核心概念14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言