0%

Vue常用API

Extend

假設今天有兩個元件,他們的差異非常小,
只有少部分不同的話,該怎麼處理呢?

那就先把兩個元件重複的部分抓取出來,接著,用Vue.extend的技巧將重複的部分存起來。
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
---HTML---
<div id="app">
<table class="table">
<tbody>
<tr is="row-component-one" v-for="(item, key) in data"
v-if="key % 2"
:item="item" :key="key"></tr>
<tr is="row-component-two" v-for="(item, key) in data"
v-if="(key - 1) % 2"
:item="item" :key="key"></tr>
</tbody>
</table>
</div>

---JavaScript---
<script type="text/x-template" id="row-component">
<tr>
<td>{{ item.name }}</td>
<td>{{ item.cash | currency | dollarSign }}</td>
<td>{{ item.icash | currency | dollarSign }}</td>
</tr>
</script>

<script type="text/x-template" id="row-component-two">
<tr class="bg-primary text-white">
<td>{{ item.name }}</td>
<td>{{ item.cash | currency | dollarSign }}</td>
<td>{{ item.icash | currency | dollarSign }}</td>
</tr>
</script>

<script>
var childOne = {
props: ['item'],
data: function() {
return {
data: {},
extendData: '這段文字是 extend 得到'
}
},
template: '#row-component',
filters: {
dollarSign: function (n) {
return `$ ${n}`
},
currency: function(n) {
return n.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
});
}
},
mounted: function() {
console.log('Extend:', this)
}
}

var childTwo = {
props: ['item'],
data: function() {
return {
data: {},
extendData: '這段文字是 extend 得到'
}
},
template: '#row-component-two',
filters: {
dollarSign: function (n) {
return `$ ${n}`
},
currency: function(n) {
return n.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
});
}
},
mounted: function() {
console.log('Extend:', this)
}
}
var app = new Vue({
el: '#app',
data: {
data: [
{
name: '小明',
cash: 100,
icash: 500,
},
{
name: '杰倫',
cash: 10000,
icash: 5000,
},
{
name: '漂亮阿姨',
cash: 500,
icash: 500,
},
{
name: '老媽',
cash: 10000,
icash: 100,
},
]
},
components: {
"row-component-one": childOne,
"row-component-two": childTwo,
},
mounted: function() {
console.log('Vue init:', this)
}
});
</script>

元件破壞html結構

首先我想要在這邊先提醒你一下,你可以看到在以上範例,我們在tabletbody中的tr
是用is的方式去綁定我們的元件名稱,而不是直接用我們的元件名稱當標籤,塞進去即<row-component-one></row-component-one>
這個原因是因為在tbody裡面的第一層一定要塞tr,不能是其它的內容,不然table的html佈局會沒有辦法出現,所以,才需要在tr標籤中用is來綁定相對應的 Vue 元件,這邊你要特別注意一下。

OK~~回到上面的例題內容,
你可以看到以上兩個元件,
row-component-one 和 row-component-two,他們倆個定義的內容基本上都一樣,
只有差異在template引入的內容不一樣。
所以,我們可以將這兩個元件裡面相同的內容,抓出來並貼到Vue.extend裡面。
變成以下這樣子,我們只更動JS的部分

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
<script>
var newExtend = Vue.extend({
data: function() {
return {
data: {},
extendData: '這段文字是 extend 得到'
}
},
template: '#row-component',
filters: {
dollarSign: function (n) {
return `$ ${n}`
},
currency: function(n) {
return n.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
});
}
},
mounted: function() {
console.log('Extend:', this)
}
})

var childOne = {
props: ['item'],
extends: newExtend
}

var childTwo = {
props: ['item'],
template: '#row-component-two',
extends: newExtend
}
</script>

首先,
我們先將childOne的定義內容中,除了props:['item']以外的內容,都剪下來貼到
Vue.extend裡面。
第二,我們在ChildOne的定義內容裡面,新增一個屬性extends,並為這個屬性值
的內容設入你剛剛儲存剪下來的內容的變數newExtend。
如此,就完成了將重複的部分,加到extends裡面,並利用extends來引用這些內容。
接著,我們對childTwo的定義內容,
只留下propstemplate的內容,其他的都刪掉,
並新增一個extends屬性,並為它設入newExtend的內容。
如此,就完成利用extends來餵兩個不一樣的元件,引入相同的定義內容囉。

另外,我們為childTwo的元件生命週期修改一下
ex:

1
2
3
4
5
6
7
8
var childTwo = {
props: ['item'],
template: '#row-component-two',
extends: newExtend,
mounted: function(){
console.log('childTwo');
}
}

這時候,你會看到log的結果為
Extend Extend Extend childTwo Extend childTwo Vue init
你會看到你新增的函式也會被執行到。
由此可知,extends不只會執行生命週期內的函式,如果,在生命週期內再另外加入
其他的函式的話,這些新增加的函式也同樣會被執行。
完成的結果如下

extends - 資料重新定義

我們改寫一下上面案例中的childTwo,我們為它新增資料,看看會怎樣
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
var childTwo = {
props: ['item'],
data: function(){
return {
childTwo:'元件2'
}
},
template: '#row-component-two',
extends: newExtend,
mounted: function(){
console.log('childTwo');
}
}

