?快速掌握vue3新語法(一) - 初識(setup/reactive/ref)

鐵皮飯盒發表於2021-09-30

"組合API"是vue3擴充的全新語法, 前面基礎課中講解的語法叫"選項API". 2種API在vue3中都支援.

解決了什麼?

"組合API"可以更進一步拆分"選項API"中的JS邏輯. 可以把某一邏輯的"data/computed/watch/methods/宣告週期鉤子"單獨封裝到一個函式(也可單獨一個檔案)中. 一般給拆分後的函式命名"useXxx".
image.png

拆分實際需求

分析下圖的購物車模組, 我計劃把JS部分拆分成2部分(函式): 一個用來獲取購物車商品資料和總價, 一個用來計算獲取優惠劵並計算優惠後的總價. 分表是函式: "useGetCart"和"useCoupon".
1.gif

setup結構

拆機成2個函式你一定很疑惑, 老的"選項API"不是有"methods"欄位也可以封裝函式, 暫不解釋, 先看下"組合API"的程式碼格式, 一個新的欄位"setup", 他是"組合API"的標誌屬性, 是個函式, 其返回值可以被模板識別並渲染, 類似"data".

特別注意2個函式的返回值, 他們返回了資料(類似data)和函式(類似methods). 實際函式內部包含獨立的"watch/computed/生命週期鉤子", 想當於把1個vue元件的"data"和"methods"的內容給分組了, 這也就是為什麼叫"組合API".

<template>
  <article v-if="cart.length > 0">
    <ul>
      <li v-for="item in cart" :key="item.name">
        {{ item.name }} : {{ item.price }}元
        <input v-model="item.count" type="number" style="width: 48px" />
      </li>
    </ul>
    <h5 style="margin-left: 100px">原價:{{ totalPrice }}元</h5>
    <h5 style="margin-left: 100px; color: #f10">總價:{{ realTotalPrice }}元</h5>
    <button @click="createOrder">支付</button>
  </article>
</template>

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  name: 'Cart',

    setup(){
    // 購物車詳情和總價
      const [cart,totalPrice,createOrder] = useGetCart();
    
    // 優惠後的價格
    const realTotalPrice = useCoupon();
    
    // 返回模板識別的資料,和data定義的資料一樣
    return {cart,totalPrice,realTotalPrice,createOrder};
  }
});
</script>

"useXxx"函式內部實現

這個"use"作為函式名字首是一個命名習慣, 實際起名並沒有限制. 每一個函式中的"watch/computed/生命週期鉤子", 他們都以函式的形式出現.

import {computed} from 'vue';
/**
 * 獲取購物車詳情
 */
function useGetCart() {
  //  購物車詳情(reactive)
  const cart = reactive<{ name: string; count: number; price: number }[]>([]);

  // 模擬非同步請求
  setTimeout(() => {
    cart.push(
      { name: "蘋果", count: 10, price: 10 },
      { name: "香蕉", count: 20, price: 20 }
    );
  }, 1000);

  // 總價格(computed)
  const totalPrice = computed(() => {
    return cart.reduce((total, item) => item.count * item.price + total, 0);
  });

  return [cart, totalPrice] as const;
}

這出現了一個新的函式"ref", 他是用來"定義響應資料"的, 接下來我們就講"ref"是什麼.

注意

  1. "as const"表示斷言陣列型別為元祖, 如果大家忘記了ts部分的內容, 在學習後面知識之前, 可以溫習下ts.
  2. 這裡只有生命週期的鉤子名字前面多了"on"字首, 比如mounted => onMounted

    定義響應資料(reactive/ref)

    "響應資料"就是值變化可以驅動dom變化的資料, 我們之前在"data"中定義的資料就是響應資料. 但是在"setup"中如果我們要定義資料, 這裡並沒有"data"函式, 取而代之的是"reactive/ref"函式:

    reactive

    定義響應資料, 輸入只能是物件型別, 返回輸入物件的響應版本.

    <template>
     <h1>{{count}}</h1>
    </template>
    
    <script lang="ts">
    import { defineComponent } from "vue";
    export default defineComponent({
     setup(){
     return reactive({count:99});
      }
    });
    </script>

    image.png
    實際這個例子中可以不用"reactive", 結果一樣, 但是如果"count"資料被修改, 那麼介面就不會自動變化了,一直顯示"99".

    ref

    同樣是定義響應資料, 和"reactive"的區別是返回值響應資料的格式不同, ref返回的資料需要用".value"訪問.

    const n = ref(110);
    console.log(n);

    image.png
    可以看到返回值在value欄位中, 這麼做是因為js中對資料變化的監視只支援"引用資料型別", 對於string和number型別如果需要監視需要構造一個物件, 所以這裡ref內部就需要構造一個{value:110}的變數.

    reactive和ref的選擇

    重要: 如果要監視的資料是引用型資料(object)那麼就是用reactive, 如果是(number/boolean/string)等原始資料型別就用ref.

    封裝函式(useXxx)

    現在我們回頭看2個函式的實現.

    useGetCart

    返回購物車中商品資訊和總價格, 總價格使用了"計算屬性"函式(computed), 同時我們封裝了"生成訂單"函式, 因為其需要購物車商品資訊做引數 ,所以把2者做為一組.

    import {computed} from 'vue';
    /**
     * 獲取購物車詳情
     */
    function useGetCart() {
      //  購物車詳情(reactive)
      const cart = reactive<{ name: string; count: number; price: number }[]>([]);
    
      // 模擬非同步請求
      setTimeout(() => {
     cart.push(
       { name: "蘋果", count: 10, price: 10 },
       { name: "香蕉", count: 20, price: 20 }
     );
      }, 1000);
    
      // 總價格(computed)
      const totalPrice = computed(() => {
     return cart.reduce((total, item) => item.count * item.price + total, 0);
      });
      
      
      // 生成訂單(methods)
      function createOrder(){
       // 模擬生成訂單
     setTimeout(()=>{
         console.log(`成功購買${cart.length}件商品`);
     },1000)
      }
    
      return [cart, totalPrice,createOrder] as const;
    }

    useCoupon

    獲取優惠金額, 並返回計算優惠後金額. 用watch來監視"總價格", 當變化的時候重新計算"優惠後總價", 使用"onMounted"控制資料請求觸發時機(本例並無實際意義,此處僅為了展示"onMounted"用法).

    import {watch,onMounted} from 'vue';
    /**
     * 獲取優惠劵
     */
    function useCoupon(totalPrice: Ref<number>) {
      const realTotalPrice = ref(0);
      // 此處實際可以不用onMouted,
      // 僅僅為了演示用法
      onMounted(() => {
     // 模擬非同步請求
     setTimeout(() => {
       const coupon = 9;
       watch(
         totalPrice,
         (value) => {
           realTotalPrice.value = value - coupon;
         },
         { immediate: true }
       );
     }, 1000);
      });
    
      return realTotalPrice;
    }

    "watch"作為函式, 其第二個引數是個物件, 有欄位"immediate"表示初始化即執行回撥, "deep"表示深度監視資料(object).

完整原始碼

https://github.com/any86/vue3-start/blob/master/src/views/Setup.vue

微信群

感謝大家的閱讀, 如有疑問可以加我微信, 我拉你進入微信群(由於騰訊對微信群的100人限制, 超過100人後必須由群成員拉入)

未完待續

最新動態請關注我的語雀

www.yuque.com_russell-qqjgt_rfbdgd(iPhone X).png

相關文章