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 存储的实例 - **如果您的项目使用 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),
// - ...
}
何时使用启动文件
警告
请确保您了解启动文件解决了什么问题以及何时适合使用它们,以避免在不需要的情况下使用它们。
启动文件满足一个特殊目的:它们在应用程序的 Vue 根组件实例化**之前**运行代码,同时允许您访问某些变量,如果您需要初始化库、干预 Vue Router、注入 Vue 原型或注入 Vue 应用程序的根实例,则需要这些变量。
启动文件使用示例
- 您的 Vue 插件有安装说明,例如需要在其上调用
app.use()
。 - 您的 Vue 插件需要实例化添加到根实例的数据 - 一个示例是 vue-i18n。
- 您希望使用
app.mixin()
添加全局混入。 - 您希望将某些内容添加到 Vue 应用程序 globalProperties 以方便访问 - 一个示例是方便地在 Vue 文件中使用
this.$axios
(对于选项 API)而不是在每个此类文件中导入 Axios。 - 您希望干预路由器 - 一个示例是使用
router.beforeEach
进行身份验证 - 您希望干预 Pinia 或 Vuex 存储实例 - 一个示例是使用
vuex-router-sync
包 - 配置库的各个方面 - 一个示例是使用基本 URL 创建 Axios 实例;然后您可以将其注入 Vue 原型和/或导出它(以便您可以从应用程序中的任何其他位置导入该实例)
启动文件不需要使用示例
- 对于像 Lodash 这样的普通 JavaScript 库,它们不需要在使用之前进行任何初始化。例如,只有当您希望使用它注入 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.org.cn')
重要!
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。