此時,你打開vue的開發者工具,你會發現childTwo的元件除了會有原本元件的資料以外,
也會含有你剛剛新增的childTwo的資料喔。
所以,由此可知,新的元件所新增的資料不僅會擁有它新增的資料內容,更不會把從extends裡面所繼承的內容給取代掉。

extends - 複寫原本的資料

這邊在稍微修改一下以上範例中childTwo的資料內容
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var childTwo = {
props: ['item'],
data:function(){
return{
childTwo:'元件2',
extendData: 'childTwo覆蓋原本內容'
}
},
template: '#row-component-two',
extends: newExtend,
mounted: function(){
console.log('childTwo');
}
}

你可以看到,我們在childTwo將它從extends得到的屬性extendData,在它自己的data屬性裡面,再重新為它賦值一次,這個時候,你用Vue的開發者介面去看childTwo元件的extendData的屬性值,是你覆蓋完之後的值喔~~

小總結: 資料重新定義 v.s 複寫原本的資料

在以上這兩個小節中,你可以看到,你為extends中,重複的生命週期事件重新定義的話(即extends裡面有定義過mounted hooks,再繼承這個extends的元件中在定義它自己的mounted hooks),不只會保存原本的內容,也同時會呈現你新增的內容。
若你複寫原本的資料的話,就會呈現覆寫完的結果喔。

filters 自訂畫面資料呈現格式

filters 的概念就是在一個數值後面加上一槓 (|) 接著, 再加上一個filters 函式的名稱。
filters 不只可以重複使用 且 可在同一個數值中可以套多數個filter 函式的功能。

你可以看到以上這個範例,我們希望在table裡面的數值,加上千分號的效果,
所以,我們先在元件的定義裡新增一個filters的屬性,
接著,在此屬性裡面新增自訂義的filters名稱curreny,而在此currency的函式裡面,回傳了將數值做千分號處理的效果,
注意~~ 在這個函式的有一個參數n,它就是將加在 | currency前面的值,當作參數丟進 currency 的函式中。
而這邊丟入的參數就是item.icash。
如此,就可以為該欄加上千分號的效果囉。
另外,有關filters的效果可以重複使用,你可以看到我們也有為item.cash後面加上了 | currency,故也為此欄加入了千分號的效果了。

另外,我們還想要為這些數值前面加上錢字號$。

在這邊可以看到,我們在元件的filters裡面,再加入一個dollarSign屬性,並回傳加上錢字號的結果。
那這邊要特別注意的是,dollarSign這個函式傳入的參數,是item.icash和curreny處理完之後的結果,即加完千分號的item.cash結果。
並再為這個結果加上dollarSign的效果。

替元件外的物件也加入filter的效果

以上面的案例為例,如果我們直接將child中定義的currency 和 dollarSign加到HTML中的{{ data[1].cash }}中,
是不會有效果的,即{{ data[1].cash | currency | dollarSign}}這樣是不會有效果的。

那要怎麼處理呢,
我們就將這些filters的方法從原本的局部註冊,改成全域註冊。

你可以看到我們將原本註冊在child裡面的dollarSing和currency方法,拉到外面,
改由Vue.filter來註冊這兩個方法。
你可以看到在{{ data[1].cash | currency | dollarSign }} 加入這兩個filters的方法就不會報錯囉。
用以上這種方式,就可以讓所有元件都調用這兩種方法囉。

filter 全域註冊 v.s 局部註冊 寫法不同

以上面的範例,你可以看到如果是全域註冊的話Vue.filter的filter是不用加s的。
而局部註冊的話,在元件中的filters屬性,是要加s的喔!!!

無法寫入的資料,用 set 搞定它

在Vue中,是利用settergetter來監聽元件,並且在該元件的資料被更改時,可以及時更改。
所以,當該資料沒有setget的屬性的話,就代表該資料沒有進入Vue的綁定內。
Alt描述

記得為屬性設預設值

注意,如果你沒有事先為屬性設預設值的話,該屬性的資料就不會有setget的屬性喔。
而當該元件沒有進入Vue的綁定內的話,就會發生雖然該資料有被成功寫入,但因為該筆資料沒有getset的屬性,
也就是沒又進入vue綁定,導致,該元件的內容不會出現在畫面中喔。
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
---HTML---
<div id="app">
<table class="table">
<tbody>
<tr is="row-component" v-for="(item, key) in data" :item="item" :key="key"></tr>
</tbody>
</table>
</div>

---JavaScript---
<script type="text/x-template" id="row-component">
<tr>
<td>{{ item.name }}</td>
<td>{{ item.cash }}</td>
<td>{{ item.icash }}</td>
<td>
<span v-if="data.obj">{{ data.obj.name }}</span>
<button class="btn btn-sm btn-primary" @click="addData()">寫入資料</button>
</td>
</tr>
</script>

<script>
var child = {
props: ['item'],
template: '#row-component',
data: function() {
return {
data: {}
}
},
methods: {
addData: function() {
this.data.obj = {
name: this.item.name
}
console.log(this.data, this);
}
},
mounted: function() {
console.log('Component:', this)
}
}

