# Vue JS初探(上)
## 目錄
[toc]
## What is framework
在網頁的發展歷史中,一開始大家都是直接透過原本的原生函式來撰寫網頁應用程式,因為當時的專案都不大,網路以及電腦也沒辦法負荷太多的程式碼,所以沒有太大影響。後來隨著專案越來越大,數量也越來越多,就出現了許多重複出現的需求,例如說針對一個有特定屬性的物件操作。秉持的「不要重複造輪子」的想法,大家會把這些常出現的東西寫再一起,變成所謂的「函式庫」,之後需要功能時就直接去呼叫這些已經寫好的函式,不用再重複撰寫。這樣一來降低了維護的成本,也提高的開發的效率。這樣的東西也衍伸出「黑盒子」的概念。
隨著專案更加龐大,現有的函式庫已經沒辦法有效地解決一些問題,例如說如果要開發一個網站動畫,函式庫能直接執行的功能有限,就變回當初用原生函式撰寫的狀況差不多,同時對於物件的概念日益盛行。於是比函式庫更加龐大,架構也更加完整的東西出現了:「框架」。
### 使用框架的好處
- 方便管理,由資料決定畫面,將資料從介面中抽取出來,每個畫面都是對資料處理過後的呈現,尤其是在動態JS中有關DOM的操作
- 將腳本和程式模組化,使得各組件只需要處理組件內的事,外部引用的組件來決定怎麼使用、提供什麼資料給組件,藉由簡單的切分權責,加上前述的由資料決定畫面,就能讓各個組件的任務單一,並且能被重複使用。
- 提高server和browser間資料存取的效率
## Then, what is Vue?
- Front-end, JavaScript/TypeScript framework
- Vue是個發展中的前端框架,可以輕易的和其他library或現有專案整合
- 像是把Vue和既有的HTML做整合,這讓你可以像使用插入性替換的函式庫一樣來使用Vue
- Used to **dynamic** & **data-driven** wedsites(SPA's)
- Easy to let user use DOM and manage data
- Vue是一套前端框架,目的是讓網頁可以動態Load資料,有別於傳統靜態的頁面將內容寫死在source code裡面
- Used to create stand-alone widgets
- Vue提供漸進式的方法來撰寫網頁內容。就像大部分的框架,Vue讓創造可以重複使用的網頁內容區塊(稱為widget元件)

## The characterists of Vue websites
### Makw a comparison
- between the progressive JavaScript framework and the imperative programming
|type|differnece|
|----|----------|
|the progressive JavaScript framework(渲染式框架)|透過簡單的語法,完成對目標的綁定,對於更動的內容更易修改|
|the imperative programming(指令式編成)|像是JavaScript的DOM,一個指令對應一個物件|
### MVVM模式

- 將DOM監聽的事件狀態與狀態(Model)綁定,由JS的物件管理。使用者在畫面(View)上所做的更動,ViewModel層上的Vue.js就會回傳到有JS的物件表式的Model,Model修改後同步更新View上對影的內容
- 操作的最小單位從DOM中的節點變為一個個component的組合,而每個components都有其模板與樣式JS code
- DOM Tree --> Components Tree
### The routing between browser and server
- 前端模擬路由,用於實現SPA;that is,換頁時不需向後端發出請求
- All routing is done in the browser instead on the sever
- When we interact with the components, the website doesn't go to the server and instead Vue handles in the browser(much faster and smoother)--Single Page Application
#### Don't use Vue or something similar like react

- the browser is constantly making a request to the server for every new pages(a bit slow)
#### Use Vue

- Vue can be injected to different websites
- Vue bundles take control of the website in the browser, and it renders the different Vue components needed for the page and take over the link of it
### Single Page Application(SPA)
- Only a single HTML page sent to the browser
- Vue intercept(攔截) subsequent(後來的) requests and handles "page" changes in the browser by swapping what components are shown on the page
- Result is in much faster and smoother
### Instal the extension "Vetur"

- bring some features for Vue appication
## Basic template
- `<script src="https://unpkg.com/vue@3.0.2">`是一種對於Vue形式的引用,在此我所使用的版本為3.0.2
- 也能以`<script src="https://unpkg.com/vue@next">`表示使用最新版的Vue
### Example

#### HTML的操作
1. 設立讓HTML掛載的節點,像是id,class,tag
2. 需綁定的內容以{{}}表示
- name of the property we want to output use double curly braces
- We can't use those variables outside the scope of Vue "root"
#### 建立Vue的框架
3. 利用Vue.createApp()這個method,建立實體物件(以root為例)
- pass in the object and represent something called "root component" in view
- controlling the section in the web page
4. data()函式能將JavaScript建立的對應object資料,用於HTML中綁定
5. return a data object
- it can be dynamic changing value
6. 將Vue的實體物件掛載至節點之上
- this method tells the app at what point or where in the DOM to mount this application
- any dynamic data we want to output inside this app element or any events(like click,mousemove....). All of that will be controlled by our view
- "root"物件內能夠回傳給html一物件,也能在JS檔中使用,作為動態操作value的依據
#### 補充 -- template
```htmlembedded=
const app = Vue.createApp({
template:`<h2>I am the template</h2>`
});
app.mount('#app');
```
<hr>
### methods - how to make our website be active
#### Methods and computed
- 監聽事件觸發後,透過函式的手法,對資料進行操作
- 若要使用data()內的屬性,要用this去做存取,也能用帶有參數的形式
- reference to the component itself
(後續再詳細說明,下面以methods為例)
#### Events--試問DOM中的AddEventListener和getElementById等method的靈壓?
`v-on:click="age++"`作為JavaScript中的物件綁定,語法為`v-on:使用者行為=執行程式`
- v-on is a directive which allow us to react to different type of events
- update the data() in the script
`@click` 其中的@為v-on的簡寫
`v-if`像是JavaScript中的條件判斷作用於DOM之上(這說法有點怪?舉例!!)
- In this case, v-if evalute what is in the quote,if it's true,then show this
`v-for`用於連續生成目標相近的element
- we use v-for to cycle through an array of the data and output a bit of template for each items or output to users some kind of list
`v-bind`用來動態新增或綁定一個或多個HTML屬性(src、class、style)
#### Example 1
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root">
<p>{{commodities}}-{{price}}</p>
<button v-on:click="price++">rise</button>
<button @click="commodities = 'nothing'">nothing</button>
<br>
<button @click="changegoods">Change goods </button>
<br>
<button @click="changegoods_re('Ten days wonder')">Change goods 2</button>
<div v-if="true">
<p>{{commodities}} || {{price}} </p>
</div>
</div>
<script>
const app = Vue.createApp({
data(){
return {
commodities:'The final Banpire',
price:54
}
},
methods: {
changegoods(){
console.log('you click me');
this.commodities = 'Boyce ODE';
},
changegoods_re(commodities){
this.commodities = commodities;
}
}
});
app.mount('#root');
</script>
</body>
</html>
```
#### Example 2
```htmlembedded=
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Learning Vue</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<div v-if="showBooks">
<p>{{ books[0].title }} - by {{ books[0].author }}</p>
<p>{{ books[1].title }} - by {{ books[1].author }}</p>
</div>
<div v-else>
<p>Click the button below to show books</p>
</div>
<button @click="toggleShowBooks">
<span v-if="showBooks">Hide books</span>
<span v-else>Show books</span>
</button>
<ul>
<li v-for="book in books">
<h3>{{book.title}}</h3>
<h3>{{book.author}}</h3>
</li>
</ul>
</div>
<script>
const root = Vue.createApp({
data(){
return{
showBooks:true,
books: [
{title:'The final Banpire',author:'Duncan',display:true},{title:'JavaScript Eloquent',author:'Johnny',display:true},{title:'Boyce ODE',author:'Fanny',display:true},{title:'Duncan is handsome',author:'Duncan',display:false}
]
}
},
methods:{
toggleShowBooks(){
this.showBooks=!this.showBooks;
}
}
})
root.mount('#app');
</script>
</body>
</html>
```
### v-bind
- 目的:用於和HTML與CSS之間的資料綁定
- 寫法為`v-bind:html屬性="對應的object"`,其中,`v-bind:`可簡化為`:`
- 複習一下html的屬性列表
- `<a href="">` `<img src="">` `<div class="">`等等
#### Example 3
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<script src="https://unpkg.com/vue@next"></script>
<title>Vue Learn</title>
</head>
<body>
<div id="app">
<a href="https://www.youtube.com/c/%E5%A4%9C%E6%A9%98%E5%AF%A6%E6%B3%81">夜橘YT</a>
<hr>
<a v-bind:href="link">豆漿YT</a>
</div>
<script>
const app = {
data() {
return {
link: "https://www.youtube.com/user/Qiyoudaoyi",
};
},
};
Vue.createApp(app).mount("#app");
</script>
</body>
</html>
```
#### Example 4
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
body{
background-color: white;
max-width: 960px;
margin: 20px auto;
}
p, h3, ul{
margin: 0;
padding: 0;
}
li{
list-style-type: none;
background: #fff;
margin: 20px auto;
padding: 10px 20px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
li.fav{
background: #ff9ed2;
color: white;
}
img{
width:400px;
height:200px;
}
</style>
</head>
<body>
<div id="app">
<p>{{filterBooks}}</p>
<div>
<!-- v-bind用於在Vue框架開發時,綁定HTML屬性 -->
<a v-bind:title="hint">把滑鼠移過來</a>
</div>
<div v-if="showBooks">
<ul>
<li v-for="book in filterBooks" v-bind:class="{fav: book.isFav}" @click="toggleFav(book)">
<!-- If the value is true, then we can apply this style -->
<img :src="book.img" v-bind:alter="book.owner">
<h3 style="font-size:100px">{{book.title}}</h3>
</li>
</ul>
</div>
<div v-else>
<p>Click the button below to show books</p>
</div>
<button @click="toggleShowBooks">
<span v-if="showBooks">Hide books</span>
<span v-else>Show books</span>
</button>
</div>
<script>
const app = Vue.createApp({
data(){
return{
hint:'哈哈笑死',
showBooks:true,
books: [
// Note that the property like "img" is a kind of HTML property
// The purpose to use the property "isFav" due to what we want to display
{title:'The final Banpire',owner:'Setsuna',img:'picture/Setsuna.png',isFav:true},
{title:'JavaScript Eloquent',owner:'Shioriko',img:'picture/Shioriko.png',isFav:false},
{title:'Boyce ODE',owner:'Ayumu',img:'picture/Ayumu.png',isFav:true}
]
}
},
methods:{
toggleShowBooks(){
this.showBooks=!this.showBooks
},
toggleFav(book){
book.isFav = !book.isFav
}
},
computed:{
filterBooks(){
return this.books.filter((book)=> book.isFav)
}
}
})
app.mount('#app');
</script>
</body>
</html>
```
### v-modal
- 一種雙向綁定
- v-model常用於表單及元素來做雙向數據綁定,結合v-bind跟v-on一樣,用v-bind初始綁定與呈現資料,用v-on監聽事件來做資料更新。
#### Example 5
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/vue@next"></script>
<title>Vue Learn</title>
</head>
<body>
<div id="app">
<input type="text" v-model="text" />
<div>{{ text }}</div>
</div>
<script>
const app = {
data() {
return {
text: "",
};
},
};
Vue.createApp(app).mount("#app");
</script>
</body>
</html>
```
### Computed 和 Methods 比較
- 兩者的差異在於, computed 會把計算後的結果暫存起來,如果裡面使用的資料沒有變動,就不會重複執行,以避免過度調用methods;反之,methods 只要在 HTML 裡面被使用幾次就會執行幾次。
- 在實際使用上,computed較為簡潔,尤其在重複使用相同的計算重複使用,但須注意computed需要return出一個值
- computed 的缺點是無法帶入參數,所以在設計時需考量,如果有有帶入參數的需求,就應使用 methods。
#### Example 6
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/vue@next"></script>
<title>Vue Learn</title>
</head>
<body>
<div id="app">
<input type="number" v-model="Num" />
<div>{{ Num }}</div>
<div>{{ time }}</div>
<div>{{ output }}</div>
<button v-on:click="calculate()">Calculate</button>
<button @click="calculate()">Calculate</button>
<div>{{ times }}</div>
</div>
<script>
const app = {
data() {
return {
time: 10,
Num: 0,
output: 0,
};
},
methods: {
calculate() { this.output = this.time * this.Num; },
},
computed: {
times() { return this.Num * this.time; },
},
};
Vue.createApp(app).mount("#app");
</script>
</body>
</html>
```
## Vue CLI

- 全稱為Vue.js Command-Line Interface
- 由Vue.js核心團隊開發,提供開發者快速建置 Vue.js 專案並整合相關工具鍊的一套指令列 (command-line) 工具
- 它的特色是提供開發者在短短的幾分鐘時間內,即可快速建置一個立即可用的 Vue.js (含 2.x/3.x) 示範專案,這個專案同時也內建了 Router、Hot-Reload、ES Lint 與 dev-Server 等功能。
- a tool to boilerplate full vue applications which come along to the configuration and structure that we need to create full vue website
### setup Vue CLI
1. 到[官網](https://nodejs.org/zh-tw/)安裝Node.js,建議安裝長期維護版

2. 打開command prompt,並輸入`node -v`,透過顯示版本的手段,以確認是否安裝Node.js
3. 打開terminal,輸入`npm install -g @vue/cli`(下圖為示意圖)

4. 先創一個資料夾,用以放入專案,並在terminal輸入`vue create [專案名稱]`

5. 選擇Vue 3 或 Manually select features(下面以後者作為操作)
6. features目前只需要Babel,透過空白鍵來選擇(之後提到Router會在建立一次cli,到時操作會在說明)

7. 其他選項

8. 安裝完後,進入那專案並由vscode打開即可

#### 簡單相關的file介紹
- `node_modules`: library --> if we install another library/package, they will be put in there
- `public`:contain the index.html file, which is sent to the browser initially
- `src`:the place where we write the source code
- contain main.js, which kickstarts our applications
- we use the method `createApp()`, which directly import "createApp" function from 'vue' library
- import a component from .vue file
- 載入整個專案中所使用到的所有JavaScript檔案
```htmlembedded=
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
```
#### To preview our vue application in a browser
- 打開terminal,並輸入`npm run serve`,然後會得到一個連結`http://localhost:8080/`,打開即可(之後能從控制台觀察一下)
- 打開console後,all inside in the empty div with '#app'
## Vue元件檔-SFC(Single File Component) 單一元件檔
### What is this?
一開始我們的專案都是透過Javascript去做相對應的渲染,但是當專案逐漸龐大的時候,很多可以重複用的元件都必須重複撰寫,這不僅會提升專案維護的難度,更會讓專案的架構變得複雜。
為了解決這樣的問題,Vue提供一個新的選項:SFC(Single File Component),作為單一元件的檔案。這樣的好處是當我們在組織程式碼的結構的時候, 可以很清楚地看出整個專案的架構與元件的分割關係,進而達到更高的可讀性及可重用性
### How to do?
以剛剛生成的app.vue為例
```htmlembedded=
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App" />
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
```
- 一個vue檔內會有三個部分,分別是<template\>、<script\>與<style\>。下面將逐一介紹
備註:components 中的.vue檔,檔名必須用大寫名稱,為了在<template\>不會和其他html tag 搞混
### template
- 這個部分類似html,就是撰寫這個元件長什麼樣子,寫法類似html,可以使用像是{{}}、v-if等vue的指令與語法
- With a view to not being in mess, we spilt those parts into different components. Then, when we render(打底) the components into the DOM, its template will be injected into the DOM.
- use the way of "ref" to store a refernece to a DOM element inside a variable, then, we can use regular JS way to manipulate it
- Just like the way of queryselector of DOM
### script
- 這個部分的寫法比較特殊,原則上是寫Javascript,也就是這個元件會怎麼動,但是由於這個是元件檔,所以會需要有匯出元件的部分。
- 在匯出的部分,會需要多提供這個元件的名稱(name: string),還有這個元件所使用到的元件(object list),剩下的部分就是與前面所提及的相似,例如data,computed等等。
- represent the root components as an object
- components屬性中,用於registering any extra components we use inside this component
### style
- 就是CSS,不過要注意CSS汙染,例如底下這樣的寫法,會導致整個元件的h1都被設定大小為100px,包括引用的元件。
```javascript=
<style>
h1 {
font-size:100px;
}
</style>
```
而這樣的問題可以透過增加scoped標籤解決
- `<style scoped>`
- we can add "scoped" attribute to limit CSS to this component only
- if we use "scoped" attribite on, it means that these styles will now only applay to whatever in the component
- it's not working and it won't apply this style to anything outside the components
```javascript=
<style scoped>
h1 {
font-size:100px;
}
</style>
```
### multiple components--to nest other components as html tags

- 將內含的HelloWorld.vue改為下面的Modal.vue
```htmlembedded=
<template>
<div class="backdrop">
<div class="modal">
<h1>Modal title</h1>
<p>modal content</p>
</div>
</div>
</template>
<style>
.modal{
width:400px;
padding:20px;
margin: 100px auto;
background:white;
}
.backdrop{
top:0;
position:fixed;
background:rgb(0, 0, 0, 0.5);
width:100%;
height: 100%;
}
</style>
<style scoped>
h1{
color:aqua;
font-size: 100px;
}
</style>
```
然後在App.vue中引入Modal這個component
```htmlembedded=
<template>
<div>
<h1>{{title}}</h1>
<Modal/>
<h1>we can write any html components here!!</h1>
<input type="text" ref="name">
<button @click="handleclick">click me</button>
</div>
</template>
<script>
import Modal from './components/Modal.vue'
export default {
name: 'App',
components: {Modal},
data(){
return{
title:'My first Vue app :)'
}
},
methods:{
handleclick(){
console.log(this.$refs.name);
this.$refs.name.classList.add('active');
this.$refs.name.focus();
}
}
}
</script>
<style>
/* global --> inside the component file they apply to any element on the page*/
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
h1{
border: 1px solid red;
display: inline-block;
padding-bottom: 10px;
}
.active{
color:blueviolet;
}
</style>
```
- 在`<template>`中為該component插入的位置,在此以`<Modal/>`表示
- 在`<script>`中引入該.vue的位置,在此以
`import Modal from './components/Modal.vue'`表示
- 也要在default物件中裡的components物件註冊child component
- for `$refs`, we can access on the refs object "any ref" that we name in the template
#### 附註:`<template>`出現error,顯示出The template root requires exactly one element.的狀況
- 原因:vue的模板只容許一個element,因此出現多個element便會報錯(這問題多出現於設定為Vue2的情況)
- 解決方法:
- 將`<template>`中的內容**以一個`<div>`包裝**
- 到`settings`中,輸入`eslint`,並取消勾選`Validate vue-html in using eslint-plugin-vue
- eslint-plugin-vue: 提供 Vue Style Guide
## The interaction between Parent Components and Child Components
- 從`<style scoped>`這最簡單的例子便能看出,Vue.js每個components都必須以互為獨立的形式,that is,我們不能從child components中修改parent components或其他components的資料
- 因此,我們需要透過某種屬性或是方法,完成child componens和 parent components 之間的資料傳遞
在撰寫Vue元件檔的時候,各位可能有注意到一行程式碼
```javascript=
<HelloWorld msg="Welcome to Your Vue.js App" />
```
這裡的msg就是Props。==Props就是父元件將資料傳遞給子元件的方式==,或稱元件間溝通的方式,他是一個陣列型態
父元件寫法:
```javascript=
<component :props="data" />
```
其中:props為名稱,data為值
子元件中的寫法
```javascript=
props:[
"props1",
"props2"
]
```
在app.vue與helloeorld.vue可以看到範例,使用方法就是直接將它當作資料使用
```htmlembedded
<div>{{ props1 }}</div>
<p>{{ props2 }}</p>
```
- 之前曾提及過Vue能將網頁所需的內容切割成多個components,並將之重複利用;然而,在不能直接取用的前提下,若有從外部引進資料的需求,就需要透過 props 屬性來引用外部的狀態。
- for those html elements, such as`<p>` `<h1>`, we can pass those html datas into the components from App.vue (the parent elements) to child components
- advantage:
1. make the components reusable && dynamic
2. there are multiple components using the same data that may be a text or an array of items, exc. We have to define that data in one single place
- which is called "single source of truth/data"
<hr>
### emit語法
- props屬性由parent component傳遞資料給child component,從上到下傳遞。反之,自然也會有下到上的溝通方式
- `emit` is what we use to **customize** events which can be fired from a component and **can be listened from the parent component**
- 語法:
```htmlembedded=
this.$emit('the_customized_event',{title:'this is a title'})
```
其中,emit能接收第二個參數作為想傳遞的資料(也可將之省略)
接著,我們看看下面兩個例子
- 父元件
```htmlmixed=
<template>
<div id="app">
<child v-on:childMethod="parentMethod"></child>
</div>
</template>
<script>
import Child from './components/Child';
export default {
name: 'App',
components: {
Child,
},
methods: {
parentMethod() {
console.log('Hello World');
},
},
};
</script>
```
- 子元件
```htmlmixed=
<template>
<button @click="handleClick">Emit</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('childMethod');
},
},
};
</script>
```
也可以帶入參數
- 父元件
```htmlmixed=
<template>
<div id="app">
<child v-on:childMethod="parentMethod"></child>
</div>
</template>
<script>
import Child from './components/Child';
export default {
name: 'App',
components: {
Child,
},
methods: {
parentMethod(val) {
console.log(val);
},
},
};
</script>
```
- 子元件
```htmlmixed=
<template>
<button @click="handleClick">Emit</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('childMethod', "Hello world!");
},
},
};
</script>
```
透過在後方加入參數的方式,可以將值往上傳到父元件
### Example 7
- App.vue
```htmlembedded=
<template>
<div>
<h1>{{title}}</h1>
<div v-if="showModal">
<ThisModal header="This is header" :text="text" theme="sale" @close="ToggleModal"/>
</div>
<button @click="ToggleModal">open</button>
</div>
</template>
<script>
import ThisModal from './components/ThisModal.vue'
export default {
name: 'App',
components: {ThisModal},
data(){
return{
title:'My first Vue app :)',
text:"This is a text",
showModal:false
}
},
methods:{
ToggleModal() {
this.showModal = !this.showModal;
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
h1{
border: 1px solid red;
display: inline-block;
padding-bottom: 10px;
}
.active{
color:blueviolet;
}
</style>
```
- ThisModel.vue
```htmlembedded=
<template>
<div class="backdrop" @click.self="closeModal">
<!-- 'self' means that only when we click on this and this is the target of the event, it will fire this function-->
<div class="modal" :class="{sale: theme === 'sale'}">
<h1>{{header}}</h1>
<p>{{text}}</p>
</div>
</div>
</template>
<script>
export default{
props:['header','text','theme'],
methods:{
closeModal(){
this.$emit('close');
}
}
}
</script>
<style>
.modal{
width:400px;
padding:20px;
margin: 100px auto;
background:white;
}
.backdrop{
top:0;
position:fixed;
background:rgb(0, 0, 0, 0.5);
width:100%;
height: 100%;
}
.modal h1{
/* make the style more specific */
color:red;
}
.modal p{
font-style: normal;
/* override the global */
}
.modal.sale {
background-color: brown;
color:wheat;
}
.modal.sale h1{
color:white;
}
</style>
```
#### We will illustrate the code step by step
- recall: **data-binded**
1. class屬性作為data-binded,這裡的例子也是很直覺,在`:class="{sale: theme === 'sale'}"`之中,如果sale的value為true(也就是===成立),則會有css渲染(也就是下方的.madal .sale)
2. 承上,我們能觀察到一個很有趣的比較,就是從`class="modal"`和 `:class="{sale: theme === 'sale'}"`之中,能發現前著的綁定呈 static,而後者呈dynamic的key vlaue pass
<hr>
- first, we pay attention on the property of "**props**"
1. "props"屬性的value透過array表示,將想操作的attribute以**string**的形式放入
2. 將我們所要從parent component的資料,註冊於child component之中,放入child component所插入的位置,像是`<Modal/>` 之中
3. 以"header"為例:在App.vue中,我們不需要在裡面定義和"header"本身有關的資料,因為我們已經在ThisModal.vue中註冊進props屬性綁定的array中;因此,我們能在ThisModal.vue中,直接以{{}}攫取
4. 如果不是用data-binded的形式(也就是:或v-bind:),操作的內容只能以string的形式;反之,像是數字、陣列、物件等便能進一步操作
<hr>
- second, how to control whether we want to inject the `<ThisModal/>` (that is, the child components)
- 以此為例,我們希望按下open的按鈕來讓`<ThisModal/>`component 顯示,而**只**按下背景使該component關閉
1. we use property "showModal" to control, and use the way of `v-if`
2. ThisModal.vue中做為App.vue的child component,利用`
closeModal(){
this.$emit('close');
}`的函式,返回parent component中`<ThisModal header="This is header" :text="text" theme="sale" @close="ToggleModal"/>`v-on所監聽的自訂'close' event,並執行其中的ToggleModal()函式