# Vue 3 介紹

1. 快速建構<font color="#FF7F50">網頁應用</font>的<font color="#1E90FF">前端</font>框架
2. 語法簡潔、容易學習、社群龐大
## 簡介
**Vue 3** 是一個用來構建網頁應用的前端框架。
屬於漸進式框架(Progressive Framework)
它可以幫助我們更簡單、更高效地開發互動性強的網站。
即使你沒有程式設計的背景,也可以理解它的一些基本概念和優點。
### 為什麼要使用 Vue 3?
1. **簡單易學**:Vue 3 的設計讓它非常容易學習和使用。即使是新手,也能快速上手,開始構建漂亮的網頁。
2. **高效**:它可以幫助我們更高效地開發應用,減少手動操作和重複勞動。這意味著我們可以用更少的代碼做更多的事情。
3. **靈活**:Vue 3 非常靈活,適用於從簡單的單頁應用到複雜的大型專案。你可以根據需要選擇使用哪些功能,不需要一次性學習全部內容。
### Vue 3 的基本概念
1. **反應式資料綁定**:這是 Vue 最重要的特性之一。當資料發生變化時,界面會自動更新。比如,你改變了用戶名,那麼顯示用戶名的地方會自動變成新的名字。
2. **組件**:Vue 應用是由一個個組件組成的。每個組件都是獨立的小程式,可以包含自己的資料、模板和邏輯。這讓我們可以把應用拆分成很多小部分,每個部分各自負責一個功能,這樣開發和維護起來就更簡單。
3. **指令**:Vue 提供了一些特殊的標記,叫做指令,用來在 HTML 標籤上添加特定的功能。比如 `v-if` 用來條件顯示某個元素,`v-for` 用來循環渲染列表。
### 簡單範例
下面是一個簡單的 Vue 3 應用範例,它顯示了一個可以點擊的按鈕,點擊後計數會增加。
```vue
<template>
<div>
<p>你已經點擊了 {{ count }} 次</p>
<button @click="increment">點擊我</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
</script>
<style>
button {
font-size: 16px;
padding: 10px 20px;
}
</style>
```
### 範例解釋
1. **模板 (template)**:
- 這裡定義了 HTML 結構,包括一段顯示點擊次數的文字和一個按鈕。
- 使用 `{{ count }}` 來顯示計數變量 `count` 的值。
- 使用 `@click="increment"` 來綁定按鈕的點擊事件,當按鈕被點擊時,會調用 `increment` 函數。
2. **腳本 (script setup)**:
- 使用 `import { ref } from 'vue';` 引入 Vue 的 `ref` 函數,用來創建一個反應式變量 `count`。
- 定義了一個 `increment` 函數,每次調用這個函數都會讓 `count` 的值增加 1。
3. **樣式 (style)**:
- 定義了一些基本的按鈕樣式,使按鈕看起來更美觀。
### 總結
Vue 3 是一個強大且易於使用的前端框架,可以幫助我們更高效地構建互動性強的網頁應用。它通過反應式資料綁定、組件化開發和簡單的指令,讓我們可以更輕鬆地處理複雜的網頁邏輯。即使你沒有程式設計背景,也能夠理解並開始使用 Vue 3 來創建自己的網頁應用。
## 認識網頁應用程式(Web Application) & 前端框架
> 網頁應用程式是通過網頁瀏覽器訪問和使用的應用軟體,不需要在本地設備上安裝。

### 主要特點
1. **瀏覽器使用**:
- 打開瀏覽器,輸入網址即可使用應用程式。

2. **不需安裝**:
- 無需在設備上安裝或更新,只需訪問網址即可使用最新版本。

3. **跨平台**:
- 可以在不同設備(電腦、手機、平板)和操作系統(Windows、macOS、Android、iOS)上使用。

### 常見範例
- **Gmail**:用來收發電子郵件。

- **Facebook**:用來社交和分享信息。

- **Google Docs**:用來在線編寫和儲存文檔。

### 總結

