接續上一篇,Vue-js元件沒有記錄完的內容。
props使用上的注意事項 本單元要講解主要有以下三部分
單向數據流
物件傳參考特性 及 尚未宣告的變數
如何維持元件的生命週期
單向數據流 ex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ---HTML--- <div id="app"> <h2>單向數據流</h2> <photo :img-url="url"></photo> <p>修正單向數據流所造成的錯誤</p> </div> ---JavaScript--- <script type="text/x-template" id="photo"> <div> <img :src="imgUrl" class="img-fluid" alt="" /> <input type="text" class="form-control" v-model="imgUrl"> </div> </script> <script> Vue.component('photo', { props: ['imgUrl'], template: '#photo', }) var app = new Vue({ el: '#app', data:{ url: 'https://images.unsplash.com/photo-1522204538344-922f76ecc041?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=50e38600a12d623a878983fc5524423f&auto=format&fit=crop&w=1351&q=80', } }) </script>
像上面的範例,你在畫面上可以看到一張圖片和下面的一個input
欄位,input
裡面有這張圖片的網址, 當我們直接修改input
欄位裡面的內容時,瀏覽器會報錯,報錯內容為”不要直接對props傳入的資料做修改”,那此時,我們要怎麼修正這個錯誤呢?
將以上的範例,修改成下面這個樣子
修改了兩個地方, 第一,一個是我們在photo的元件定義中,新增一個變數newUrl,讓它吃到photo元件的媒介屬性imgUrl的值, 最後,再回傳newUrl。 第二,我們把x-template
中定義photo元件的input
欄位的v-model
改成跟newUrl綁定。 這樣一來,我們在input修改的其實是變數newUrl,就不在是直接對props傳入的資料做修改囉。 可以看出來props
是單向資料流的概念,只能由外部傳入內部,不能再由內部去修改外部傳入的資訊。
物件傳參考特性 及 尚未宣告的變數 先介紹 尚未宣告的變數
尚未宣告的變數 ex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ---HTML--- <div id="app"> <h2 class="mt-3">物件傳參考特性 及 尚未宣告的變數</h2> <div class="row"> <div class="col-sm-4"> <card :user-data="users_origin"></card> </div> </div> </div> ---JavaScript--- <script type="text/x-template" id="card"> <div class="card"> <img class="card-img-top" :src="user.picture.large" alt="Card image cap"> <div class="card-body"> <h5 class="card-title">{{ user.name.first }} {{ user.name.last }}</h5> <p class="card-text">{{ user.email }}</p> </div> <div class="card-footer"> <input type="email" class="form-control" v-model="user.email"> </div> </div> </script> <script> Vue.component('card', { props: ['userData'], template: '#card', data: function () { return { user: this.userData } } }); var app = new Vue({ el: '#app', data: { users_origin: {}, }, created: function() { var vm = this; $.ajax({ url: 'https://randomuser.me/api/', // 資料來源 dataType: 'json', // 資料格式 success: function(data) { vm.users_origin = data.results[0]; // 將資料來源陣列的第一個元素存入users_origin變數中 } }); } }); </script>
以上的範例,你會看到瀏覽器無法渲染出我們的card元件之外, 還會跳出’large’這個屬性是沒有定義的,也就是讀取不到我們傳入的數值。 以上這種狀況就是,在渲染元件的同時,我們想要匯入的資料並沒有傳輸完全,導致瀏覽器會不知道 這些數值是誰。 這時候,我們就會用v-if
來將渲染物件的時機往後挪,等到資料輸入完全之後,在渲染該物件,也就不會在出錯囉。 所以要改成
v-if解決資料傳輸與元件渲染時間差問題 首先,上述的例子中,ajax本身預設是非同步,所以,會造成資料傳輸與元件渲染時,有時間差。 以上的你可以看到,我們在html文件中的card元件標籤上,加入v-if="users_origin.phone"
, 藉由判斷原始資料的其中某個元素是否已經存在,若沒有就不渲染該元件,若有,就渲染該元件, 如此,就可以避免渲染元件和資料傳輸之間的時間差的問題囉。
v-if判斷的對象 另外,要注意v-if
判斷的對象是資料來源端users_origins
喔,不是card元件自己本身的成員屬性user-data喔,
物件傳參考特性 接下接著介紹物件傳參考特性, 藉由以上的範例,你直接對card元件的input
欄位的內容作修改, 接著,你打開Vue開發者工具, 你先點擊card元件,你會看到它的data中的user的email屬性會被改成你剛剛修改的內容, 再來你點擊Root,你會發現users_origin的user的eamil屬性也同樣的會變成你剛剛修改的內容, 這個現象並不是因為Vue的特性,而是因為JavaScript在傳遞物件是傳參考的特性, 所以,當你在其中一個地方更改這個傳遞進來的物件,因為傳參考的特性,會連來源端的內容也一起被修改到囉。
維持狀態與生命週期 ex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 ---HTML--- <div id="app"> <h2 class="mt-3">維持狀態與生命週期</h2> <div class="form-check"> <input type="checkbox" class="form-check-input" id="isShow" v-model="isShow"> <label class="form-check-label" for="isShow">Check me out</label> </div> <div class="row"> <div class="col-sm-4" v-if="isShow"> <keep-card> </keep-card> </div> </div> </div> ---JavaScript--- <script type="text/x-template" id="card"> <div class="card"> <img class="card-img-top" :src="user.picture.large" v-if="user.picture" alt="Card image cap"> <div class="card-body"> <h5 class="card-title" v-if="user.name">{{ user.name.first }} {{ user.name.last }}</h5> <p class="card-text">{{ user.email }}</p> </div> <div class="card-footer"> <input type="email" class="form-control" v-model="user.email"> </div> </div> </script> <script> Vue.component('keepCard', { template: '#card', data: function() { return { user: {} } }, created: function() { var vm = this; $.ajax({ url: 'https://randomuser.me/api/', dataType: 'json', success: function(data) { vm.user = data.results[0]; } }); } }); var app = new Vue({ el: '#app', data: { user: {}, isShow: true }, }); </script>
以上這個範例,當你將Check me out這個選項點選就會渲染出keepcard元件, 當你取消Check me out就會消滅這個元件, 當你下一次再點選Check me out,你會發現這次生成的人物相片跟上次的不一樣, 這是因為,每一次這個元件重新生成的時候,它就會跑一次它自己的ajax重新抓取資料, 故會導致上一次和這一次的相片不一樣,若你要解除這樣的狀況,就要使用<keeep-alive></keep-alive>
標籤, 來解決這個狀況。
keep-alive標籤保留上一次元件的資料 注意,我們將keep-alive
標籤包在keepcard的外層,另外,要注意原本放在div
上的v-if
, 要改為放在keepcard標籤上,這樣就能達成當選擇不在顯示該元件式,會保留本次資料, 等下次要再顯示時,該元件的資料就會是上一次的資料囉。
v-if解決資料傳輸與元件渲染時間差問題 但是,以上這個修改完的內容,會跳其他錯誤,就是它會說讀不到’large’這個屬性值。 原因,跟”尚未宣告變數的範例”內容的問題差不多,都是因為ajax尚未讀取完成內容,就要渲染元件了, 所造成的時間差上的錯誤。 你可以看到我們是在x-template
中去加入v-if
判斷的內容,而可以在x-template
中,直接判定ueser的屬性, 是因為user是keepcard元件自身的成員屬性,所以,可以直接調用,不需要透過媒介屬性了。 因此,再加了v-if
的判斷,就不會再出現讀取不到某個資料屬性的錯誤囉。
預防傳入的資料型別有誤 ex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ---HTML--- <div id="app"> <h2>Props 的型別</h2> <prop-type :cash="cash_origin"></prop-type> </div> ---JavaScript--- <script type="text/x-template" id="propType"> <div> <input type="number" class="form-control" v-model="newCash"> {{ typeof(cash)}} </div> </script> <script> Vue.component('prop-type', { props: ['cash'], template: '#propType', data: function() { return { newCash: this.cash } } }); var app = new Vue({ el: '#app', data: { cash_origin: '300' } }); </script>
以上的範例你可以看到,在畫面上呈現的cash的屬性是string
, 那是因為,我們在利用Vue.component的props去定義媒介屬性的時候,並沒有特別限定cash的型別, 導致它什麼型別都可以接收,而資料來源的cash_origin剛好又是字串型別,cash接收到,自然它就會變成字串型別了。 那要預防這種狀況的話,我們就要修改一下porps
的寫法, ex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ---HTML--- <div id="app"> <h2>Props 的型別</h2> <prop-type :cash="cash_origin"></prop-type> </div> ---JavaScript--- <script type="text/x-template" id="propType"> <div> <input type="number" class="form-control" v-model="newCash"> {{ typeof(cash)}} </div> </script> <script> Vue.component('prop-type', { props: { cash:{ type:Number, // 限定其型別必須為數字型別 default: 300 // 預設值為300 } }, template: '#propType', data: function() { return { newCash: this.cash } } }); var app = new Vue({ el: '#app', data: { cash_origin: '300' } }); </script>
瀏覽器顯示接收的資料型別錯誤 在props中,按照上面的方式修改後,此時,瀏覽器就會跳錯,它會說期待是接收到數字型別的資料, 但卻接收到字串型別的資料。 有這個提示訊息,你就能知道,是來源端的資料型別給錯了,給成字串型別的,應該修改成數字型別的資料。
為props屬性值新增預設值 接著,在type的後面還有一個default,它是cash的預設值,當你都沒給它任何資料時,它會是預設值300。
靜態與動態傳入數值差異 ex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ---HTML--- <div id="app"> <h2 class="mt-3">靜態與動態傳入數值差異</h2> <prop-type cash="300"></prop-type> </div> ---JavaScript--- <script type="text-x-template" id="propType"> <div> <input type="text" class="form-control" v-model="newCash"> {{typeof(cash)}} </div> </script> <script> Vue.component('prop-type',{ props:['cash'] template:'#propType', data: function(){ return { newCash: this.cash } } }); var app = new Vue({ el: '#app', data:{ } }) </script>
以上的範例可以看到在prop-type元件上,我們這邊是利用靜態傳遞的方式, 將300這個值傳給cash,那這個時候,cash的型別是字串型別。 若我們上面的cash的傳遞方式改成動態的方式綁定, 也就是直接在cash旁邊加上v-bind
,變成:cash=”300”, 這個時候cash的型別會變成Number型別,而這也就是動態傳遞和靜態傳遞會造成資料型別不一樣的狀況。