# Vue.js 課程筆記(Vue - The Complete Guide)
###### tags: `線上課程` `Vue.js`
# 參考
* [Udemy 課程:Vue - The Complete Guide (w/ Router, Vuex, Composition API)](http://www04.eyny.com/index.php)
* [勇者鬥 Vue 龍](https://peterhpchen.github.io/VuejsQuest/basic/01_Preface.html)
* [Awesome Vue.js](https://awesome-vue.js.org/)
# Ch2
新增並連接 Vue 的實體
```htmlembedded=
<section id="user-goal">
<h2>My Course Goal</h2>
</section>
```
```javascript=
const app = Vue.createApp()
app.mount('#user-goal')
```
在 html 插入並綁定 Vue
```htmlembedded=
<p>{{ outPutGoal }}</p>
```
```javascript=
const app = Vue.createApp({
data() {
return {
courseGoal: 'Finish the course!'
}
}
})
app.mount('#user-goal')
```
* `v-bind` 綁定 html 元素的屬性
* Vue App 的 `methods`
* `methods` 裡的 `this` 指向 Vue Apps 中 `data` 的變數
* 用 `v-html=` 從 `data()` 中輸出 html 的語法而不是字串
```htmlembedded=
<p v-html="outPutGoal()"></p>
courseGoalB: '<h2>Master Vue</h2>'
```
## 事件綁定
* Vue 綁定事件
```
<button v-on:click="counter++">Add</button>
<button v-on:click="counter--">Remove</button>
<p>Result: {{ counter }}</p>
data() {
return {
counter: 0,
};
},
```
* 事件修飾符 (Event Modifier),下面例子等於 `event.preventDefault();`
```htmlembedded=
<form v-on:submit.prevent="submitForm()">
</form>
```
* `v-once` 將元素的內容鎖定在初始值
```htmlembedded=
<p v-once>Starting Counter: {{ counter }}</p>
```
* 雙向綁定,綁定資料也會監聽表單輸入事件,常用在input、select、checkbox `v-model=name`
* 當 `methods()` 作為資料綁定,如果函數被執行,Vue 也會執行 `methods()` 內的其他函數,如果使用`computed()`,則不會
>only use methods if you know that you want to recalculate a value whenever anything on the page changed.
* `watch()`,監視某個`data`,當值變動時會執行函數
```javascript=
watch: {
counter(value) {
if (value > 50) {
setTimeout(() => {
this.counter = 0
}, 1000);
}
}
}
```
* 比較
* `methods()` => event-binding
* `computed()` => data-binding
* `watch()` =>
* 縮寫:
* `v-bind:url` => `:url`
* `v-on:click` => `@click`
* Vue 操作 CSS
```htmlembedded=
<div :class="['demo', { active: boxASelected }]" @click="boxSelected('A')"></div>
```
# Ch3
* `v-if`
* `v-else`
* `v-show`
* 比較
* `v-if`:如果沒滿足,元素就不存在
* `v-show`:元素一直存在於頁面中,只是透過`display: none` 的方式將元素隱藏起來
* `v-for`
* 為了避免重複產生 DOM 元素而浪費資源,在 `v-for` 中要設定 `key` ,通常是元素的_id
# Ch4 打怪小遊戲
* 玩家攻擊,並受到怪物反擊
* 玩家和怪物的血量條外觀隨著變化 => `css style`
* 設置一個變數紀錄回合數,每三回合,玩家可以特殊攻擊一次
* 玩家回血,並消耗一次回合
* Game Over 功能,設置 `winner` 變數,並用 `watch()` 監視玩家和怪物的血量,產生贏家或平手
* 如果血量小於0,`style` 顯示直接歸零
* Game Over 後產生 Start New Game 按鈕,會重製所有 `data()` 中變數的值
* 設置投降事件,按下 surrender 按鈕後,直接設置 `winner`變數值為 `monster`,遊戲結束
* 設定 Log 紀錄,每個 logMessage 有`{ actionBy, actionType, actionValue }`,並根據 `actionBy` 和 `actionType` 有不同的 `style`
# Ch6 Component (元件)
## Global 全域註冊
使用 Vue.component 語法來註冊一個元件,在註冊全域元件時要給予兩個參數,分別為「組件名稱」及「選項物件」,在下方範例中「組件名稱」為 `friend-contact`,「選項物件」則為其後的內容。
```javascript=
app.component('friend-contact', {
template: `
<li>
<h2>{{ friend.name }}</h2>
<button @click="toggleDetails">
{{ isDetailVisible ? 'Hide' : 'Show' }}
</button>
<ul v-if="isDetailVisible">
<li><strong>Phone:</strong> {{ friend.phone }}</li>
<li><strong>Email:</strong> {{ friend.email }}</li>
</ul>
</li>
`,
data() {
return {
isDetailVisible: false,
friend: {
id: 'manuel',
name: 'Manuel Lorenz',
phone: '01234 5678 991',
email: 'manuel@localhost.com'
}
}
},
methods: {
toggleDetails() {
this.isDetailVisible = !this.isDetailVisible
}
}
})
```
# Ch7 Vue CLI
# Ch8 Component Comunication
## Props (properties) (Parent => Child)
子組件可以設定 `props` 屬性,而父組件可以將這些屬性用 DOM 客製屬性設定在子組件上,在子組件內就可以當作實體中的屬性使用。
HTML 屬性需要使用 kebab-case ,全小寫並使用分隔符號( `-` )來設定,因此雖然在 JavaScript 內可以用 camelCase 設定,但在 HTML 屬性上還是要用 kebab-case 給予屬性值。
```javascript=
// App.vue
<template>
<section>
<header>
<h1>My Friends</h1>
</header>
<ul>
<friend-contact
name="Xing"
phone-number="0123 456"
email-address="asd123@gmail.com"
></friend-contact>
<friend-contact
name="Andy"
phone-number="0999 456"
email-address="ccc1515@gmail.com"
></friend-contact>
</ul>
</section>
</template>
// FriendContact.vue
<template>
<li>
<h2>{{ name }}</h2>
<button @click="toggleDetails">
{{ detailsAreVisible ? "Hide" : "Show" }} Details
</button>
<ul v-if="detailsAreVisible">
<li>
<strong>Phone:</strong>
{{ phoneNumber }}
</li>
<li>
<strong>Email:</strong>
{{ emailAddress }}
</li>
</ul>
</li>
</template>
<script>
export default {
props: ["name", "phoneNumber", "emailAddress"],
data() {
return {
detailsAreVisible: false,
friend: {
id: "manuel",
name: "Manuel Lorenz",
phone: "0123 45678 90",
email: "manuel@localhost.com",
},
};
},
methods: {
toggleDetails() {
this.detailsAreVisible = !this.detailsAreVisible;
},
},
};
</script>
```
## Props 是不可改變的
* 不能從子組件改變 props 的值
* 改變 props 值得方法
* 從父組件改變
* 在子組件中,把 props 的初始值指派給一個新的變數
## Props 屬性驗證
`props` 除了使用陣列宣告,也可以用物件宣告,而物件中可以定義此屬性的類型、驗證器。
使用陣列定義
```javascript=
props: ["name", "phoneNumber", "emailAddress", "isFavorite"],
```
使用物件定義每個屬性的型別、是否必須、預設值、驗證器
```javascript=
props: {
name: {
type: String,
required: true,
},
phoneNumber: {
type: String,
required: true,
},
emailAddress: {
type: String,
required: true,
},
isFavorite: {
type: String,
required: false,
default: "0",
validator: function(value) {
return value === "1" || value === "0";
},
},
},
```
## 動態組件
使用子組件時可以用 `v-bind` 綁定元素,使用相關語法
```javascript=
// APP.vue
<template>
<section>
<header>
<h1>My Friends</h1>
</header>
<ul>
<friend-contact
v-for="friend in friends"
:key="friend.id"
:name="friend.name"
:phone-number="friend.number"
:email-address="friend.email"
:is-favorite="true"
></friend-contact>
</ul>
</section>
</template>
```
## $emit (Child => Parent)
## 定義並驗證客製事件
```javascript=
emits: ["toggle-favorite", 其他 emit...]
```
進階的驗證,確認 $emit 函數的參數
```javascript=
emits: {
"toggle-favorite": function (id) {
if (id) {
return true;
} else {
console.warn("id is missing!");
return false;
}
},
}
```
## 依賴注入 provide & inject
$emit 與 inject 的比較,預設應該使用 $emit,但是如果組件之間溝通要跨越很多層,就可以使用 inject。
# Ch9 元件進階
## local 區域註冊
使用全域註冊的缺點是,不管有沒有使用到這個組件,只要使用全域註冊就一定會載入,因此使用全域註冊會將原本不需要的組件也載入進來,拖慢載入的時間。
所以針對某些特定實體設計的組件就可以用區域註冊的方式,註冊在需要它的組件中。
```javascript=
import TheHeader from "./components/TheHeader.vue";
import BadgeList from "./components/BadgeList.vue";
import UserInfo from "./components/UserInfo.vue";
export default {
components: {
the-header: TheHeader,
badge-list: BadgeList,
user-info: UserInfo,
},
data() {
return {
}
}
}
```
## CSS Style 作用域
Vue 可以很容易的將 CSS 模組化。
在每個組件中設置的 `style` 預設為全域變數,如果要設置區域範疇的 `style` ,要加上 `scope`。
```javascript=
<style scoped>
h1 {
...
}
</style>
```
## Slot
## Vue Fragment (根節點)
在 Vue3,組件的 template 內部要設置一個根節點
```htmlembedded=
<template>
<div>
<h1>
...
</h1>
</div>
</template>
```
在 Vue3,組件的 template 可以不用設置根節點
```htmlembedded=
<template>
<h1>
...
</h1>
</template>
```
## Vue 撰寫風格
[官方文件](https://cn.vuejs.org/v2/style-guide/)
## Vue 檔案結構
元件應該以不同類型來區分,例如 UI、Layout...
# Ch11 Form 表單
# Ch12 Http Request
# Ch13 Vue Router
# Ch14 Animation & Transition
# Ch15 Vuex