# 🏅 Day 16 - Nuxt3 插件 ( Plugins ) - provide
## 今日學習目標
- 學習建立插件的基本結構
- 學習使用插件的 provide 方法建立全域的輔助函式 ( helper )
- 理解插件的執行環境設置 ( client / server )
## 前言
在開發的過程中,經常需要將第三方套件整合到專案中以節省開發時間。Nuxt 官方提供了一些與 Nuxt3 整合的模組,例如 [**Pinia**](https://nuxt.com/modules/pinia) 和 [**Tailwind CSS**](https://nuxt.com/modules/tailwindcss)。如果我們需要使用的套件沒有被整合進 Nuxt3 模組,就可以利用 Nuxt 的 Plugin 功能來將這些外部套件添加到 Nuxt 中。
Nuxt3 插件功能提供了 `provide`、`directive` 和 `use` 三種建立方式。今天將專注於介紹插件的基本結構和 `provide` 的實作,未來將會進一步探討 `directive` 和 `use` 的使用。
## 建立插件的基本結構
Nuxt3 插件必需放置在 **`plugins/`** 資料夾內。Nuxt 會在應用程式啟動時,自動掃描並註冊該資料夾中第一層的所有插件。可以使用 `npx nuxi add plugin 插件檔案名稱` 指令建立插件,產生一個包含 `defineNuxtPlugin()` 方法的檔案。`defineNuxtPlugin()` 會接收一個傳入 `nuxtApp` 參數的函式,我們可以透過這個參數來實現插件的功能。
```jsx
export default defineNuxtPlugin(nuxtApp => {
// 使用 nuxtApp 參數實作功能
console.log(nuxtApp);
})
```
### nuxtApp 參數
`nuxtApp` 參數是 Nuxt 的 [實體 (Instance)](https://nuxt.com/docs/guide/going-further/internals#the-nuxtapp-interface) ,允許我們操作 Nuxt 的全域功能 ( 如下圖 ) ,例如 [Vue 的全域實體 `vueApp`](https://vuejs.org/api/application.html) 和 [nuxtApp.provide()](https://nuxt.com/docs/api/composables/use-nuxt-app#provide-name-value) 等功能。

## 插件目錄
Nuxt 只會自動註冊 `plugins/` 資料夾第一層的插件,位於子目錄中的插件不會被自動註冊。例如下方檔案結構中只有 `pluginA.js` 會被自動註冊,而 `admin/pluginB.js` 則不會。
```
plugins/
├── admin/
│ └── pluginB.js
└── pluginA.js
```
如果希望註冊子目錄的 `admin/pluginB.js` 插件,需要在 **`nuxt.config.ts`** 中使用 [**`plugins`**](https://nuxt.com.cn/docs/api/nuxt-config#plugins-1) 屬性明確指定:
```jsx
export default defineNuxtConfig({
plugins: [
"~/plugins/admin/pluginB", // ~ 路徑指向 /<srcDir>
]
})
```
## 插件的執行環境
插件預設在伺服器端和客戶端都可以被執行。如果需要限制插件的執行環境,可以在檔案名稱中加入後綴 `.client` 或 `.server` 。
```jsx
plugins/
├── pluginC.js // 在伺服器和客戶端均執行
├── pluginD.client.js // 僅在客戶端執行
├── pluginE.server.js // 僅在伺服器端執行
```
## 建立輔助函式
學習完插件的建立方式、`nuxtApp` 參數的用法以及執行環境之後,接下來將介紹如何使用 `nuxtApp.provide()` 方法來實作**全域輔助函式**。
### 建立方式
輔助函式的建立方式有兩種。第一種方式是使用 `nuxtApp.provide()` 方法在第一個參數放入字串格式的名稱,第二個參數放入函式並回傳自定義的資料格式。第二種方式是回傳一個包含 `provide` 屬性的物件,`provide`物件內的屬性名稱為輔助函式的名稱,屬性值放入執行的函式。例如下方的範例,使用了 `nuxtApp.provide()` 或是 return provide 物件的方式建立名稱為 'example' 的輔助函式。
```jsx
// plugins/example.js
export default defineNuxtPlugin(nuxtApp => {
// 方法一 : nuxtApp.provide(輔助函式名稱,執行的輔助函式);
// 如果只需要在插件中定義一個輔助函式,nuxtApp.provide() 更方便的方式。
nuxtApp.provide('example', (date) => `${date} | 使用 Nuxt3 plugin 功能實作全域helper`);
// 方法二.
// 若需要定義多個輔助函式,或想要方便管理可以使用 return 方式,將所有輔助函式放入 `provide` 物件內
return {
provide: {
// 輔助函式的功能都放在 provide 物件內
// 建立名稱為 example 的輔助函式
example: (date) => `${date} | 使用 Nuxt3 plugin 功能實作全域 helper`
}
};
})
```
### 使用方式
執行時會將 `defineNuxtPlugin()` 定義的輔助函式寫入**全域** `NuxtApp` 的執行環境,在所有元件的`<script setup></script>` 中都能夠透過 [useNuxtApp() 函式](https://nuxt.com/docs/api/composables/use-nuxt-app#provide-name-value) 取出。接續上方建立的 'example' 插件,在 `/pages/index.vue` 頁面中可以使用 `const { $example } = useNuxtApp()` 的方式解構出函式,然後在 `<template></template>` 或是 `<script setup></script>` 內執行。
```jsx
// /pages/index.vue
<script setup>
const { $example } = useNuxtApp()
// 也可以在 JS 區塊執行
console.log($example('Day16'))
</script>
<template>
<h1>在模板內執行</h1>
<div>
{{ $example('Day16') }}
</div>
</template>
```
>❗ 注意 : 所有使用插件定義的輔助函式在元件中取用都需要加上錢字符號 `$` ,例如 `$example()`
### 實作 : 第三方套件加入輔助函式
除了自訂的輔助函式以外,也可以把外部套件的方法加入全域的輔助函式。以 sweetAlert2 為例,在 `plugins/sweetalert2.js` 加入了 showAlert 函式。
```jsx
// plugins/sweetalert2.js
import Swal from "sweetalert2";
function showAlert({
title = "Good job!",
text = "You clicked the button!",
icon = "success",
showCloseButton = true,
showCancelButton = true,
...otherParameters
} = {}) {
Swal.fire({
title,
text,
icon,
showCloseButton,
showCancelButton,
...otherParameters,
});
}
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.provide("showAlert", showAlert);
});
```
然後在頁面元件的模板或是 JavaScript 都可以執行 `$showAlert` 開啟提示視窗 :
```jsx
<script setup>
const { $showAlert } = useNuxtApp();
</script>
<template>
<h2>開啟 sweetAlert2</h2>
<button
@click="$showAlert({ showDenyButton: true, denyButtonText: `Don't save` })">
開啟
</button>
</template>
```
<br>
> 今日學習的[範例 Code - 資料夾: day16-plugin-provide-example](https://github.com/hexschool/nuxt-daily-tasks-2024)
## 題目
請 fork 這一份 [模板](https://github.com/jasonlu0525/nuxt3-live-question/tree/day16-plugin-provide) ,完成以下條件 :
- 使用 Nuxt3 Plugin 功能在 `/plugins/bootstrap.js` 引入 `bootstrap5 v5.3.3` 版本`Offcanvas` 與 `Modal` 元件的 JavaScript 功能,並將 `new bootstrap.Offcanvas()` 與 `new bootstrap.Modal()` 提供為全域輔助函式。( [Modal 官方文件](https://getbootstrap.com/docs/5.3/components/modal/#methods) 、 [Offcanvas 官方文件](https://getbootstrap.com/docs/5.3/components/offcanvas/#how-it-works))
```jsx
// 加入全域輔助函式
new bootstrap.Offcanvas(element, options);
new bootstrap.Modal(element, options);
```
- 在 `/pages/index.vue` 中,從 Plugin 取出 `Offcanvas` 和 `Modal` 並在 onMounted 生命週期初始化元件。使用 `<template>` 中的按鈕,透過 `@click` 事件來操作元件的 `.show()` 和 `.hide()` 方法,以控制元件的開啟與關閉。
- bootstrap5 SCSS 在模板中已經有提供,不需額外設定。
- 請根據下圖 bootstrap5 v5.3.3 Modal 功能的 [原始碼](https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.js) 判斷 Plugin 是否需要限制插件的執行環境(伺服器端或客戶端,或是無需限制)。

## 回報流程
將答案上傳至 GitHub 並複製 GitHub repo 連結貼至底下回報就算完成了喔 !
解答位置請參考下圖(需打開程式碼的部分觀看)

<!--
解答 : https://github.com/jasonlu0525/nuxt3-live-answer/tree/day16-plugin-provide
-->
回報區
---
| # | Discord | Github / 答案 |
| --- | ----- | ----- |
|1|眼睛|[Github](https://github.com/Thrizzacode/nuxt3-live-question/tree/day16-plugin-provide)|
|2|LinaChen|[Github](https://github.com/Lina-SHU/nuxt3-live-question)|
| 3 | Steven |[Github](https://github.com/y7516552/nuxt3-live-question/tree/day16)|
| 4 | Rocky |[Github](https://github.com/WuRocky/Nuxt-Day15-Plugins-provide.git)|
| 5 | MY |[Github](https://github.com/ahmomoz/nuxt3-live-question/tree/day16-plugin-provide-hw)|
| 6 | hsin yu |[Github](https://github.com/dogwantfly/nuxt3-daily-task-live-question/tree/day16-plugin-provide)|
| 7 | dragon |[Github](https://github.com/peterlife0617/2024-nuxt-training-homework01/tree/feature/day16)|
| 8 | Jim Lin |[Github](https://github.com/junhoulin/Nuxt3-hw-day-after10/tree/day16)|
| 9 | tanuki狸 |[Github](https://github.com/tanukili/Nuxt-2024-week01-2/tree/day16-plugin-provide)|
|10|Ariel|[Github](https://github.com/Ariel0508/nuxt3-hw/tree/day16-plugin-provide)|
|11|Johnson|[Github](https://github.com/tttom3669/2024_hex_nuxt_daily/tree/day16-plugin-provide)|
|12| 阿塔 |[Github](https://github.com/QuantumParrot/2024-Hexschool-Nuxt-Camp-Daily-Task)|
<!--
|---|---|[Github]()|
-->