入门套件
这份指南是为了在您希望创建一个本质上是“入门套件”的情况下而准备的,该套件在官方入门套件的基础上添加了一些内容(/quasar.config 文件配置、文件夹、文件、CLI 钩子)。这允许您拥有多个项目共享相同的结构/逻辑(而不是必须单独更改所有项目以匹配您的公共模式),并且还可以与社区共享所有这些。
TIP
为了创建一个 App Extension 项目文件夹,请首先阅读 开发指南 > 介绍。
完整示例
要查看我们将构建的示例,请转到 MyStarterKit 完整示例,这是一个包含此 App Extension 的 GitHub 仓库。
我们将创建一个示例 App Extension,其中执行以下操作:
- 提示用户要安装此 App Extension 的哪些功能
- 根据用户的选择,将文件渲染(复制)到主机文件夹中
- 扩展 /quasar.config 文件
- 扩展 Webpack 配置
- 使用 App Extension 钩子(onPublish)
- 在卸载 App Extension 时删除添加的文件
- 使用提示定义 App Extension 的功能
结构
出于本示例的目的,我们将创建以下文件夹结构:
安装脚本
下面的安装脚本仅将文件渲染到托管的应用程序中。请注意上面的 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 项目中。