为什么要捐赠
API 资源管理器
升级指南
新增!
quasar.config 文件
将项目转换为使用 Vite 的 CLI
浏览器兼容性
支持 TypeScript
目录结构
命令列表
CSS 预处理器
路由
延迟加载 - 代码分割
处理资源
启动文件
预取功能
API 代理
处理 Vite
处理 process.env
使用 Pinia 进行状态管理
使用 Vuex 进行状态管理
代码检查器
测试与审计
开发移动应用
Ajax 请求
将开发服务器开放到公共网络
使用 Vite 的 Quasar CLI - @quasar/app-vite
启动文件

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 会在启动应用程序时调用导出的函数,并额外将**一个对象**传递给该函数,该对象具有以下属性

属性名称描述
appVue 应用实例
router来自 'src/router/index.js' 的 Vue Router 实例
storePinia 或 Vuex 存储的实例 - **如果您的项目使用 Pinia(您有 src/stores)或 Vuex(您有 src/store),则只会传递 store**
ssrContext仅在服务器端可用,如果构建用于 SSR。 更多信息
urlPathURL 的路径名(路径 + 搜索)部分。它还在客户端包含哈希值。
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(对于 Options API),而不是在每个此类文件中导入 Axios。
  • 您想干预路由器 - 一个示例是使用 router.beforeEach 进行身份验证
  • 您想干预 Pinia 或 Vuex 存储实例 - 一个示例是使用 vuex-router-sync
  • 配置库的各个方面 - 一个示例是创建具有基本 URL 的 Axios 实例;然后,您可以将其注入 Vue 原型和/或导出它(以便您可以从应用程序中的任何其他位置导入该实例)

启动文件的不必要用法示例

  • 对于像 Lodash 这样的纯 JavaScript 库,它们不需要在使用之前进行任何初始化。例如,Lodash 只有在您想用它注入 Vue 原型时才有意义作为启动文件使用,例如能够在 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!

如前几节所述,boot 文件的默认导出可以返回一个 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 应用流程

为了更好地理解 boot 文件的工作原理及其作用,您需要了解您的网站/应用是如何启动的。

  1. Quasar 初始化(组件、指令、插件、Quasar i18n、Quasar 图标集)
  2. 导入 Quasar 扩展(如果使用则导入 Roboto 字体、图标、动画等)
  3. 导入 Quasar CSS 和您的应用的全局 CSS
  4. 加载 App.vue(尚未使用)
  5. 导入 Store(如果在 src/stores 中使用 Pinia 或在 src/store 中使用 Vuex)
  6. 将 Pinia(如果使用)注入到 Vue 应用实例中
  7. 导入路由器(在 src/router 中)
  8. 导入 boot 文件
  9. 执行路由器默认导出函数
  10. 执行 boot 文件的默认导出函数
  11. (如果在 Electron 模式下)导入 Electron 并将其注入到 Vue 原型中
  12. (如果在 Cordova 模式下)监听“deviceready”事件,只有在事件触发后才继续执行以下步骤
  13. 使用根组件实例化 Vue 并附加到 DOM

boot 文件示例

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)
})

路由器认证

一些 boot 文件可能需要干预 Vue 路由器的配置。

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
  })
})

从 boot 文件访问数据

有时您希望在无法访问根 Vue 实例的文件中访问在 boot 文件中配置的数据。

幸运的是,由于 boot 文件只是普通的 JavaScript 文件,因此您可以根据需要向 boot 文件添加任意数量的命名导出。

以 Axios 为例。有时您希望在 JavaScript 文件中访问您的 Axios 实例,但您无法访问根 Vue 实例。要解决此问题,您可以在 boot 文件中导出 Axios 实例并在其他地方导入它。

考虑以下 axios 的 boot 文件

axios boot 文件 (src/boot/axios.js)

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 importES6 export