Quasar 應用程式的一個常見用例是在**根 Vue 應用程式實例化之前執行程式碼**,例如注入和初始化您自己的依賴項(範例:Vue 元件、函式庫…)或僅僅配置應用程式的一些啟動程式碼。
由於您無法存取任何 /main.js
檔案(以便 Quasar CLI 可以無縫地初始化和建置 SPA/PWA/SSR/Cordova/Electron 的相同程式碼庫),Quasar 提供了一個優雅的解決方案,允許使用者定義所謂的啟動檔案。
在早期的 Quasar 版本中,若要在根 Vue 實例化之前執行程式碼,您可以修改 /src/main.js
檔案並加入任何您需要執行的程式碼。
這種方法有一個主要問題:隨著專案的成長,您的 main.js
檔案很可能變得雜亂且難以維護,這與 Quasar 鼓勵開發人員編寫可維護且優雅的跨平台應用程式的概念背道而馳。
透過啟動檔案,您可以將每個依賴項拆分為獨立、易於維護的檔案。停用任何啟動檔案,甚至透過 /quasar.config
檔案配置上下文判斷哪些啟動檔案加入建置中,也變得輕而易舉。
啟動檔案的結構
啟動檔案是一個簡單的 JavaScript 檔案,它可以選擇性地匯出一個函式。Quasar 將在啟動應用程式時呼叫匯出的函式,並額外將**一個物件**連同以下屬性傳遞給該函式
屬性名稱 | 描述 |
---|---|
app | Vue 應用程式實例 |
router | 來自 'src/router/index.js' 的 Vue Router 實例 |
store | Pinia 或 Vuex store 的實例 - **如果您的專案使用 Pinia(您有 src/stores)或 Vuex(您有 src/store),則只會傳遞 store** |
ssrContext | 僅在伺服器端可用,如果為 SSR 建置。更多資訊 |
urlPath | URL 的路徑名稱(路徑 + 搜尋)部分。它也包含用戶端上的雜湊值。 |
publicPath | 已配置的公開路徑。 |
redirect | 用於重新導向到另一個 URL 的函式。接受字串(完整 URL)或 Vue Router 位置字串或物件。 |
export default ({ app, router, store }) => {
// something to do
}
啟動檔案也可以是非同步的
export default async ({ app, router, store }) => {
// something to do
await something()
}
您可以使用 boot
輔助函式包裝傳回的函式,以獲得更好的 IDE 自動完成體驗(透過 Typescript)
import { boot } from 'quasar/wrappers'
export default boot(async ({ app, router, store }) => {
// something to do
await something()
})
請注意,我們正在使用 ES6 解構賦值。僅賦值您實際需要/使用的內容。
您可能會問自己為什麼需要匯出函式。這實際上是可選的,但在您決定移除預設匯出之前,您需要了解何時需要它
// Outside of default export:
// - Code here gets executed immediately,
// - Good place for import statements,
// - No access to router, Vuex store, ...
export default async ({ app, router, store }) => {
// Code here has access to the Object param above, connecting
// with other parts of your app;
// Code here can be async (use async/await or directly return a Promise);
// Code here gets executed by Quasar CLI at the correct time in app's lifecycle:
// - we have a Router instantiated,
// - we have the optional Vuex store instantiated,
// - we have the root app's component ["app" prop in Object param] Object with
// which Quasar will instantiate the Vue app
// ("new Vue(app)" -- do NOT call this by yourself),
// - ...
}
何時使用啟動檔案
警告
請確保您了解啟動檔案解決了什麼問題以及何時適合使用它們,以避免在不需要它們的情況下應用它們。
啟動檔案有一個特殊用途:它們在 App 的 Vue 根元件實例化**之前**執行程式碼,同時讓您可以存取某些變數,如果您需要初始化函式庫、干預 Vue Router、注入 Vue 原型或注入 Vue 應用程式的根實例,這是必需的。
啟動檔案的適當使用範例
- 您的 Vue 插件有安裝說明,例如需要對其呼叫
app.use()
。 - 您的 Vue 插件需要實例化添加到根實例的資料 - 一個範例是 vue-i18n。
- 您想要使用
app.mixin()
新增全域 mixin。 - 您想要將某些內容新增到 Vue 應用程式的 globalProperties 以方便存取 - 一個範例是在您的 Vue 檔案中方便地使用
this.$axios
(對於 Options API),而不是在每個此類檔案中匯入 Axios。 - 您想要干預路由器 - 一個範例是使用
router.beforeEach
進行身份驗證 - 您想要干預 Pinia 或 Vuex store 實例 - 一個範例是使用
vuex-router-sync
套件 - 配置函式庫的各個方面 - 一個範例是建立具有基本 URL 的 Axios 實例;然後您可以將其注入 Vue 原型和/或匯出它(以便您可以從應用程式中的任何其他位置匯入該實例)
不需要使用啟動檔案的範例
- 對於像 Lodash 這樣的純 JavaScript 函式庫,它們在使用前不需要任何初始化。例如,只有當您想要使用 Lodash 注入 Vue 原型時,才適合將 Lodash 用作啟動檔案,例如能夠在您的 Vue 檔案中使用
this.$_
。
啟動檔案的使用方法
第一步始終是使用 Quasar CLI 產生新的啟動檔案
$ quasar new boot <name> [--format ts]
其中 <name>
應替換為適合您啟動檔案的名稱。
此命令會建立一個新檔案:/src/boot/<name>.js
,內容如下
// import something here
// "async" is optional!
// remove it if you don't need it
export default async ({ /* app, router, store */ }) => {
// something to do
}
您也可以傳回 Promise
// import something here
export default ({ /* app, router, store */ }) => {
return new Promise((resolve, reject) => {
// do something
})
}
提示
如果您不需要預設匯出,則可以將其從啟動檔案中省略。這些是您不需要存取 “app”、“router”、“store” 等的情況。
您現在可以根據啟動檔案的預期用途將內容新增到該檔案。
別忘了您的預設匯出需要是一個函式。但是,如果您希望啟動檔案公開某些內容以供日後使用,您可以擁有任意數量的具名匯出。在這種情況下,您可以在應用程式中的任何位置匯入任何這些具名匯出。
最後一步是告訴 Quasar 使用您的新啟動檔案。為此,您需要在 /quasar.config
檔案中新增該檔案
boot: [
// references /src/boot/<name>.js
'<name>'
]
在建置 SSR 應用程式時,您可能希望某些啟動檔案僅在伺服器上或僅在用戶端上執行,在這種情況下,您可以像下面這樣做
boot: [
{
server: false, // run on client-side only!
path: '<name>' // references /src/boot/<name>.js
},
{
client: false, // run on server-side only!
path: '<name>' // references /src/boot/<name>.js
}
]
如果您想從 node_modules 指定啟動檔案,您可以透過在路徑前面加上 ~
(波浪符號)字元來做到
boot: [
// boot file from an npm package
'~my-npm-package/some/file'
]
如果您希望僅針對特定的建置類型將啟動檔案注入到您的應用程式中
boot: [
ctx.mode.electron ? 'some-file' : ''
]
重新導向到另一個頁面
警告
重新導向時請注意,因為您可能會將應用程式配置為進入無限重新導向迴圈。
export default ({ urlPath, redirect }) => {
// ...
const isAuthorized = // ...
if (!isAuthorized && !urlPath.startsWith('/login')) {
redirect({ path: '/login' })
return
}
// ...
}
redirect()
方法接受字串(完整 URL)或 Vue Router 位置字串或物件。在 SSR 上,它可以接收第二個參數,該參數應為數字,表示任何重新導向瀏覽器的 HTTP 狀態碼(3xx 碼)。
// Examples for redirect() with a Vue Router location:
redirect('/1') // Vue Router location as String
redirect({ path: '/1' }) // Vue Router location as Object
// Example for redirect() with a URL:
redirect('https://quasar.dev.org.tw')
重要!
Vue Router 位置(字串或物件形式)並非指 URL 路徑(和雜湊),而是指您定義的實際 Vue Router 路由。所以**不要將 publicPath 加入其中**,如果您正在使用 Vue Router 雜湊模式,則不要將雜湊加入其中。
假設我們定義了這個 Vue Router 路由
{
path: '/one',
component: PageOne
}
那麼**無論我們的 publicPath 為何**,我們都可以像這樣呼叫 redirect()
// publicPath: /wiki; vueRouterMode: history
redirect('/one') // good way
redirect({ path: '/one' }) // good way
redirect('/wiki/one') // WRONG!
// publicPath: /wiki; vueRouterMode: hash
redirect('/one') // good way
redirect({ path: '/one' }) // good way
redirect('/wiki/#/one') // WRONG!
// no publicPath; vueRouterMode: hash
redirect('/one') // good way
redirect({ path: '/one' }) // good way
redirect('/#/one') // WRONG!
如前幾節所述,啟動檔案的預設匯出可以傳回 Promise。如果此 Promise 因包含 “url” 屬性的物件而被拒絕,則 Quasar CLI 會將使用者重新導向到該 URL
export default ({ urlPath }) => {
return new Promise((resolve, reject) => {
// ...
const isAuthorized = // ...
if (!isAuthorized && !urlPath.startsWith('/login')) {
// the "url" param here is of the same type
// as for "redirect" above
reject({ url: '/login' })
return
}
// ...
})
}
或更簡單的等效方式
export default () => {
// ...
const isAuthorized = // ...
if (!isAuthorized && !urlPath.startsWith('/login')) {
return Promise.reject({ url: '/login' })
}
// ...
}
Quasar 應用程式流程
為了更好地了解啟動檔案的工作方式和作用,您需要了解您的網站/應用程式是如何啟動的
- Quasar 已初始化(元件、指令、插件、Quasar i18n、Quasar 圖示集)
- Quasar Extras 已匯入(Roboto 字體 – 如果使用、圖示、動畫、...)
- Quasar CSS 和您應用程式的全域 CSS 已匯入
- App.vue 已載入(尚未使用)
- Store 已匯入(如果使用 src/stores 中的 Pinia 或 src/store 中的 Vuex)
- Pinia(如果使用)已注入到 Vue 應用程式實例中
- Router 已匯入(在 src/router 中)
- 啟動檔案已匯入
- Router 預設匯出函式已執行
- 啟動檔案的預設匯出函式已執行
- (如果在 Electron 模式下)Electron 已匯入並注入到 Vue 原型中
- (如果在 Cordova 模式下)正在監聽 “deviceready” 事件,然後才繼續執行後續步驟
- 使用根元件實例化 Vue 並附加到 DOM
啟動檔案範例
Axios
import { boot } from 'quasar/wrappers'
import axios from 'axios'
const api = axios.create({ baseURL: 'https://api.example.com' })
export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api
app.config.globalProperties.$axios = axios
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
// so you won't necessarily have to import axios in each vue file
app.config.globalProperties.$api = api
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
// so you can easily perform requests against your app's API
})
export { axios, api }
vue-i18n
import { boot } from 'quasar/wrappers'
import { createI18n } from 'vue-i18n'
import messages from 'src/i18n'
export default boot(({ app }) => {
// Create I18n instance
const i18n = createI18n({
locale: 'en-US',
messages
})
// Tell app to use the I18n instance
app.use(i18n)
})
路由器身份驗證
某些啟動檔案可能需要干預 Vue Router 配置
import { boot } from 'quasar/wrappers'
export default boot(({ router, store }) => {
router.beforeEach((to, from, next) => {
// Now you need to add your authentication logic here, like calling an API endpoint
})
})
從啟動檔案存取資料
有時您想要在無法存取根 Vue 實例的檔案中存取您在啟動檔案中配置的資料。
幸運的是,由於啟動檔案只是普通的 JavaScript 檔案,您可以根據需要將任意數量的具名匯出新增到您的啟動檔案中。
讓我們以 Axios 為例。有時您想在 JavaScript 檔案中存取您的 Axios 實例,但您無法存取根 Vue 實例。為了解決這個問題,您可以將 Axios 實例匯出到您的啟動檔案中,然後在其他地方匯入它。
考慮以下 axios 的啟動檔案
import axios from 'axios'
// We create our own axios instance and set a custom base URL.
// Note that if we wouldn't set any config here we do not need
// a named export, as we could just `import axios from 'axios'`
const api = axios.create({
baseURL: 'https://api.example.com'
})
// for use inside Vue files through this.$axios and this.$api
// (only in Vue Options API form)
export default ({ app }) => {
app.config.globalProperties.$axios = axios
app.config.globalProperties.$api = api
}
// Here we define a named export
// that we can later use inside .js files:
export { axios, api }
在任何 JavaScript 檔案中,您都可以像這樣匯入 axios 實例。
// we import one of the named exports from src/boot/axios.js
import { api } from 'boot/axios'
語法延伸閱讀:ES6 import, ES6 export。