0%

Vue-js元件-01

接續上一篇,Vue-js元件沒有記錄完的內容。

props使用上的注意事項

本單元要講解主要有以下三部分

  1. 單向數據流
  2. 物件傳參考特性 及 尚未宣告的變數
  3. 如何維持元件的生命週期

單向數據流

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型別,而這也就是動態傳遞和靜態傳遞會造成資料型別不一樣的狀況。