var app = new Vue({
el: '#app',
data: {
data: [
{
name: '小明',
cash: 100,
icash: 500,
},
{
name: '杰倫',
cash: 10000,
icash: 5000,
},
{
name: '漂亮阿姨',
cash: 500,
icash: 500,
},
{
name: '老媽',
cash: 10000,
icash: 100,
},
]
},
components: {
"row-component": child
},
mounted: function() {
console.log('Vue init:', this)
}
});
</script>

以上範例你可以看到,當你點擊child元件中的寫入資料的按鈕的時候,會觸發它的addData方法,
這個時候,在addData方法中我們會動態地為data物件新增一個obj屬性,並為這個obj屬性新增一個name屬性,
並將這個name屬性的值,設為當下被點擊元件的name的名稱。

從這邊我們可以看出來,因為,data的obj屬性 和 obj屬性的name屬性,都是動態地被創出來的,所以,
根本不可能為它們預先設定預設值,也就造成obj的name屬性沒有setget的這兩個屬性,進而造成它沒有進入Vue的綁定,
最終,在畫面上是沒有辦法呈現該obj的name的結果。

以上的狀況,常發生在我們利用Ajax抓資料,並再資料被抓進來的時候,順道為它們新增屬性。
所以,我們希望事後補資料或動態地新增屬性到元件的data中的話,我們就需要用set的語法。

接下來就來介紹Vue.set要怎麼使用:
this.$set( target, index, value )
這個語法有三個引數
target: 將資料寫入到target
key: 該數值在target中的名稱是什麼
value: 要寫入的內容

藉由以上的方法,你就可以看到this.data的key值obj,就有了getset的方法,也就代表該資料已經進入Vue的綁定了。
接著,我們為鍵值obj新增了屬性name,並將this.item.name的值設給它。
經過以上set的設定,我們就可以在按下寫入資料按鈕後,動態的新增這個obj鍵值和它的name屬性,並將它們呈現在畫面中了。

Mixin 混合其它的元件內容

Mixinextends的功能有點雷同。
但是,extends是專門給單一元件使用。
Mixin是可以混合多個元件一起使用。

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
---HTML---
<div id="app">
<table class="table">
<tbody>
<tr is="row-component" v-for="(item, key) in data"
:item="item" :key="key"></tr>
</tbody>
</table>
</div>

---JavaScript---
<script type="text/x-template" id="row-component">
<tr>
<td>{{ item.name }}</td>
<td>{{ item.cash | currency | dollarSign }}</td>
<td>{{ item.icash | currency | dollarSign }}</td>
</tr>
</script>

<script>
// mixin 是多個混合的概念

Vue.component('row-component', {
props: ['item'],
data: function() {
return {
data: {},
}
},
template: '#row-component',
filters: {
dollarSign: function (n) {
return `$ ${n}`
},
currency: function(n) {
return n.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
});
}
},
mounted () {
console.log('這段是 Mixin 產生')
}
});


var app = new Vue({
el: '#app',
data: {
data: [
{
name: '小明',
cash: 100,
icash: 500,
},
{
name: '杰倫',
cash: 10000,
icash: 5000,
},
{
name: '漂亮阿姨',
cash: 500,
icash: 500,
},
{
name: '老媽',
cash: 10000,
icash: 100,
},
]
},
mounted: function() {
console.log('Vue init:', this)
}
});
</script>

我們使用mixin 改寫要以上的範例
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
---HTML---
<div id="app">
<table class="table">
<tbody>
<tr is="row-component" v-for="(item, key) in data"
:item="item" :key="key"></tr>
</tbody>
</table>
</div>

---JavaScript---
var mixinFilter = {
template: '#row-component',
filters:{
dollarSign: function (n) {
return `$ ${n}`
},
currency: function(n) {
return n.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
});
}
}
}
var mixinMounted = {
mounted () {
console.log('這段是 Mixin 產生')
}
}
Vue.component('row-component', {
props: ['item'],
data: function() {
return {
data: {},
}
},

mixins:[mixinFilter,mixinMounted]
});


var app = new Vue({
el: '#app',
data: {
data: [
{
name: '小明',
cash: 100,
icash: 500,
},
{
name: '杰倫',
cash: 10000,
icash: 5000,
},
{
name: '漂亮阿姨',
cash: 500,
icash: 500,
},
{
name: '老媽',
cash: 10000,
icash: 100,
},
]
},
mounted: function() {
console.log('Vue init:', this)
}
});
</script>

可以看到,我們先宣告兩個物件mixinFilter和mixinMounted,
並把重複的內容塞到他們裡面,
接著,在row-component的元件定義中,寫入資料mixins,然後,丟一個陣列進去,
那這個陣列裡的元素就是我們剛剛定義的mixinFilter和mixinMounted物件囉,
所以,用這種方式,我們就可以透過mixin來增加很多內容。

另外,我們新增一個新元件,並將mixin的內容加進去,再為這個新元件新增資料data

此時,你用Vue開發者工具就可以看到row-component-two元件,有多一個它自己的data資料屬性囉。