本篇文章介紹父子元件之間,由外到內的資料傳遞方式 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 出現抓不到值的錯誤訊息。
這時候我們可以使用 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,這也會讓元件銷毀後再生成。
如果不希望每次元件生成時,都重新執行一次裡面的程式碼(像是 AJAX)的話,我們可以幫元件加上 <keep-alive>
標籤。
另外,我們把 v-if 從原本的 div 標籤,改到卡片元件上面,因為要確保資料載入後才繪製出資料內容,也就是剛才提到的時間差問題(AJAX 還沒執行完成,資料還沒有載入好)。
實際操作後,可以發現我們取消勾選之後,從 Vue DevTools 可以看到 <KeepCard>
會變成 inactive
(暫時隱藏)的狀態。下次啟動時,就不會重新執行元件的整個生命週期,而是會直接把它呼叫出來囉!
以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