入门套件

这份指南是为了在您希望创建一个本质上是“入门套件”的情况下而准备的,该套件在官方入门套件的基础上添加了一些内容(/quasar.config 文件配置、文件夹、文件、CLI 钩子)。这允许您拥有多个项目共享相同的结构/逻辑(而不是必须单独更改所有项目以匹配您的公共模式),并且还可以与社区共享所有这些。

TIP

为了创建一个 App Extension 项目文件夹,请首先阅读 开发指南 > 介绍

完整示例

要查看我们将构建的示例,请转到 MyStarterKit 完整示例,这是一个包含此 App Extension 的 GitHub 仓库。

我们将创建一个示例 App Extension,其中执行以下操作:

  • 提示用户要安装此 App Extension 的哪些功能
  • 根据用户的选择,将文件渲染(复制)到主机文件夹中
  • 扩展 /quasar.config 文件
  • 扩展 Webpack 配置
  • 使用 App Extension 钩子(onPublish)
  • 在卸载 App Extension 时删除添加的文件
  • 使用提示定义 App Extension 的功能

结构

出于本示例的目的,我们将创建以下文件夹结构:

README.md
package.json
my-starter-kit-boot.js
README.md
tasks.md
serviceA.js
serviceB.js
index.js
# Described in Index API
install.js
# Described in Install API
prompts.js
# Described in Prompts API
uninstall.js
# Described in Uninstall API

安装脚本

下面的安装脚本仅将文件渲染到托管的应用程序中。请注意上面的 src/templates 文件夹,我们决定将这些模板保存在其中。

export default function (api) {
  // (可选!)
  // Quasar 兼容性检查;您可能需要
  // 强硬的依赖,比如 "quasar" 包的最小版本
  // 或 Quasar App CLI 的最小版本
  api.compatibleWith('quasar', '^2.0.0')

  if (api.hasVite === true) {
    api.compatibleWith('@quasar/app-vite', '^1.0.0-beta.0')
  }
  else { // api.hasWebpack === true
    api.compatibleWith('@quasar/app-webpack', '^3.0.0')
  }

  // 我们将一些文件渲染到托管项目中

  if (api.prompts.serviceA) {
    api.render('./templates/serviceA')
  }

  if (api.prompts.serviceB) {
    // 我们提供模板的插值变量
    // 以便在渲染服务 B 文件时使用
    api.render('./templates/serviceB', {
      productName: api.prompts.productName
    })
  }

  // 我们总是渲染以下模板:
  api.render('./templates/common-files')
}

请注意,我们使用提示来决定将什么渲染到托管项目中。此外,如果用户选择了“service B”,那么我们还将有一个“productName”,我们可以在渲染服务 B 文件时使用。

index 脚本

我们在 index 脚本中执行一些操作,比如扩展 /quasar.config 文件,钩入其中一个许多 Index API 钩子(在这种情况下是 onPublish),以及链式化 Webpack 配置:

export default function (api) {
  // (可选!)
  // Quasar 兼容性检查;您可能需要
  // 强硬的依赖,比如 "quasar" 包的最小版本
  // 或 Quasar App CLI 的最小版本
  api.compatibleWith('quasar', '^2.0.0')

  if (api.hasVite === true) {
    api.compatibleWith('@quasar/app-vite', '^1.0.0-beta.0')
  }
  else { // api.hasWebpack === true
    api.compatibleWith('@quasar/app-webpack', '^3.0.0')
  }

  // 在这里我们扩展 /quasar.config 文件;
  // (extendQuasarConf() 将在本教程的后面定义,继续阅读)
  api.extendQuasarConf(extendQuasarConf)

  // 在这里注册 onPublish 钩子,
  // 仅当用户回答说他想要发布服务时
  if (api.prompts.publishService) {
    // onPublish() 将在本教程的后面定义,继续阅读
    api.onPublish(onPublish)
  }

  if (api.hasVite === true) {
    api.extendViteConf(extendVite)
  }
  else { // api.hasWebpack === true
    // 我们在 Webpack 配置中添加/更改/删除某些内容
    // (chainWebpack() 将在本教程的后面定义,继续阅读)
    api.chainWebpack(chainWebpack)
  }

  // 还有很多其他可用的钩子...
}

这里是 extendQuasarConf 定义的示例:

function extendQuasarConf (conf, api) {
  conf.extras.push('ionicons-v4')
  conf.framework.iconSet = 'ionicons-v4'

  //
  // 我们注册了一个引导文件。用户不需要修改它,
  // 所以我们将其保留在 App Extension 代码中:
  //

  // 确保 my-ext 引导文件已注册
  conf.boot.push('~quasar-app-extension-my-starter-kit/src/boot/my-starter-kit-boot.js')

  // @quasar/app-vite 不需要这个
  if (api.hasVite !== true) {
    // 确保引导文件被转译
    conf.build.transpileDependencies.push(/quasar-app-extension-my-starter-kit[\\/]src/)
  }
}

onPublish 函数:

function onPublish (api, { arg, distDir }) {
  // 当调用 "quasar build --publish" 时调用此

钩子

  // 在这里写入您的发布逻辑...
  console.log('We should publish now. But maybe later? :)')

  // 我们是否尝试发布 Cordova 应用?
  if (api.ctx.modeName === 'cordova') {
    // 做一些事情
  }
}

extendVite 函数:

function extendVite (viteConf, { isClient, isServer }, api) {
  // viteConf 是由 Quasar CLI 生成的 Vite 配置对象
}

chainWebpack 函数:

function chainWebpack (cfg, { isClient, isServer }, api) {
  // cfg 是一个由 Webpack chain 生成的对象;
  // 如何使用它的文档:webpack-chain docs (https://github.com/neutrinojs/webpack-chain)
}

卸载脚本

当 App Extension 被卸载时,我们需要进行一些清理。但要小心从应用程序空间中删除什么!某些文件可能仍然是必需的。如果您决定有一个卸载脚本,请谨慎操作。

// 我们使用 yarn 添加到我们的 App Extension 中,
// 所以我们可以导入以下内容:
const rimraf = require('rimraf')

export default function (api) {
  // 删除文件夹时要小心!
  // 您不希望删除项目仍然需要的文件,
  // 或者不是由此应用程序扩展拥有的文件。

  // 在这里,我们还可以将 /src/services 文件夹整个删除,
  // 但是如果用户在此文件夹中添加了其他文件呢?

  if (api.prompts.serviceA) {
    // 我们在安装时添加了它,所以我们删除它
    rimraf.sync(api.resolve.src('services/serviceA.js'))
  }

  if (api.prompts.serviceB) {
    // 我们在安装时添加了它,所以我们删除它
    rimraf.sync(api.resolve.src('services/serviceB.js'))
  }

  // 我们在安装时添加了它,所以我们删除它
  rimraf.sync(api.resolve.app('some-folder'))
  // 警告... 我们添加了这个文件夹,但是如果
  // 开发人员在此文件夹中添加了更多文件呢???
}

请注意,我们正在请求 rimraf npm 包。这意味着我们将它添加到了我们的 App Extension 项目中。