# 🏅 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) 等功能。 ![day16-1](https://hackmd.io/_uploads/BJIzjTYGJx.png) ## 插件目錄 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 是否需要限制插件的執行環境(伺服器端或客戶端,或是無需限制)。 ![day16-2](https://hackmd.io/_uploads/SyLSjTtfye.png) ## 回報流程 將答案上傳至 GitHub 並複製 GitHub repo 連結貼至底下回報就算完成了喔 ! 解答位置請參考下圖(需打開程式碼的部分觀看) ![](https://i.imgur.com/vftL5i0.png) <!-- 解答 : 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]()| -->