:::info
舉例:User 在網頁的搜尋內打上「蘋果」並按下送出,此時前端會將尋找「蘋果」這件事告訴後端,而後端就要根據指令告訴資料庫去尋找「蘋果」,資料庫找到「蘋果」後再給後端,後端再告訴前端,前端就會顯示跟「蘋果」相關的資料給 User。
:::
# Vue3 + Vite 專案建置 & 套件
## 前置
> 版本請依當下的版本為原則
1. [VsCode](https://code.visualstudio.com/download)

2. [Vetur](https://marketplace.visualstudio.com/items?itemName=Vue.volar) :VsCode內的套件

3. [Vue Devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd):Chrome的插件

4. [NodeJS](https://nodejs.org/en)

## 專案建置
:::success
此文件版本:
Node.js:v20.13.1
npm:v10.5.2
Vue:^3.4.21
Vite:^5.2.0
:::
1. 在桌面建立專案資料夾並在此資料夾位置內使用以下指令[(參考官網)](https://cn.vuejs.org/guide/quick-start.html)
```
npm create vite@latest
```
:::warning
@latest:指的是安裝當下現行最新版本
:::
2. 執行後會出現以下選項(若選項與下方不同請依官網為主)
```cmd!
✔ Project name: … <專案名稱>
✔ Select a framework: >> Use arrow-keys. Return to submit. // 這裡是選擇該專案要應用的前端框架
vanilla
> vue
react
preact
lit-element
svelte
✔ Select a variant? >> Use arrow-keys. Return to submit. // 使用什麼樣的開發語言?
> vue
vue-ts // 如果會 TypeScript 可以選這個
Scaffolding project in ./<專案名稱>...
Done.
cd <專案名稱>
npm install
npm run dev
```
完成後終端機範例畫面如下:

## 專案目錄結構
> 當你使用 Vue 3 和 Vite 創建一個專案時,會生成一個預設的目錄結構。
> 這個目錄結構看起來可能有點複雜,但其實每個文件和資料夾都有其特定的用途。

```!
my-vue3-vite-app/ <- 專案資料夾名稱
├── .vscode/ <- 針對此專案資料夾在 VsCode 內的設定,因每人設定不同故不另外說明。
├── node_modules/
├── public/
│ └── favicon.ico
├── src/
│ ├── assets/
│ ├── components/
│ ├── views/ <- 如果沒有這資料夾可自行創建
│ ├── App.vue
│ ├── main.js
│ └── router.js
├── .gitignore
├── index.html
├── package.json
├── README.md <- 可自行決定是否刪除
├── vite.config.js
└── yarn.lock (or package-lock.json) <- 在專案執行 npm install 就會自行建置
```
### 各部分說明
1. **node_modules/**:
- 這裡存放所有專案的相依套件(dependencies)。你不用手動修改這裡的內容,這是由 `npm` 或 `yarn` 自動生成的。
2. **public/**:
- 這個資料夾裡面的文件會被原樣複製到最終生成的應用程式中。通常用來存放靜態資源,比如網站的 `favicon.ico`。
3. **src/**:
- 這是你主要工作的地方。所有的 Vue 組件、JavaScript 文件、CSS 文件等都放在這裡。
- **assets/**:
- 放置靜態資源,如圖片、字體等。
- **components/**:
- 存放 Vue 的組件。組件是應用的可重用部分,比如按鈕、表單等。
- **views/**:
- 放置應用的主要視圖(頁面)。每個視圖代表一個頁面,比如首頁、關於頁等。
- **App.vue**:
- 主應用組件,是整個應用的根組件。
- **main.js**:
- 應用的入口文件。這裡是應用開始運行的地方,通常會在這裡創建 Vue 實例並掛載到 DOM 上。
- **router.js**:
- 設定應用的路由(如果使用 Vue Router)。這裡定義了應用中不同 URL 對應的視圖。
4. **.gitignore**:
- 設定哪些文件和資料夾在使用 Git 進行版本控制時應該被忽略。比如 `node_modules/` 通常會被忽略,因為它們可以通過 `package.json` 重新生成。
5. **index.html**:
- 應用的主 HTML 文件。這裡定義了應用的基本結構,Vue 會在這裡插入渲染後的內容。
6. **package.json**:
- 專案的配置文件,包含專案名稱、版本、依賴套件、腳本等信息。
7. **README.md**:
- 專案的說明文件,通常用來描述專案的用途、安裝和使用方法。
8. **vite.config.js**:
- Vite 的配置文件,用來配置 Vite 的各種選項,比如別名、插件等。
9. **yarn.lock(或 package-lock.json)**:
- 鎖定文件,記錄了當前專案中所有安裝的相依套件的具體版本,確保每次安裝時都一致。
### 總結
- **`src/`** 是你主要編寫程式碼的地方。
- **`node_modules/`** 和 **`package.json`** 管理你的依賴包。
- **`public/`** 用來存放不需要編譯的靜態文件。
- **配置文件**(如 **`.gitignore`**、**`vite.config.js`**)用來配置你的專案。
:::success
#### `package.json` 內容說明
當你打開一個使用 npm(Node Package Manager)或 yarn 管理的 JavaScript 專案時,你會看到一個名為 `package.json` 的文件。這個文件包含了專案的重要資訊和設定。以下是對 `package.json` 文件內容的簡單白話解釋:
```json
{
"name": "my-vue3-vite-app",
"version": "1.0.0",
"description": "A simple Vue 3 + Vite project",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"vue": "^3.2.0"
},
"devDependencies": {
"vite": "^4.0.0",
"@vitejs/plugin-vue": "^4.0.0"
},
"license": "MIT"
}
```
#### 每個部分的解釋
1. **`name`**:
- 專案的名稱。這個名稱應該是唯一的,通常使用小寫字母和連字符(-)。
```json
"name": "my-vue3-vite-app"
```
2. **`version`**:
- 專案的版本號。通常遵循語義化版本規範(semver),比如 `1.0.0`。
```json
"version": "1.0.0"
```
3. **`description`**:
- 專案的簡短描述,說明這個專案是做什麼的。
```json
"description": "A simple Vue 3 + Vite project"
```
4. **`main`**:
- 專案的入口文件,通常是應用的主文件。對於大多數應用來說,這一行可以忽略。
```json
"main": "index.js"
```
5. **`scripts`**:
- 定義一些快捷命令,你可以在命令行中運行這些命令來執行常見的任務,比如啟動開發伺服器、打包應用等。
- 例如:在終端機內打上 `npm run dev` ,就會執行`scripts`內的`dev`動作。
```json
"scripts": {
"dev": "vite", // 啟動開發伺服器
"build": "vite build", // 打包應用
"serve": "vite preview" // 預覽打包後的應用
}
```
6. **`dependencies`**:
- 專案的生產環境依賴包。這些是應用在運行時需要的套件。
```json
"dependencies": {
"vue": "^3.2.x" // Vue 3 框架
}
```
7. **`devDependencies`**:
- 專案的開發環境依賴包。這些是開發應用時需要的工具和庫,但在應用最終運行時並不需要。
```json
"devDependencies": {
"vite": "^4.0.0", // Vite 構建工具
"@vitejs/plugin-vue": "^4.0.0" // Vite 的 Vue 插件
}
```
8. **`license`**:
- 專案的許可證,說明這個專案的版權和使用條款。`MIT` 是一種常見的開源許可證。
```json
"license": "MIT"
```
#### 總結
- **`name`** 和 **`version`**:專案的名稱和版本號。
- **`description`**:簡單介紹專案。
- **`main`**:專案的入口文件(通常可以忽略)。
- **`scripts`**:一些快捷命令,方便你執行常見任務。
- **`dependencies`** 和 **`devDependencies`**:專案需要的相依套件,分為運行時和開發時的依賴。
- **`license`**:專案的許可證。
:::
## Vue 組件架構

### `xxx.vue` 文件結構
一個 Vue 組件文件通常包含三個主要部分:`template`、`script` 和 `style`。以下是一個簡單的例子:
```vue
<template>
<div class="example">
<h1>{{ message }}</h1>
<button @click="updateMessage">Click me</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue 3!'
};
},
methods: {
updateMessage() {
this.message = 'You clicked the button!';
}
}
};
</script>
<style>
.example {
text-align: center;
}
h1 {
color: blue;
}
</style>
```
### 每個部分的解釋
1. **`<template>` 部分**:
- 這部分定義了組件的 HTML 結構,也就是這個組件在頁面上會如何顯示。
- 使用雙花括號 `{{ }}` 來插入變量或表達式的值。
- 使用 Vue 指令(如 `@click`)來添加事件處理。
```vue
<template>
<div class="example">
<h1>{{ message }}</h1>
<button @click="updateMessage">Click me</button>
</div>
</template>
```
- **`<div class="example">`**:這是一個包含了其他元素的容器,並且有一個名為 `example` 的 CSS 類。
- **`<h1>{{ message }}</h1>`**:這個 `h1` 標籤會顯示 `message` 變量的內容。
- **`<button @click="updateMessage">Click me</button>`**:這個按鈕在被點擊時會調用 `updateMessage` 方法。
2. **`<script>` 部分**:
- 這部分定義了組件的邏輯,包括數據、方法、生命周期鉤子等。
- 使用 `export default` 將組件的配置對象導出。
```vue
<script>
export default {
data() {
return {
message: 'Hello, Vue 3!'
};
},
methods: {
updateMessage() {
this.message = 'You clicked the button!';
}
}
};
</script>
```
- **`data()`**:這是一個函數,返回一個包含組件狀態的對象。在這個例子中,狀態中有一個 `message` 變量。
- **`methods`**:這裡定義了組件中的方法。在這個例子中,有一個 `updateMessage` 方法,當按鈕被點擊時,它會更新 `message` 的值。
3. **`<style>` 部分**:
- 這部分定義了組件的樣式,使用標準的 CSS 語法。
- 樣式可以是全局的或是只針對這個組件的。
```vue
<style>
.example {
text-align: center;
}
h1 {
color: blue;
}
</style>
```
- **`.example`**:這個類選擇器應用於所有具有 `example` 類的元素。在這個例子中,它會使容器的文本居中。
- **`h1`**:這個元素選擇器應用於所有 `h1` 標籤,將它們的文字顏色設為藍色。
### 總結
- **`<template>`**:定義組件的 HTML 結構,描述組件如何顯示。
- **`<script>`**:定義組件的邏輯和數據,描述組件如何運作。
- **`<style>`**:定義組件的 CSS 樣式,描述組件如何呈現。
# 一、建立組件與基本資料的綁定:v-model、ref
## 建立組件 & 引入
1. 在 /components 內建立 componentsA.vue 並打上快捷鍵 `vue` + Tab(或者 Enter)

2. 在 App.vue 內引入 componentsA.vue

## 基本資料的綁定
> 當使用 Vue 3 來開發應用時,會遇到一些重要的概念,例如資料綁定(單向和雙向)、`v-model` 和 `ref`。
> 這些概念能讓你更有效率地操作和管理資料。
### 資料綁定(單向綁定)
**單向綁定**是一種從資料到視圖的資料流動方式。這意味著資料從 JavaScript 邏輯(通常在 `data` 或 `computed` 屬性中)流向 HTML 模板,用於顯示或渲染。
**範例:**
```vue!
<template>
<div>
<p>資料綁定(單向綁定)</p>
{{ data }}
</div>
</template>
<script setup>
// 定義 data 變數且這裡的 data 是一個常數,不可更改
const data = 'Hello World';
</script>
```
**說明:**
- 在這個例子中,`data` 資料從 JavaScript 流向模板,顯示在 `{{ data }}` 標籤中。
- 這是一種單向資料綁定,因為資料只從 JavaScript 流向模板,不能反過來修改。
### 資料綁定(雙向綁定) `v-model`
**雙向綁定**允許資料在 JavaScript 和 HTML 模板之間雙向流動。使用 `v-model` 指令,可以實現輸入元素(如輸入框)和 JavaScript 資料之間的雙向綁定。
**範例:**
```vue
<template>
<div>
<p>資料綁定(雙向綁定)</p>
<input type="text" v-model="text" />
<p>{{ text }}</p>
</div>
</template>
<script setup>
import { ref } from '@vue/reactivity'; // 引入 ref
// 使用 ref 宣告 text 變數
const text = ref('初始值'); // 使用 ref 宣告 text 變數
</script>
```
**說明:**
- 在這個例子中,`v-model` 綁定 `input` 元素和 `message` 資料。
- 當你在輸入框中輸入內容時,`message` 資料會自動更新,並顯示在 `<p>` 標籤中。
- 這是一種雙向資料綁定,因為資料可以在 JavaScript 和模板之間來回流動。
### `ref`
**`ref`** 是 Vue 中用來直接訪問 DOM 元素或組件實例的一種方法。它可以讓你在模板中標記某個元素,然後在 JavaScript 中輕鬆地操作這個元素。
**範例:**
```vue
<template>
<div>
<input ref="inputElement" type="text" />
<button @click="focusInput">Focus Input</button>
</div>
</template>
<script>
export default {
methods: {
focusInput() {
this.$refs.inputElement.focus();
}
}
};
</script>
```
**說明:**
- 在這個例子中,`ref="inputElement"` 將 `input` 元素標記為 `inputElement`。
- 使用 `this.$refs.inputElement` 可以在 JavaScript 中訪問這個 `input` 元素。
- 當按下按鈕時,`focusInput` 方法會被調用,使 `input` 元素獲得焦點。
### 總結
- **單向綁定**:資料從 JavaScript 流向模板(例如 `{{ message }}`)。
- **雙向綁定(v-model)**:資料在 JavaScript 和模板之間雙向流動(例如 `v-model="message"`)。
- **`ref`**:用來在 JavaScript 中直接訪問 DOM 元素或組件實例。
這些概念使得 Vue 3 能夠更靈活和強大地操作和管理資料,讓你的應用更加動態和互動。
:::success
在 Vue 3 中,`v-model` 和 `ref` 是常用的工具,然而在特定情境下,有一些方法可以進一步優化效能和代碼可讀性。
兩種情境模擬:帳號登入和基本資料輸入,並展示如何使用更優化的方法。
### 情境一:帳號登入
在帳號登入的情況下,`v-model` 非常方便,因為你需要雙向綁定用戶輸入的帳號和密碼。然而,如果希望進一步優化效能,特別是對於大型表單,可以考慮僅在必要時更新資料,例如使用事件處理而不是雙向綁定。
#### 使用 `v-model`
```vue
<template>
<form @submit.prevent="login">
<input v-model="username" type="text" placeholder="Username" />
<input v-model="password" type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
const username = ref('');
const password = ref('');
const login = () => {
console.log('Logging in with', username.value, password.value);
// 實際的登入邏輯
};
</script>
<style>
/* 樣式 */
</style>
```
#### 使用事件處理器(優化效能)
```vue
<template>
<form @submit.prevent="login">
<input @input="updateUsername" type="text" placeholder="Username" />
<input @input="updatePassword" type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
const username = ref('');
const password = ref('');
const updateUsername = (event) => {
username.value = event.target.value;
};
const updatePassword = (event) => {
password.value = event.target.value;
};
const login = () => {
console.log('Logging in with', username.value, password.value);
// 實際的登入邏輯
};
</script>
<style>
/* 樣式 */
</style>
```
### 情境二:基本資料輸入
對於基本資料輸入表單,`v-model` 也是非常方便的選擇。不過,如果表單數據非常多,使用 `ref` 來手動管理資料更新,並僅在需要時更新資料,可以提高效能。
#### 使用 `v-model`
```vue
<template>
<form @submit.prevent="saveData">
<input v-model="firstName" type="text" placeholder="First Name" />
<input v-model="lastName" type="text" placeholder="Last Name" />
<input v-model="email" type="email" placeholder="Email" />
<button type="submit">Save</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
const firstName = ref('');
const lastName = ref('');
const email = ref('');
const saveData = () => {
console.log('Saving data:', {
firstName: firstName.value,
lastName: lastName.value,
email: email.value
});
// 實際的保存邏輯
};
</script>
<style>
/* 樣式 */
</style>
```
#### 使用 `ref` 和事件處理器(優化效能)
```vue!
<template>
<form @submit.prevent="saveData">
<input ref="firstNameInput" @input="updateData('firstName')" type="text" placeholder="First Name" />
<input ref="lastNameInput" @input="updateData('lastName')" type="text" placeholder="Last Name" />
<input ref="emailInput" @input="updateData('email')" type="email" placeholder="Email" />
<button type="submit">Save</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
const firstName = ref('');
const lastName = ref('');
const email = ref('');
const firstNameInput = ref(null);
const lastNameInput = ref(null);
const emailInput = ref(null);
const updateData = (field) => (event) => {
if (field === 'firstName') firstName.value = event.target.value;
if (field === 'lastName') lastName.value = event.target.value;
if (field === 'email') email.value = event.target.value;
};
const saveData = () => {
console.log('Saving data:', {
firstName: firstName.value,
lastName: lastName.value,
email: email.value
});
// 實際的保存邏輯
};
</script>
<style>
/* 樣式 */
</style>
```
### 差異和原因
- **`v-model` 簡單直接**:適合簡單和中等大小的表單,因為它提供了簡單的雙向綁定,易於閱讀和編寫。
- **事件處理器優化效能**:在大型表單或性能敏感的情況下,使用事件處理器來手動更新資料,可以減少不必要的資料綁定操作,提高應用效能。
- **`ref` 精細控制**:使用 `ref` 可以精細控制 DOM 元素,有助於在特定情況下(如焦點管理或非綁定更新)提高性能和靈活性。
### 什麼是 `ref` 和 `reactive`?
在 Vue 3 中,`ref` 和 `reactive` 是用來創建響應式資料的兩種方法。這些方法可以讓我們的資料變得“活”起來,當資料改變時,自動更新使用這些資料的地方。
#### 簡單比喻
- **`ref`** 就像是用來存放單個值的小盒子,裡面的東西變了,盒子就會告訴我們變了什麼。
- **`reactive`** 就像是用來存放整個物件的大盒子,物件中的任何屬性變了,這個大盒子也會告訴我們變了什麼。
### `ref` 和 `reactive` 的基本用法
#### `ref`
- 用來創建單個響應式變量。
- 當你有一個數字、字串、布林值或其他簡單資料類型時,使用 `ref`。
**範例**:
```vue
<template>
<div>
<p>計數: {{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
</script>
```
**解釋**:
- `ref(0)` 創建了一個初始值為 0 的響應式變量 `count`。
- 當我們點擊按鈕時,`count` 的值增加,頁面上的顯示會自動更新。
#### `reactive`
- 用來創建響應式物件。
- 當你有一個包含多個屬性的物件時,使用 `reactive`。
**範例**:
```vue
<template>
<div>
<p>用戶名: {{ user.name }}</p>
<button @click="changeName">改變名字</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const user = reactive({
name: 'John',
age: 30
});
function changeName() {
user.name = 'Jane';
}
</script>
```
**解釋**:
- `reactive({ name: 'John', age: 30 })` 創建了一個包含 `name` 和 `age` 屬性的響應式物件 `user`。
- 當我們改變 `user.name` 的值時,頁面上的顯示會自動更新。
### 使用差異
1. **資料類型**:
- `ref`:用於單個值(數字、字串、布林值等)。
- `reactive`:用於物件或陣列。
2. **語法**:
- `ref` 創建的變量需要通過 `.value` 來訪問和修改。
- `reactive` 創建的物件直接訪問和修改其屬性。
**範例比較**:
- **`ref`**:
```javascript
const count = ref(0);
console.log(count.value); // 訪問值
count.value = 1; // 修改值
```
- **`reactive`**:
```javascript
const user = reactive({ name: 'John', age: 30 });
console.log(user.name); // 訪問屬性
user.name = 'Jane'; // 修改屬性
```
### 總結
- 使用 `ref` 來創建單個響應式變量,適用於簡單的資料類型。
- 使用 `reactive` 來創建響應式物件,適用於包含多個屬性的物件或陣列。
- `ref` 變量需要通過 `.value` 來訪問和修改,而 `reactive` 則直接訪問和修改其屬性。
這些工具讓我們在 Vue 3 中更方便地管理和響應資料變化,使得應用更加動態和互動。
這些方法可以根據具體情況靈活選擇,以達到最佳的效能和代碼可讀性。
:::
# 二、條件渲染顯示與點擊事件綁定 v-if、v-else、v-show
## 什麼是條件渲染顯示與點擊事件綁定
>當你在用 Vue.js 開發一個網站或應用程式時,可能需要根據不同的>情況來顯示或隱藏某些部分。
>這種根據條件來顯示或隱藏內容的功能稱為**條件渲染**。
>Vue.js 提供了一些簡單的工具來實現這種功能,比如 `v-if`、`v-else` 和 `v-show`。
### 什麼是條件渲染?
**條件渲染**就像在做選擇題時,如果符合某個條件,才會顯示特定的內容。
舉個例子:如果今天是星期一,你會顯示「今天是星期一」,否則就顯示「今天不是星期一」。
### `v-if` 和 `v-else`
- **`v-if`**:如果條件成立(為真),就顯示這部分內容;如果條件不成立(為假),這部分內容就不會出現在頁面上。
- **`v-else`**:和 `v-if` 搭配使用,當 `v-if` 條件不成立時,`v-else` 的內容才會顯示出來。
#### `v-if` 範例:
```vue!
<template>
<div>
<button @click="changeDay(1)">切換星期</button>
<button v-on:click="changeDay(2)">切換星期</button>
<p v-if="isMonday === 1">今天是星期一</p>
<p v-if="isMonday === 2">今天不是星期一</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
// 將 isMonday 初始值設定為1。
const isMonday = ref(1);
// 使用 @click 事件後使 changeDay(1 or 2) 傳到 index 內並依參數內容發生變化
const changeDay = (index) => {
isMonday.value = index
}
</script>
<style>
/* 樣式 */
</style>
```
:::success
`@click=` 和 `v-on:click` 是同樣的
:::
#### `v-if` 和 `v-else` 範例:
```vue
<template>
<div>
<button @click="isMonday = !isMonday">切換星期</button>
<p v-if="isMonday">今天是星期一</p>
<p v-else>今天不是星期一</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
const isMonday = ref(true);
</script>
<style>
/* 樣式 */
</style>
```
**說明:**
- 當 `isMonday` 為 `true` 時,顯示「今天是星期一」。
- 當 `isMonday` 為 `false` 時,顯示「今天不是星期一」。
- 點擊按鈕會切換 `isMonday` 的值。
### `v-show`
- **`v-show`**:根據條件顯示或隱藏內容。和 `v-if` 不同的是,`v-show` 不會從 DOM 中刪除元素,只是透過 CSS 的 `display` 屬性來隱藏或顯示元素。
#### 範例:
```vue
<template>
<div>
<button @click="changeDay(1)">星期一</button>
<button v-on:click="changeDay(2)">星期二</button>
<button v-on:click="changeDay(3)">星期三</button>
<p v-show="isMonday === 1">今天是星期一</p>
<p v-show="isMonday === 2">今天是星期二</p>
<p v-show="isMonday !== 1 && isMonday !== 2">今天是星期三</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
// 將 isMonday 初始值設定為1。
const isMonday = ref(1);
// 使用 @click 事件後使 changeDay(1 or 2) 傳到 index 內並依參數內容發生變化
const changeDay = (index) => {
isMonday.value = index;
};
</script>
<style></style>
```
**說明:**
- 當 `isVisible` 為 `true` 時,文字顯示。
- 當 `isVisible` 為 `false` 時,文字隱藏。
- 點擊按鈕會切換 `isVisible` 的值。
### 總結
- **`v-if`**:根據條件來添加或刪除 DOM 元素。用於元素在 DOM 中的添加和移除。
- **`v-else`**:和 `v-if` 搭配使用,當 `v-if` 條件不成立時顯示。
- **`v-show`**:根據條件來顯示或隱藏元素,但不會從 DOM 中移除元素,只是改變 CSS `display` 屬性。
:::success
## 最後複習 v-if、v-else、v-show 以及實際應用
> 當你在設計一個網站或應用時,有時你需要根據某些條件來決定是否顯示特定的內容。
### 差異:
- **`v-if`**:當條件為真時,會將元素添加到 DOM 中;當條件為假時,會從 DOM 中刪除元素。它是一個完全的切換,元素存在與否完全取決於條件。
- **`v-else`**:和 `v-if` 一起使用,用於當 `v-if` 條件不成立時,顯示另外一個元素。
- **`v-show`**:不論條件是真還是假,元素始終存在於 DOM 中,但是通過 CSS 控制其 `display` 屬性來顯示或隱藏它。即使條件為假,元素也只是隱藏了,但仍然存在於 DOM 中。
### 實際應用:
- **`v-if` 的實際應用**:當你有一些很重要的內容,只在特定情況下才想顯示時,可以使用 `v-if`。
- 例如:當用戶登入後才顯示其個人資訊。
- **`v-else` 的實際應用**:當你想在 `v-if` 條件不成立時顯示另外一個元素時,可以使用 `v-else`。
- 例如:當用戶未登入時顯示登入表單,登入後顯示用戶資訊。
- **`v-show` 的實際應用**:當你想在某些情況下隱藏元素,但仍然希望它佔用空間,可以使用 `v-show`。
- 例如:在點擊按鈕後顯示或隱藏一個下拉菜單。
### 總結:
- **`v-if`**:完全地添加或刪除元素,適用於元素在 DOM 中的添加和移除。
- **`v-else`**:和 `v-if` 搭配使用,在 `v-if` 條件不成立時顯示另外一個元素。
- **`v-show`**:元素始終存在於 DOM 中,通過 CSS 控制其顯示或隱藏。適用於需要頻繁顯示或隱藏的元素,但不希望影響其在 DOM 中的佔用空間。
這些工具讓你能夠根據不同的條件來動態地顯示或隱藏內容,從而為用戶提供更好的使用體驗。
:::
# 三、迴圈渲染與 key 屬性 v-for
## 什麼是迴圈渲染與 key 屬性
> 當我們使用 Vue.js 來建構應用程式時,`v-for` 是一個非常有用的指令,允許我們輕鬆地迭代並渲染列表或物件中的項目。
> `key` 屬性則有助於 Vue 快速有效地更新渲染的元素。
### 範例 1: 使用陣列與 `v-for` 和 `key`
假設我們有一個名字的列表,我們想要顯示這些名字。
範例代碼:
```vue!
<template>
<ul>
<li v-for="(name, index) in names" :key="index">{{index}}. {{ name }}</li>
</ul>
</template>
<script setup>
const names = ['Alice', 'Bob', 'Charlie'];
</script>
<style></style>
```
畫面結果:

#### 說明
- **v-for**: 我們使用 `v-for` 指令來迭代 `names` 陣列中的每個名字。
- **key**: `key` 屬性設置為每個項目的索引,這樣可以幫助 Vue 識別每個項目並有效地更新 DOM。
### 範例 2: 使用物件與 `v-for` 和 `key`
如果我們有一個物件,其中包含一些項目並且每個項目都有一個<font color="red">唯一的 ID 或者值</font>,我們可以這樣做:
範例代碼:
```vue!
<template>
<ul>
<li v-for="item in items" :key="item.id">{{item.id}} {{ item.name }}</li>
</ul>
</template>
<script setup>
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 1' },
{ id: 3, name: 'Item 2' },
{ id: 4, name: 'Item 3' },
];
</script>
<style></style>
```
畫面結果:

#### 說明
- **v-for**: 我們使用 `v-for` 指令來迭代 `items` 物件中的每個項目。
- **key**: `key` 屬性設置為每個項目的 ID,以幫助 Vue 有效地識別和更新項目。
### 範例 3: 結合 `v-if` 和 `v-for`
有時候,我們可能需要在渲染列表前做一些條件判斷。
假設我們只想顯示名字長度大於 3 的名字:
範例代碼:
```vue!
<template>
<ul>
<li v-for="(name, index) in names" :key="index">
<span v-if="name.length > 3">{{ name }}</span>
</li>
</ul>
</template>
<script setup>
const names = ['Ann', 'Alice', 'Bob', 'Charlie'];
</script>
<style></style>
```
畫面結果:

#### 說明
- **v-if**: 我們在 `v-for` 的每個項目中使用 `v-if` 指令來判斷是否渲染該項目。只有名字長度大於 3 的名字會被顯示。
### 範例 4: `v-if` + `v-else` 結合 `v-for`
結合 v-if 和 v-for,以條件判斷來顯示列表項目。
範例代碼:
```vue
<template>
<div>
<ul>
<li v-for="(item, index) in items" :key="index">
<span v-if="item.isAvailable">{{ item.name }}</span>
<span v-else>商品已售空</span>
</li>
</ul>
</div>
</template>
<script setup>
const items = [
{ name: 'Apple', isAvailable: true },
{ name: 'Banana', isAvailable: false },
{ name: 'Cherry', isAvailable: true }
]
</script>
<style></style>
```
畫面結果:

#### 說明
- 在這個例子中,我們渲染了一個包含物件的陣列,並根據每個項目的 isAvailable 屬性來決定顯示項目的名稱還是顯示 "商品已售空"。
### 範例 5: 結合 `v-show` 和 `v-for`
`v-show` 和 `v-if` 不同,它只會簡單地隱藏或顯示元素,而不會完全移除或添加到 DOM 中。假設我們有一個條件來顯示或隱藏某些名字:
範例代碼:
```vue!
<template>
<ul>
<li v-for="(name, index) in names" :key="index" v-show="name.includes('a')">{{ name }}</li>
</ul>
</template>
<script setup>
const names = ['Ann', 'Alice', 'Bob', 'Charlie'];
</script>
<style></style>
```
畫面結果:

#### 說明
- **v-show**: 我們在 `v-for` 的每個項目中使用 `v-show` 指令來判斷是否顯示該項目。只有包含字母 'a' 的名字會被顯示出來,但那些不符合條件的項目仍在 DOM 中,只是被隱藏了。
這些範例展示了如何使用 `v-for` 和 `key` 屬性來渲染列表,並結合條件渲染指令(`v-if`、`v-else` 和 `v-show`)來控制顯示的邏輯。希望這能幫助你更好地理解 Vue.js 中的這些概念!
# 四、總複習
示範如何利用 `v-model`、`ref`、`v-for`、`key`、`v-if + v-else` 和 `v-show` 來動態渲染和控制一個表格。
### 示例代碼
```vue!
<template>
<div>
<table border="1">
<thead>
<tr>
<th>名稱</th>
<th>年齡</th>
<th>職業</th>
<th>狀態</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="person in people" :key="person.id">
<td>{{ person.name }}</td>
<td>{{ person.age }}</td>
<td>{{ person.occupation }}</td>
<td>
<span v-if="person.isActive">Active</span>
<span v-else>Inactive</span>
</td>
<td>
<button @click="toggleStatus(person)">切換狀態</button>
</td>
</tr>
</tbody>
</table>
<div>
<label>
顯示訊息:
<input type="checkbox" v-model="showMessage">
</label>
<div v-show="showMessage">這是一個 v-show 範例的訊息</div>
</div>
<div>
<label>
新增人員:
<input type="text" v-model="newPerson.name" placeholder="名稱" ref="nameInput">
<input type="number" v-model="newPerson.age" placeholder="年齡">
<input type="text" v-model="newPerson.occupation" placeholder="職業">
<button @click="addPerson">新增</button>
</label>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const people = ref([
{ id: 1, name: 'John Doe', age: 30, occupation: 'Developer', isActive: true },
{ id: 2, name: 'Jane Smith', age: 25, occupation: 'Designer', isActive: false },
{ id: 3, name: 'Sam Johnson', age: 40, occupation: 'Manager', isActive: true }
]);
const showMessage = ref(true);
const newPerson = ref({
id: null,
name: '',
age: null,
occupation: '',
isActive: true
});
const toggleStatus = (person) => {
person.isActive = !person.isActive;
};
const addPerson = () => {
if (newPerson.value.name && newPerson.value.age && newPerson.value.occupation) {
newPerson.value.id = people.value.length + 1;
people.value.push({ ...newPerson.value });
newPerson.value.name = '';
newPerson.value.age = null;
newPerson.value.occupation = '';
newPerson.value.isActive = true;
ref('nameInput').value.focus();
}
};
</script>
<style>
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
}
</style>
```
### 邏輯說明
1. **表格結構**:
- 使用 `<table>` 元素來建立表格,並使用 `<thead>` 和 `<tbody>` 來分別定義表頭和表格內容。
2. **v-for 與 key**:
- 在 `<tbody>` 中,使用 `v-for` 指令來循環渲染 `people` 陣列中的每個物件。
- 使用 `:key="person.id"` 來唯一標識每個行,幫助 Vue 追蹤每個項目,從而優化重新渲染的過程。
3. **v-if + v-else**:
- 在每一行的狀態欄位中,使用 `v-if` 和 `v-else` 指令根據 `person.isActive` 的值來顯示 "Active" 或 "Inactive"。
4. **v-show**:
- 在表格下方,使用 `v-show="showMessage"` 來控制訊息的顯示。當 `showMessage` 為 `true` 時,訊息會顯示,否則訊息會被隱藏(但仍然在 DOM 中)。
5. **v-model**:
- 使用 `v-model` 雙向綁定來綁定顯示訊息的 checkbox 和新人的輸入字段。
6. **ref**:
- 使用 `ref` 來引用新增人員名稱輸入字段,以便在新增人員後自動聚焦該字段。
7. **切換狀態**:
- 在每一行的操作欄位中,設置一個按鈕,點擊時會調用 `toggleStatus` 函數切換 `person.isActive` 的狀態。
8. **新增人員**:
- 新增人員的輸入字段使用 `v-model` 進行綁定,點擊新增按鈕時會調用 `addPerson` 函數將新的人員加入 `people` 陣列中。
這個示例展示了如何結合 `v-model`、`ref`、`v-for`、`key`、`v-if + v-else` 和 `v-show` 來動態渲染和控制表格內容及其他元素的顯示狀態,並實現新增和狀態切換功能。
# 五、計算屬性 Computed
## 計算屬性 (computed) 簡單說明
在 Vue.js 中,計算屬性 (computed properties) 是一種根據已有的資料來自動計算出新的資料的屬性。這些屬性會根據依賴的資料自動更新,並且會被緩存 (只有當依賴的資料改變時才重新計算)。這樣可以讓我們的代碼更清晰、更簡潔,並避免不必要的重複計算。
### 範例代碼
這裡有幾個範例,示範如何使用計算屬性來處理不同的情況。
#### 基本範例:顯示全名
```vue
<template>
<div>
<p>全名: {{ fullName }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});
</script>
<style></style>
```
**邏輯說明**:
- `firstName` 和 `lastName` 是兩個反應式變量,分別代表名字和姓氏。
- `fullName` 是一個計算屬性,它根據 `firstName` 和 `lastName` 的值來自動生成全名。
#### 處理資料過於龐大的情況
```vue
<template>
<div>
<p>總數: {{ total }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const items = ref(Array.from({ length: 10000 }, (_, i) => i + 1));
const total = computed(() => {
return items.value.reduce((sum, item) => sum + item, 0);
});
</script>
<style></style>
```
**邏輯說明**:
- `items` 是一個包含 10000 個數字的陣列。
- `total` 是一個計算屬性,用來計算 `items` 中所有數字的總和。計算屬性會自動更新並且只在 `items` 改變時重新計算,從而提升性能。
#### 處理空值的情況
```vue!
<template>
<div>
<p>有效項目數: {{ validItemCount }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const items = ref([1, null, 3, undefined, 5, '', 7]);
const validItemCount = computed(() => {
return items.value.filter(item => item !== null && item !== undefined && item !== '').length;
});
</script>
<style></style>
```
**邏輯說明**:
- `items` 是一個包含數字和空值的陣列。
- `validItemCount` 是一個計算屬性,用來計算 `items` 中有效項目的數量(過濾掉 `null`、`undefined` 和空字符串)。
#### 處理資料類型問題的情況
```vue
<template>
<div>
<p>數字總和: {{ totalSum }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const mixedItems = ref(['1', 2, '3', 4, '5']);
const totalSum = computed(() => {
return mixedItems.value.reduce((sum, item) => sum + Number(item), 0);
});
</script>
<style></style>
```
**邏輯說明**:
- `mixedItems` 是一個包含數字和字串的陣列。
- `totalSum` 是一個計算屬性,用來計算 `mixedItems` 中所有項目的數字總和(將字串轉換為數字)。
#### 簡單說明
想像你有一本帳本,你每天都記錄收入和支出。你可以隨時計算出總餘額,但每次都要重新計算。計算屬性就像是幫你自動計算總餘額的工具,只要你更新收入或支出的資料,它就會自動更新總餘額,而你不需要每次手動計算。
#### 使用 get() 和 set() 的範例
在 Vue.js 中,計算屬性可以不僅用來讀取資料,還可以用來寫入資料,這時我們會使用 `get` 和 `set` 方法。
#### 範例:雙向綁定的全名
```vue
<template>
<div>
<p>全名: {{ fullName }}</p>
<p>
修改名字:
<input v-model="firstName" placeholder="名字">
<input v-model="lastName" placeholder="姓氏">
</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`;
},
set(newValue) {
const parts = newValue.split(' ');
firstName.value = parts[0];
lastName.value = parts[1] || '';
}
});
</script>
<style></style>
```
#### 邏輯說明
1. **初始變數**:
- `firstName` 和 `lastName` 是兩個反應式變數,分別代表名字和姓氏。
2. **計算屬性 fullName**:
- `fullName` 是一個計算屬性,包含 `get` 和 `set` 方法。
3. **get 方法**:
- 當你讀取 `fullName` 的時候,`get` 方法會返回名字和姓氏的組合,即 `${firstName.value} ${lastName.value}`。
4. **set 方法**:
- 當你修改 `fullName` 的時候,`set` 方法會被調用。這個方法接收新的全名作為參數,並把它拆分成名字和姓氏,然後分別更新 `firstName` 和 `lastName`。
#### 簡單總結
- **計算屬性**:用來根據其他資料的變化自動計算出新的資料,並且只有在依賴的資料改變時才重新計算。
- **get 方法**:用來定義計算屬性的讀取邏輯。
- **set 方法**:用來定義計算屬性的寫入邏輯,使得計算屬性可以雙向綁定。
這樣的設計讓我們可以在頁面上顯示和更新綁定的資料,而不需要手動處理複雜的計算和更新邏輯,使得程式碼更簡潔、易讀和可維護。
### 總結
- 計算屬性可以幫助我們從已有的資料中自動計算出新的資料。
- 它們會根據依賴的資料自動更新,並且會被緩存,只在依賴的資料改變時重新計算。
- 這讓我們的代碼更簡潔、更高效,<font color="red">避免不必要的重複計算</font>。
:::danger
Computed 只執行純計算,不要發出異步請求或在計算的 Getter 中改變 DOM!
它唯一的職責只有:<font color="red">**計算並返回該值。**</font>
:::
這些範例展示了計算屬性在處理不同情況下的應用,包括處理大量資料、處理空值、以及處理不同資料類型。通過這些範例,可以幫助理解如何在 Vue.js 中使用計算屬性來簡化和優化我們的代碼。
# 額外學習 - 副作用 Side-Effect
## 什麼是 Side-Effect
**Side-effect**,中文可以翻譯為「副作用」。
在程式設計中,副作用是指一段程式碼執行後,不僅僅是返回結果,還會影響外部的狀態或環境。這些外部影響就稱為副作用。
### 簡單說明
假設你在廚房煮飯,煮飯這個動作會有預期的結果,就是你會得到一鍋煮好的飯。但是,如果在煮飯的過程中,蒸汽弄濕了廚房的窗戶,這個弄濕窗戶的現象就是一個「副作用」。煮飯的主要目的是得到飯,而弄濕窗戶則是意料之外的影響。
### 副作用的應用
#### 例子 1:修改全局變數
假設我們有一個計數器函數,每次調用這個函數都會讓計數器加一。
```javascript
let counter = 0;
function incrementCounter() {
counter += 1; // 這裡就是副作用,因為它修改了外部變數 counter
}
incrementCounter();
console.log(counter); // 輸出 1
```
**應用場景**:
這種修改全局變數的副作用在許多情況下是必需的,比如在網頁應用中,當用戶點擊按鈕時,我們需要更新顯示在頁面上的點擊次數。
#### 例子 2:API 請求
假設我們有一個函數,用來向伺服器發送資料並獲取回應。
```javascript
function fetchData() {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log(data); // 這裡就是副作用,因為它處理了外部的 API 回應
});
}
fetchData();
```
**應用場景**:
這種副作用在需要與外部系統或服務交互時非常常見,比如從伺服器獲取資料、向伺服器發送表單資料等。
### 副作用的優點與缺點
**優點**:
1. **實用性**:副作用讓程式可以與外部世界互動,完成各種實用功能,比如更新網頁內容、與伺服器通訊、記錄日誌等。
2. **必要性**:許多功能依賴副作用,比如用戶輸入處理、狀態更新、計數等。
**缺點**:
1. **難以追蹤**:副作用會影響外部狀態,可能導致程式的行為難以預測和理解,特別是在大型系統中。
2. **測試困難**:有副作用的程式碼通常比較難以測試,因為它依賴於外部狀態或環境,測試時需要模擬這些外部條件。
3. **可重入性問題**:有副作用的函數在多次調用時,可能產生不同的結果,這會使程式的行為不穩定。
### 總結
副作用在程式設計中是不可避免的,因為我們常常需要程式與外部環境交互。理解和管理副作用是寫出可靠、可維護程式碼的重要技能。透過適當的設計模式和工具(如 Vue.js 的組件生命周期方法),我們可以有效地控制和測試副作用,從而提高程式的質量和可預測性。
# 六、監聽器 Watch、WatchEffect
## 什麼是監聽器 (Watch) 和 WatchEffect?
在 Vue 3 中,**監聽器 (watch)** 和 **WatchEffect** 是用來監控資料變化並對變化做出反應的工具。這些工具讓我們可以在資料發生改變時自動執行特定的操作。
### Watch 監聽器
`watch` 主要用來監聽指定的資料或計算屬性,當它們改變時執行一些操作。
### WatchEffect
`watchEffect` 會立即執行一次它所包含的函數,並自動追蹤函數內部所使用的所有反應式資料,當任何這些資料發生變化時再次執行這個函數。
### 業界常用範例及邏輯
#### 範例 1:表單驗證 (Watch)
假設我們有一個註冊表單,需要實時驗證用戶輸入的郵箱是否有效。
```vue
<template>
<div>
<input v-model="email" placeholder="輸入你的郵箱">
<p v-if="emailError">{{ emailError }}</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const email = ref('');
const emailError = ref('');
watch(email, (newEmail) => {
if (!newEmail.includes('@')) {
emailError.value = '郵箱格式不正確';
} else {
emailError.value = '';
}
});
</script>
```
**邏輯**:
- 監聽 `email` 變量,當用戶輸入新的郵箱時,檢查是否包含 '@' 符號。
- 如果格式不正確,設定錯誤訊息,否則清除錯誤訊息。
**為什麼要使用**:
- 可以實時檢查用戶輸入,提供即時反饋,提升用戶體驗。
#### 範例 2:資料同步 (WatchEffect)
假設我們需要同步一個反應式變量到本地儲存 (localStorage)。
```vue
<template>
<div>
<input v-model="username" placeholder="輸入你的名字">
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue';
const username = ref(localStorage.getItem('username') || '');
watchEffect(() => {
localStorage.setItem('username', username.value);
});
</script>
```
**邏輯**:
- 在 `username` 變量改變時,自動將新值儲存到 `localStorage`。
- 初始值從 `localStorage` 中讀取。
**為什麼要使用**:
- 簡化代碼,不需要明確指定要監聽哪些變量,只需在函數內使用即可自動監聽。
### 比較其他監聽方式
#### 1. `watch` vs `watchEffect`
- **`watch`**:
- 只監聽指定的變量。
- 需要明確指定監聽的變量。
- 適合監聽特定變量的變化,並執行複雜的操作。
- **`watchEffect`**:
- 自動監聽函數內使用的所有反應式變量。
- 立即執行一次。
- 適合快速反應的簡單操作,不需要明確指定變量。
**優缺點**:
- **`watch`** 的優點是精確控制,缺點是需要手動指定變量且若是遇到是物件是監聽不到的,因為他只能監聽到第一層。
監聽不到的情況:
- ```vue!
<template>
<div>
<input type="text" v-model="email.levels.email" >
</div>
</template>
<script setup>
import { ref } from 'vue';
import { watch } from '@vue/runtime-core';
const email = ref({
levels:{
email: '', // 會監聽不到
}
});
watch(email, (newEmail) => {
console.log(email, newEmail);
});
</script>
```
- 使其可監聽到的情況(增加 deep 屬性):
```vue!
<template>
<div>
<input type="text" v-model="email.levels.email" >
</div>
</template>
<script setup>
import { ref } from 'vue';
import { watch } from '@vue/runtime-core';
const email = ref({
levels:{
email: '',
}
});
watch(
email,
(newEmail) => {
console.log(email, newEmail);
},
{ deep: true }); // 增加 deep 屬性
</script>
```
但回傳的會是整個物件而不是只單純監聽 email 這個值,更好作法如下:
```vue!
<template>
<div>
<input type="text" v-model="email.levels.email" >
</div>
</template>
<script setup>
import { ref } from 'vue';
import { watch } from '@vue/runtime-core';
const email = ref({
levels:{
email: '',
}
});
watch(
() => email.value.levels.email, // <- 這樣寫之後
(newEmail) => {
console.log(email, newEmail);
},
{ deep: true }); // <- 就不必增加 deep 屬性
</script>
```
- **`watchEffect`** 的優點是簡便易用,缺點是可能不夠精確,對於複雜操作可能不夠靈活。
### 哪個比較常被使用?
- 在需要監控特定變量時,`watch` 比較常被使用,因為它提供了更精確的控制。
- 在需要快速反應並自動追蹤所有相關變量時,`watchEffect` 比較常被使用,因為它簡化了代碼。
### 其他替代方案
#### 1. Vuex
Vuex 是 Vue 的狀態管理模式,可以集中管理應用的所有狀態,並提供更複雜的狀態監控和變更機制。
**差異**:
- Vuex 更適合大型應用,需要集中管理狀態。
- `watch` 和 `watchEffect` 更適合小範圍的監控和反應。
**優缺點**:
- Vuex 的優點是集中化管理,缺點是學習成本較高。
- `watch` 和 `watchEffect` 的優點是簡單易用,缺點是在大型應用中可能不夠強大。
### 總結
- **`watch`**:用來監聽特定變量的變化,適合精確控制和複雜操作。
- **`watchEffect`**:自動監聽函數內使用的變量,適合簡便快速反應的操作。
- **Vuex**:用於大型應用的集中狀態管理,更強大但學習成本高。
不同的工具有不同的適用場景,選擇合適的工具可以讓我們的開發工作更加高效和可維護。
:::success
## 進階補充
### 什麼是 Vuex?
**Vuex** 是 Vue.js 的狀態管理庫,用於集中管理應用中的所有組件的狀態。它提供了一個全局的 store 對象,可以用來存儲共享的狀態,並提供了一些方法來操作這些狀態,使得狀態的變更和管理更加容易。
### 為什麼要使用 Vuex?
1. **集中管理**:Vuex 提供了一個集中的數據存儲,方便管理所有組件共享的狀態,使得應用的狀態管理更加清晰。
2. **組件通信**:通過 Vuex,組件之間可以方便地共享狀態,不需要通過 props 和事件來進行繁瑣的傳遞。
3. **便於調試**:Vuex 的狀態是響應式的,任何修改都會被監聽到,方便進行調試和追蹤。
### Vuex 的基本概念
1. **State**:存儲應用中的所有狀態,可以通過 `this.$store.state` 訪問。
2. **Getter**:類似於計算屬性,用來從狀態中計算出新的值,可以通過 `this.$store.getters` 訪問。
3. **Mutation**:唯一可以修改狀態的地方,但是必須是同步函數,可以通過 `this.$store.commit` 調用。
4. **Action**:用來執行非同步操作,可以通過 `this.$store.dispatch` 調用,可以提交 Mutation 來修改狀態。
5. **Module**:將 Store 拆分成多個模塊,每個模塊擁有自己的 state、getters、mutations 和 actions。
### 業界常用範例及邏輯
#### 範例 1:購物車管理
假設我們有一個購物車功能,需要在多個組件中共享購物車的狀態。
```javascript
// store/index.js
import { createStore } from 'vuex';
export default createStore({
state: {
cart: []
},
mutations: {
addToCart(state, product) {
state.cart.push(product);
},
removeFromCart(state, productId) {
state.cart = state.cart.filter(item => item.id !== productId);
}
},
actions: {
addToCart({ commit }, product) {
commit('addToCart', product);
},
removeFromCart({ commit }, productId) {
commit('removeFromCart', productId);
}
},
getters: {
cartItemCount: state => state.cart.length
}
});
```
**邏輯**:
- 在 state 中定義了一個 `cart` 陣列,用來存儲購物車中的商品。
- 通過 mutations 定義了兩個方法:`addToCart` 和 `removeFromCart`,用來增加和移除商品。
- 通過 actions 定義了兩個方法,用來提交 mutations。
- 通過 getters 定義了 `cartItemCount` 方法,用來計算購物車中的商品數量。
#### 範例 2:用戶登錄狀態管理
假設我們需要管理用戶的登錄狀態,並在多個組件中顯示不同的內容。
```javascript
// store/index.js
import { createStore } from 'vuex';
export default createStore({
state: {
user: null
},
mutations: {
setUser(state, user) {
state.user = user;
}
},
actions: {
login({ commit }, user) {
// 認證用戶,並獲取用戶信息
commit('setUser', user);
},
logout({ commit }) {
// 登出,清空用戶信息
commit('setUser', null);
}
},
getters: {
isLoggedIn: state => !!state.user
}
});
```
**邏輯**:
- 在 state 中定義了一個 `user` 變量,用來存儲用戶信息。
- 通過 mutations 定義了一個 `setUser` 方法,用來設置用戶信息。
- 通過 actions 定義了兩個方法:`login` 和 `logout`,分別用來處理用戶登錄和登出的操作。
- 通過 getters 定義了 `isLoggedIn` 方法,用來判斷用戶是否已經登錄。
### 替代方式
#### 1. Composition API
Vue 3 的 Composition API 提供了一種新的方式來組織組件的邏輯。它允許我們將組件的邏輯按照功能切分成多個獨立的片段,並在需要時將它們組合在一起。Composition API 與 Vuex 不同,它更加靈活,可以更好地管理組件的狀態和行為。
**差異**:
- Vuex 是一個狀態管理庫,用於集中管理應用的狀態。
- Composition API 是 Vue 3 中的一個特性,用於組織組件的邏輯,包括狀態管理、生命週期鉤子等。
**優缺點**:
- Vuex 適用於大型應用
- Composition API:適合中小型應用的靈活組件邏輯管理,易於使用,但在管理全局狀態時可能不夠強大。
選擇合適的工具取決於應用的規模和需求,對於大型應用,Vuex 提供了強大的集中管理能力,而對於中小型應用,Composition API 提供了更靈活的狀態和邏輯管理方式。
:::
# 七、父子組件溝通、傳資料
## 什麼是父子組件溝通、傳資料
當我們在講父子組件之間的溝通和傳遞資料的時候,可以想像成父母和孩子之間的對話和互動。這裡有幾個重要的概念:
### 父組件給子組件傳遞資料
這就像父母給孩子一些指示或信息。具體來說,這叫做 "props"(屬性)。父母(父組件)可以通過 `props` 來給孩子(子組件)傳遞信息。例如,父母可能會告訴孩子今天的作業是什麼,這些作業就是用 `props` 傳遞的。
#### 實際應用
- **情境**:你和爸爸在家裡。爸爸給你一個籃子,裡面裝著蘋果。你拿到籃子後,可以看見裡面的蘋果。
父組件:
```html
<template>
<ChildComponent :apples="appleCount" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
// 爸爸準備的蘋果數量
const appleCount = 5;
</script>
<style scoped>
</style>
```
子組件:
```html
<template>
<div>爸爸給了我 {{ apples }} 個蘋果。</div>
</template>
<script setup>
import { defineProps } from 'vue';
// 接收爸爸傳來的蘋果數量
const props = defineProps({
apples: Number
});
</script>
<style scoped>
</style>
```
- **說明**:
- 父組件:爸爸準備了5個蘋果,並將它們放入籃子(appleCount)。
- 子組件:你接收到籃子,並可以看到裡面有5個蘋果(透過defineProps來接收apples)。
### 子組件給父組件傳遞資料
這就像孩子向父母匯報情況或要求幫助。子組件通過觸發事件(emitting events)來告訴父組件一些信息。這些事件就像孩子的喊話或通知,父母聽到後可以做出反應。
#### 實際應用
- **情境**:你在外面買了一些東西,想告訴爸爸你買了什麼。
子組件:
```html
<template>
<button @click="buyItem">買東西</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['itemBought']);
const buyItem = () => {
const item = '蘋果';
emit('itemBought', item);
};
</script>
<style scoped>
</style>
```
父組件:
```html
<template>
<ChildComponent @itemBought="handleItemBought" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const handleItemBought = (item) => {
console.log('孩子買了:', item);
};
</script>
<style scoped>
</style>
```
- **說明**:
- 父組件:爸爸等待你的消息,並在你告訴他後(透過handleItemBought),記錄你買了什麼。
- 子組件:你買了一個蘋果,並透過defineEmits告訴爸爸你買了什麼(itemBought事件)。
### 父子組件雙向綁定
在 Vue 3 中,雙向綁定可以通過 v-model 來實現,就像父母和孩子之間的互動是雙向的。例如,孩子在選擇的過程中,不僅需要父母給予建議,父母也需要及時知道孩子的進展。
#### 實際應用
- **情境**:你和爸爸討論要不要買某個東西,爸爸給你建議,你也告訴爸爸你的想法。
子組件:
```html
<template>
<input v-model="localItem" @input="notifyParent" />
</template>
<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue';
const props = defineProps({
suggestedItem: String
});
const emit = defineEmits(['updateItem']);
const localItem = ref(props.suggestedItem);
watch(() => props.suggestedItem, (newVal) => {
localItem.value = newVal;
});
const notifyParent = () => {
emit('updateItem', localItem.value);
};
</script>
<style scoped>
</style>
```
父組件:
```html
<template>
<ChildComponent :suggestedItem="item" @updateItem="updateItem" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
import { ref } from 'vue';
const item = ref('書包');
const updateItem = (newItem) => {
item.value = newItem;
};
</script>
<style scoped>
</style>
```
- **說明**:
- 父組件:爸爸給你一個建議(item),並等待你的反饋。
- 子組件:你根據爸爸的建議,提出了自己的意見(localItem),並通知爸爸(updateItem事件)。
- 雙向傳遞:爸爸和你之間能夠互相影響彼此的決定。
### 總結
1. **父組件給子組件傳資料**:通過 `props`,像父母給孩子指示。
2. **子組件給父組件傳資料**:通過觸發事件 `emit`,像孩子向父母匯報。
3. **雙向綁定**:通過 `v-model`,實現父母和孩子之間的雙向互動。
**這些機制讓組件之間能夠方便地進行溝通和協作,就像家庭成員之間的交流一樣。**
:::success
### 小補充: `defineProps` 和 `defineEmits`。
### defineProps
#### 簡易說明
`defineProps` 就像是爸爸給你一個禮物盒,裡面有東西(資料)。你可以打開禮物盒,拿到裡面的東西。
#### 比喻
- **情境**:爸爸把一個裝滿糖果的禮物盒給你。
- **解釋**:`defineProps` 就是用來接收父組件(爸爸)傳給子組件(你)的資料(糖果)。
### defineEmits
#### 簡易說明
`defineEmits` 就像是你在家裡,告訴爸爸你做了某件事。這樣爸爸就知道發生了什麼。
#### 比喻
- **情境**:你告訴爸爸你完成了作業。
- **解釋**:`defineEmits` 就是用來讓子組件(你)通知父組件(爸爸)某件事情已經發生(你完成了作業)。
### 具體範例
利用上面的比喻轉換成代碼。
#### defineProps 例子
**父組件 (ParentComponent.vue)**
```html
<template>
<ChildComponent :candies="5" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
<style scoped>
</style>
```
**子組件 (ChildComponent.vue)**
```html
<template>
<div>爸爸給了我 {{ candies }} 顆糖果。</div>
</template>
<script setup>
import { defineProps } from 'vue';
// 接收爸爸傳來的糖果數量
const props = defineProps({
candies: Number
});
</script>
<style scoped>
</style>
```
- **說明**:爸爸(父組件)給了你(子組件)5顆糖果,子組件透過 `defineProps` 接收這些糖果。
#### defineEmits 例子
**父組件 (ParentComponent.vue)**
```html
<template>
<ChildComponent @homeworkDone="handleHomeworkDone" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const handleHomeworkDone = () => {
console.log('孩子完成了作業');
};
</script>
<style scoped>
</style>
```
**子組件 (ChildComponent.vue)**
```html
<template>
<button @click="finishHomework">完成作業</button>
</template>
<script setup>
import { defineEmits } from 'vue';
// 告訴爸爸作業完成了
const emit = defineEmits(['homeworkDone']);
const finishHomework = () => {
emit('homeworkDone');
};
</script>
<style scoped>
</style>
```
- **說明**:你(子組件)完成了作業,並透過 `defineEmits` 通知爸爸(父組件)作業完成了。
:::
# 八、組件生命週期

## 什麼是組件的生命週期
> 想像你在種一棵樹,這棵樹從種子到成長,再到結果,最後到落葉凋零,整個過程就像是組件的生命週期。
### 1. **種子階段:`setup`**
- **比喻**:這是你剛剛把種子種到土裡,開始準備養分和水分。
- **解釋**:這個階段是組件初始化的階段,我們在這裡設置初始資料和需要的變數。
### 2. **發芽階段:`onBeforeMount`**
- **比喻**:這是種子開始發芽,但還沒長出地面。
- **解釋**:組件即將被掛載到頁面,但還沒有真正顯示出來。
### 3. **成長階段:`onMounted`**
- **比喻**:這是小樹苗破土而出,開始在陽光下生長。
- **解釋**:組件已經被掛載到頁面,已經可以看到並開始與用戶互動。
### 4. **開花階段:`onBeforeUpdate`**
- **比喻**:樹苗開始長出花苞,但花還沒有完全開放。
- **解釋**:組件中的數據發生變化,DOM 即將更新,但還沒有開始更新。
### 5. **結果階段:`onUpdated`**
- **比喻**:花苞開放並結出了果實。
- **解釋**:組件的DOM已經更新完成,界面顯示出最新的數據和狀態。
### 6. **落葉階段:`onBeforeUnmount`**
- **比喻**:果實被採摘,葉子開始掉落。
- **解釋**:組件即將從頁面上被移除,這是做一些清理工作的好時機。
### 7. **凋零階段:`onUnmounted`**
- **比喻**:樹木已經凋零,回歸大地。
- **解釋**:組件已經從頁面上被移除,所有的事件監聽和資料都被清理掉。
### 具體使用方式
在 Vue 中,這些生命週期方法可以通過 `setup` 函數來使用,例如:
```javascript
import { onMounted, onBeforeUnmount } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('樹苗已經長出來了');
});
onBeforeUnmount(() => {
console.log('樹苗即將凋零');
});
}
};
```
### 總結
- **種子階段 (`setup`)**:初始化組件。
- **發芽階段 (`onBeforeMount`)**:組件即將掛載。
- **成長階段 (`onMounted`)**:組件已掛載。
- **開花階段 (`onBeforeUpdate`)**:組件數據即將更新。
- **結果階段 (`onUpdated`)**:組件數據已更新。
- **落葉階段 (`onBeforeUnmount`)**:組件即將卸載。
- **凋零階段 (`onUnmounted`)**:組件已卸載。
這些階段就像一棵樹從種子到凋零的過程,對應著Vue3組件的誕生、成長和結束。
生命週期詳細說明以及其他Hook應用請參考官網:
1. [Lifecycle Hooks](https://zh-hk.vuejs.org/api/composition-api-lifecycle.html)
2. [Composition API: Lifecycle Hooks for Vue3](https://zh-hk.vuejs.org/api/composition-api-lifecycle.html)
:::success
## **額外小補充**:Vue3 和 Vue2 的差異,以及它們在應用和邏輯上的不同之處。
### Vue3 vs Vue2
#### 1. 設計和結構
- **Vue2**:就像一個老房子,所有房間和功能都已經固定,你只能在特定的地方做改動。你需要遵循特定的規則來添加或修改房間(功能)。
- **Vue3**:就像一個新建的模塊化房子,你可以自由選擇和組裝不同的模塊,根據需要來定制你的房子(功能)。這使得設計和擴展變得更靈活。
#### 2. Composition API
- **Vue2**:使用的是 Options API,就像是按照菜譜(options)來做菜,你需要按順序把材料放入指定的位置。
- **Vue3**:引入了 Composition API,這就像是你自己決定如何組合材料來做出你想要的菜,讓你可以更靈活地組織代碼和功能。
#### 3. 性能提升
- **Vue2**:性能已經不錯,但在大型應用中可能會有些瓶頸,就像一輛普通的轎車,平時開起來很舒服,但在需要高性能時可能不夠快。
- **Vue3**:性能進一步優化,尤其是初始化和更新速度,就像是一輛跑車,無論是日常使用還是高性能需求都能應對自如。
#### 4. TypeScript 支持
- **Vue2**:支持 TypeScript,但需要額外的配置,就像是你可以在普通的房子里安裝智能家居系統,但需要一些改造。
- **Vue3**:原生支持 TypeScript,這就像是新房子一開始就設計了智能家居系統,你只需要直接使用即可。
### 實際應用和邏輯
#### 1. 組件的組織
**Vue2**:
```javascript
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
```
**Vue3**:
```javascript
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}
}
```
- **說明**:
- Vue2:你需要把所有的資料和方法放在特定的區域(`data` 和 `methods`)。
- Vue3:你可以直接在 `setup` 函數中定義資料和方法,這樣使得代碼更加集中和易於理解。
#### 2. 更好的邏輯重用
**Vue2**:
使用 Mixins 來重用邏輯,但可能會出現命名衝突。
**Vue3**:
使用 Composition API 來重用邏輯,可以創建可重用的函數組合,避免了命名衝突。
- **比喻**:
- Vue2:像是在做菜時,把相同的調料放在不同的地方,可能會出現混淆。
- Vue3:你可以創建一個調料包,隨時可以拿出來用,避免混淆。
#### 3. 減少了大量的 Vue 特定語法
**Vue2**:
```javascript!
export default {
name: 'App', //組件名稱
components: {}, // 其他直屬組件
data: () => ({}), // 組件內的資料(Data)變數有哪些
props: {
msg: String
},
computed: {
reversedMsg() {
return this.msg.split('').reverse().join('')
}
}, // 存放計算屬性
methods: {}, // 存放function
}
```
**Vue3**:
```javascript!
import { defineProps, computed } from 'vue'
export default {
setup() {
const props = defineProps(['msg'])
const reversedMsg = computed(() => props.msg.split('').reverse().join(''))
return {
reversedMsg
}
}
}
```
- **說明**:
- Vue2:需要用 Vue 特定的語法來定義 `props` 和 `computed`。
- Vue3:使用 JavaScript 的語法,更加簡潔和直觀。
### 總結
- **靈活性**:Vue3 提供了更靈活的 Composition API,讓開發者可以更自由地組織代碼。
- **性能**:Vue3 在性能上有顯著提升,適合大型應用。
- **TypeScript 支持**:Vue3 原生支持 TypeScript,對於需要嚴格類型檢查的項目更加友好。
- **可重用性**:Vue3 的 Composition API 提供了更好的邏輯重用方式,避免了 Vue2 中的命名衝突問題。
這些改進使得 Vue3 比 Vue2 更加適應現代前端開發需求,並且更容易維護和擴展。
:::
# 九、插槽 Slot
## Slot 插槽的比喻
想像你在家裡舉辦一個派對,邀請了很多朋友。你準備了一個裝飾品架子(Slot),但是你讓每個朋友自由地決定要放什麼東西在架子上。這樣每個朋友都可以展示他們帶來的特別物品。
### Slot 插槽的概念
- **父組件**:派對的主辦者(你),準備了架子(插槽)。
- **子組件**:你的朋友,他們帶來了自己的物品並放到架子上。
### 基本插槽(Default Slot)
#### 情境
你準備了一個空白的架子(插槽),任何人都可以把任何東西放上去。
#### 例子
**父組件 (ParentComponent.vue)**
```html
<template>
<div>
<h1>歡迎來到我的派對</h1>
<Decoration>
<p>這是朋友A帶來的花瓶</p>
</Decoration>
</div>
</template>
<script setup>
import Decoration from './Decoration.vue';
</script>
```
**子組件 (Decoration.vue)**
```html
<template>
<div class="decoration">
<slot></slot>
</div>
</template>
```
#### 邏輯
- **父組件**:你(主辦者)準備了一個架子(Decoration),並讓朋友A放上了一個花瓶(`<p>這是朋友A帶來的花瓶</p>`)。
- **子組件**:架子(Decoration)上有一個插槽(`<slot></slot>`),可以顯示父組件放入的任何內容。
### 具名插槽(Named Slot)
#### 情境
你準備了一個架子,上面有兩個標籤:「左邊」和「右邊」,讓朋友決定要在左邊還是右邊放東西。
#### 例子
**父組件 (ParentComponent.vue)**
```html
<template>
<div>
<h1>歡迎來到我的派對</h1>
<Decoration>
<template v-slot:left>
<p>這是朋友A帶來的花瓶</p>
</template>
<template v-slot:right>
<p>這是朋友B帶來的畫</p>
</template>
</Decoration>
</div>
</template>
<script setup>
import Decoration from './Decoration.vue';
</script>
```
**子組件 (Decoration.vue)**
```html
<template>
<div class="decoration">
<div class="left">
<slot name="left"></slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
</template>
```
#### 邏輯
- **父組件**:你準備了一個架子(Decoration),並決定了兩個區域:「左邊」和「右邊」。
- 在「左邊」放上了朋友A的花瓶(`<template v-slot:left>...</template>`)。
- 在「右邊」放上了朋友B的畫(`<template v-slot:right>...</template>`)。
- **子組件**:架子上有兩個具名插槽(`<slot name="left"></slot>` 和 `<slot name="right"></slot>`),顯示父組件指定的內容。
### 作用域插槽(Scoped Slot)
#### 情境
你準備了一個架子,並且給每個來參加派對的朋友一個標籤,讓他們知道他們可以在架子的特定區域展示他們的東西。
#### 例子
**父組件 (ParentComponent.vue)**
```html
<template>
<div>
<h1>歡迎來到我的派對</h1>
<Decoration>
<template v-slot:default="slotProps">
<p>這是朋友{{ slotProps.name }}帶來的{{ slotProps.item }}</p>
</template>
</Decoration>
</div>
</template>
<script setup>
import Decoration from './Decoration.vue';
</script>
```
**子組件 (Decoration.vue)**
```html
<template>
<div class="decoration">
<slot :name="friendName" :item="friendItem"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue';
// 模擬的數據
const friendName = ref('A');
const friendItem = ref('花瓶');
</script>
```
#### 邏輯
- **父組件**:你準備了一個架子(Decoration),並透過作用域插槽(`<template v-slot:default="slotProps">...</template>`)獲取來自子組件的資料(`slotProps`),然後決定顯示的內容。
- 使用這些資料來展示朋友A帶來的花瓶(`這是朋友{{ slotProps.name }}帶來的{{ slotProps.item }}`)。
- **子組件**:架子上有一個插槽,並提供了資料(`friendName` 和 `friendItem`),讓父組件決定如何顯示這些資料。
### 總結
- **基本插槽(Default Slot)**:父組件決定放入什麼內容,子組件只提供一個空間來顯示這些內容。
- **具名插槽(Named Slot)**:父組件可以指定不同的內容放到子組件的不同區域,子組件有多個命名的空間來顯示這些內容。
- **作用域插槽(Scoped Slot)**:子組件提供資料給父組件,父組件根據這些資料決定顯示的內容。
這些插槽就像是派對上的架子,你可以決定架子上的哪些區域展示什麼物品,或是根據朋友帶來的物品來決定如何展示。
# 十、v-bind指令
### 什麼是 v-bind?
在 Vue.js 中,`v-bind` 是一個指令,用來綁定元素的屬性或 HTML 特性(attribute)與 Vue 實例的數據。這樣可以讓數據驅動的應用更靈活和動態。
#### 簡單的說:
`v-bind` 就像是把程式中的變數(數據)和 HTML 元素的屬性連接在一起。這樣當變數的值改變時,HTML 元素也會自動更新,顯示最新的值。
### v-bind 的語法
基本語法是 `v-bind:attribute="expression"`,其中 `attribute` 是 HTML 元素的屬性,`expression` 是 Vue 實例中的數據或變數。
### 舉例說明
#### 例子1:動態改變圖片的來源
假設我們有一個圖片的 URL 存在 Vue 的數據中,我們可以用 `v-bind` 來動態改變這個圖片的來源。
```html
<!DOCTYPE html>
<html>
<head>
<title>v-bind 範例</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
<div id="app">
<img v-bind:src="imageUrl" alt="Dynamic Image">
<!-- 也可以使用:src="imageUrl" -->
</div>
<script>
new Vue({
el: '#app',
data: {
imageUrl: 'https://example.com/image1.jpg'
}
});
</script>
</body>
</html>
```
在這個例子中,當 `imageUrl` 變數的值改變時,圖片的來源也會自動更新。
#### 例子2:動態設置按鈕的禁用狀態
我們可以使用 `v-bind` 來根據某個條件動態設置按鈕的禁用狀態。
```html
<!DOCTYPE html>
<html>
<head>
<title>v-bind 範例</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
<div id="app">
<button v-bind:disabled="isButtonDisabled">Click Me!</button>
<button @click="toggleButton">Toggle Button State</button>
</div>
<script>
new Vue({
el: '#app',
data: {
isButtonDisabled: true
},
methods: {
toggleButton() {
this.isButtonDisabled = !this.isButtonDisabled;
}
}
});
</script>
</body>
</html>
```
在這個例子中,`isButtonDisabled` 變數控制著按鈕是否被禁用。點擊 "Toggle Button State" 按鈕可以切換按鈕的狀態。
### 總結
`v-bind` 是 Vue.js 提供的一個指令,用來將 Vue 實例中的數據綁定到 HTML 元素的屬性上。這樣當數據改變時,HTML 元素也會自動更新顯示最新的值。透過 `v-bind`,我們可以讓應用更具動態和交互性。
# 整份自學筆記學習來源:[Vue3 基礎教學](https://youtube.com/playlist?list=PLSCgthA1AnifSzKdpV4FWq1pLVF4FbZ4K&si=IodxlYKxTc5k013Y) - [老師:Proladon](https://www.youtube.com/@Proladon)