前一篇分享最後面有提及,使用 HTML 標籤時,通常會在標籤中加入一些屬性,例如:
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer"/>
我們所設計的 <say-hello/> 標籤,只會固定輸出 Hello World!,如果我們希望,讓 World 變成一個可變動的字串,當我們單純使用<say-hello/> 時,就輸出 Hello World!,但是如果我們加入的屬性 <say-hello msg = "Quasar"/> 。就會輸出 Hello Quasar!。
從這篇分享中,你可以學到:
- 標籤屬性設計
- 從標籤傳入屬性值
- JavaScript 對文字物件的處理
- props 與 data() 的比較
- data() 的語法
從 SayHello 複製 SayHello2 元件
為了不影響 SayHello 元件,請在 components 目錄新增一個 SayHello2.vue,並 Copy SayHello 元件的內容。
就操作的方便性來說,可以在 SayHello.vue 上面按滑鼠右鍵選複製,然後在 Components 目錄上面按滑鼠右鍵選貼上。然後重新命名,開啟 SayHello2.vue 後,針對內容做一些小修改以免跟 SayHello 完全相同,修改後如下:
<template>
<h4>Hello World!</h4>
</template>
<script>
export default {
name: 'SayHello2'
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h4 {
margin: 24px 0 0;
color: green;
}
</style>
物件導向程式設計
如果你是接受過正規訓練的程式設計師,一定學過 C++ 或是 Java,因此對 "物件導向程式設計" 一定不陌生。但是請注意,我的重點並不在於 Vue.js 中 extends 或 mixins 的 OOP 相關的繼承語法,而只是單純的以物件的角度,來理解<script>中程式的內容。
當我們使用 C++ 或 Java 設計一個物件的時候,物件裡面會有兩個基本要素,一個是屬性、另一個是方法。而Vue <Script> 裡面定義的物件,也包含了屬性與方法。
我們先談屬性,因為這裡要談的就是 HTML 裡面屬性的設計方法。請在 SayHello2.vue 裡面加上 Props 程式碼:
<script>
export default {
name: 'SayHello2',
props: {
msg: String
}
}
</script>
在這裡,我們定義了一個名為 SayHello2 的 "物件" ,在這個 "物件" 裡面,我們定義了一個屬性 (Props) msg,而 Props 的主要目的,是來接收標籤中的屬性值。msg 的資料型態為 String。
當我們在 <script> 中定義了 msg 屬性之後,就可以在 <template> 中以 {{...}} 來使用它,如下:
<template>
<h4>Hello {{msg}}!</h4>
</template>
在標籤中傳入屬性值
接下來,我們在 About.vue 裡面使用 SayHello2,我們一樣使用 IntelliSense 的方式來撰寫程式:
在 </p> 的下一行,輸入 <say... ,選取 say-hello-2,並輸入 msg 的傳入值,如下:
<say-hello/>
<p>
中央流行疫情指揮中心今(21)日表示,本(2021)年12月14日起入境擇定春節檢疫專案C方案之民眾,
將自12月21日起陸續期滿168小時,凡經檢疫第6天PCR檢測陰性及返家居家檢疫環境與條件均符合規定者,
請全程佩戴口罩,搭乘地方政府安排之防疫車輛,返家接續後7天在家居家檢疫,並配合以下檢測措施
</p>
<say-hello-2 msg="Quasar"/>
由於使用的 IntelliSense,以下的程式碼會自動完成:
<script>
import SayHello from 'src/components/SayHello.vue';
import SayHello2 from 'src/components/SayHello2.vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { SayHello, SayHello2 },
name: 'PageAbout'
})
</script>
執行結果如下:
屬性的預設值與其它處理
前面的例子中,當我們以 <say-hello-2 msg="Quasar"/> 引用時,msg 會將 Quasar 字串傳入,但是實際上很多標籤的屬性都是可有可無的,如果我們也允許 <say-hello-2/> 語法,我們就必須將 msg 設定一個預設值,以增加使用的彈性:
<script>
export default {
name: 'SayHello2',
props: {
msg: {
type: String,
default: 'World'
}
}
}
</script>
這時,如果我們以 <say-hello-2/> 引用,就會輸出 default 值 World。
<template>
<q-page class="flex flex-center">
<h3>關於這個網站</h3>
<say-hello/>
<p>
中央流行疫情指揮中心今(21)日表示,本(2021)年12月14日起入境擇定春節檢疫專案C方案之民眾,
將自12月21日起陸續期滿168小時,凡經檢疫第6天PCR檢測陰性及返家居家檢疫環境與條件均符合規定者,
請全程佩戴口罩,搭乘地方政府安排之防疫車輛,返家接續後7天在家居家檢疫,並配合以下檢測措施
</p>
<say-hello-2 msg="Quasar"/>
<say-hello-2/>
</q-page>
</template>
由於 JavaScript 本身是物件導向語言,所以我們可以使用 "object.method()" 的語法來做一些額外的處理。msg 的型態是 String,因此,我們可以使用 String 物件所提供的方法。例如,如果我們希望 msg 在輸出的時候,可以將字串轉換成大寫,我們可以這樣寫:
<template>
<h4>Hello {{msg.toUpperCase()}}!</h4>
</template>
如果我們希望將 msg 字串反轉,則稍微複雜一些,需要用三個方法來達成:
<template>
<h4>Hello {{msg.split('').reverse().join('')}}!</h4>
</template>
執行的順序是,split('') 將字串轉換成 array,reverse() 再將 array 反轉,最後再 join('') 成新的字串:
一個特殊的 Method -- data()
在繼續我們的程式之前,我先針對程式的顯示做一些整理,讓輸出效果好一點。請打開 About.vue ,我們針對 <style> 部分做一些修改,就是把這個頁面中會用到的 HTML 標籤的式樣做簡單的定義:
<style scoped>
h3 {
margin: 5px;
color: blue;
}
p {
margin: 5px;
color: purple;
}
</style>
在這裡重新定義了 Margin,讓每個顯示區塊有一致的留白寬度。至於 Margin 所代表的意義,可以由下面的圖形大致了解。如果有興趣,可以自行試試。
About.vue 裡面定義了 h3的式樣,再進入 SayHello.vue 裡面定義 h4,完整程式如下:
<template>
<h4>Hello World!</h4>
</template>
<script>
export default {
name: 'SayHello'
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h4 {
margin: 5px;
color: orange;
}
</style>
再進入 SayHello2.vue 定義 h5,完整程式如下:
<template>
<h5>Hello {{msg.toUpperCase()}}!</h5>
</template>
<script>
export default {
name: 'SayHello2',
props: {
msg: {
type: String,
default: 'World'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h5 {
margin: 5px;
color: green;
}
</style>
最後,我把 About.vue 的 <p> 修改成說明,這樣比較清楚我們做了那些測試:
<template>
<q-page class="flex flex-center">
<h3>關於這個網站</h3>
<say-hello/>
<p>上方顯示了網頁標題以及 say-hello 元件的固定標題文字。其中網頁標題的樣式為 h3,
而 say-hello 標題樣式為 h4。</p>
<say-hello-2 msg="Quasar"/>
<say-hello-2/>
<p>上方顯示了 say-hello-2 元件的動態標題文字,會以 msg 屬性傳入要打招呼的對象字串,
如果沒有傳入屬性,預設值為 World!</p>
</q-page>
</template>
如果你有試著把瀏覽器放大所小,就會看到 RWD 的效果,裡面不論是 heading 或是文字段落,都是向右延伸而且居中顯示,這是因為 <q-page class="flex flex-center"> 的緣故。
言歸正傳,前面的範例都只使用到 props,這個屬性的用途在於接收標籤傳入的值,這個值會直接在標籤內容中以 {{prop_name}} 的方式顯示其值。一般來說,props的值是由外部傳入的,我們不會在程式裡面修改其值。但是如果我們要顯示的值,是必須由程式運算而產生的呢?這時我們就可以用 data() 來達成。
例如,我們想展示 漢堡單價 50 元,買 12 個,共 600 元。 這段文字,其中 price = 50、amount = 12,total = price * amount 則是個運算式。price / amount 是傳入值,我們可以定義為 props,而 total 為運算值,我們就必須定義為 data()。
建立一個 TotalPrice.vue 元件,內容如下:
<template>
<p>漢堡單價 {{price}} 元,買 {{amount}} 個,共 {{total}} 元。</p>
</template>
<script>
export default {
name: 'TotalPrice',
props: {
price: {
type: Number, default: 70
},
amount: {
type: Number, default: 0
}
},
data() {
return {
total: this.price * this.amount
}
}
}
</script>
<style scoped>
p {
margin: 5px;
color: rgb(196, 16, 121);
}
</style>
Price 跟 Amount 沒有問題,我們將他們定義成 props,型態為 Number,並給予預設值。重點在於 data(),標題處已經闡明,data() 可以視為一個特殊的 Method,因為是 Method,所以我們要用 Method 的語法:data() {...} 來定義它,裡面只有一個指令,就是 return {...} ,代表它只做一件事情,就是把值傳回來。
data() 的基本語法如下:
data() {
return {
data_name: 100
}
}
data_name 是一個變數名稱,冒號的後面則直接給予一個值,但是在這裡,我們不是單純給予一個數值,而是 price * amount,又由於 props 跟 data() 在同一層,如果在 data() 中要引用 props 中的值,必須要用 this.price * this.amount 才行。最後得到我們的程式碼:
data() {
return {
total: this.price * this.amount
}
}
在 <template> 中要引用變數,data() 與 props 是一樣的:
<template>
<p>漢堡單價 {{price}} 元,買 {{amount}} 個,共 {{total}} 元。</p>
</template>
最後,在 About.vue 裡面加入三行:
<template>
<q-page class="flex flex-center">
...
<total-price price = "50" amount = "12"/> ...(1) 整行
</q-page>
</template>
<script>
...
import TotalPrice from 'src/components/TotalPrice.vue'; ...(2) 整行
export default defineComponent({
components: { SayHello, SayHello2, TotalPrice }, ...(3) TotalPrice 部分
...
})
</script>
執行結果如下:
下一篇分享,將進入最精采的部分,就是 MVVM 的資料綁定。
留言
張貼留言