Back To Articles

Vue.js Props - 由外到內的資料傳遞

🧑🏻‍💻 Huang, Yung-Hsiang 📅 November 12, 2019

Article Image

本篇文章介紹父子元件之間,由外到內的資料傳遞方式 Props。

靜態或動態傳遞

Props 是由外到內的資料傳遞,根據有沒有加上冒號,又可以分為「靜態傳遞」與「動態傳遞」。

靜態傳遞

在父層撰寫子元件的標籤,並且把資料(圖片 URL 網址)透過 Props 屬性(這裡取名為 img-url)傳給子元件,就能在元件內透過 props imgUrl 取得傳過來的資料。

<photo img-url="https://images.unsplash.com/photo"></photo>

動態傳遞

當外部資料會即時變動時,我們可以使用 v-bind 的方式動態綁定外層的資料,即 :img-url

例如:外層有一份資料名稱為 url,值先設定為 https://images.unsplash.com/photo,我們要把它傳給內層做使用,不過這個變數的值會隨著其他操作而改變。

<photo :img-url="url"></photo>

等價命名 (camelCase vs. kebab-case)

由於 HTML 的「特性名」是大小寫不敏感的,瀏覽器會自動把所有大寫字符解釋為小寫字符,所以在 HTML 裡面 camelCase(小駝峰命名)與 kebab-case(短橫線分隔命名)是等價的。

不過使用 Props 時,建議 HTML 的部分使用 kebab-case,而 JavaScript 裡面則使用 camelCase 來命名變數。

單向資料流

Vue 希望 Props 傳下去的資料盡量維持單向,而不要反向地寫回去。

如果直接更改 Props 從外層傳遞進來的值,會出現錯誤。最常發生的情況就是在元素上使用 v-model 綁定 Props 的資料。

要避免這個錯誤,我們通常會在子元件額外定義一個新的變數,用來接收 Props 傳進來的資料,避免直接綁定 Props,簡單來說就是多一層的概念,這個時候我們調整這個變數就不會有問題了。

補充說明:物件傳參考的特性

剛才我們提到,由於單向資料流的關係,我們會額外定義一個變數來使用 Props。
但是定義變數時,要注意到資料格式如果是物件、陣列等類型,要考慮到他們只是傳參考值的特性。

我們直接透過下面這個例子來理解,範例中我們將 b 的 user 改為小強,結果 a 的 user 也會變成小強。
這就是因為 a 物件在傳遞的時候,是把整個參考值傳遞到 b 上面,所以如果 b 被修改,那麼 a 也會被修改。

var a = { user: "小明" };
var b = a;
b.user = "小強";

console.log(a.user); // 小強

設定 Props 的型別與預設值

用 Props 傳遞外層資料到內層時,假設想要顯示數值資料,我們可以直接設定 Props 應該要是哪一種 type(資料型別),不過當然外層也要以純數值 (Number) 傳遞,不能寫成 String 等其他錯誤的型別。

此外,如果希望 Props 能帶有預設值,可以直接於 Props 裡設定 default 屬性。

props: {
  age: {
    type: Number,
    default: 0,
    required: true,
  }
}

使用 Props 碰到的疑難雜症

傳入數值卻變成字串

在文章最前面有提到,Props 依照冒號可以解讀成靜態傳入與動態傳入兩種。

如果將 Number 用靜態屬性傳給子元件,像是 money="1000",父元件這邊傳入的會是 String。
但如果加上冒號,像是 :money="1000",則這時候的數值才會是 Number。

搭配 v-if 處理時間差的繪製問題

有時候,我們的資料匯入會有一些時間差,導致元件使用變數時,因為變數還沒有傳進去,導致 Vue 出現抓不到值的錯誤訊息。

imgur

這時候我們可以使用 v-if 讓元件的產生時間往後移,讓資料完成時才同步繪製元件

例如:如果有電話號碼,才繪製出通訊錄卡片

<card :user-data="user" v-if="user.phone"></card>
var app = new Vue({
  el: "#app",
  data: {
    user: {},
    isShow: true,
  },
  created: function () {
    var vm = this;
    $.ajax({
      url: "https://randomuser.me/api/",
      dataType: "json",
      success: function (data) {
        vm.user = data.results[0];
      },
    });
  },
});

在這個例子中,我們想要透過 AJAX 抓取遠端資料,並且把資料存進變數 user 裡面,再將這些圖片顯示出來。

但是因為抓取資料、傳遞資料這些動作會有時間差,導致 Vue 會出現元件找不到變數的錯誤。
因此我們可以加上 v-if 的條件,這裡就是當 user 變數中的 phone 屬性還沒載入時,就先不要執行把卡片渲染到畫面上的動作。

加上 Keep Alive 維持狀態與生命週期

下面這個例子裡,<keep-card> 元件可以透過 Checkbox 的勾選來重新生成。
每一次勾選 Checkbox,都會重新執行 AJAX,這也會讓元件銷毀後再生成。

CodePen Demo (Before)

如果不希望每次元件生成時,都重新執行一次裡面的程式碼(像是 AJAX)的話,我們可以幫元件加上 <keep-alive> 標籤。
另外,我們把 v-if 從原本的 div 標籤,改到卡片元件上面,因為要確保資料載入後才繪製出資料內容,也就是剛才提到的時間差問題(AJAX 還沒執行完成,資料還沒有載入好)。

CodePen Demo (Keep Alive)

實際操作後,可以發現我們取消勾選之後,從 Vue DevTools 可以看到 <KeepCard> 會變成 inactive(暫時隱藏)的狀態。下次啟動時,就不會重新執行元件的整個生命週期,而是會直接把它呼叫出來囉!

imgur

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