本指南适用于您希望创建本质上是“启动工具包”的情况,该工具包在官方启动工具包的基础上添加内容(/quasar.config 文件配置、文件夹、文件、CLI 钩子)。这允许您拥有多个共享公共结构/逻辑的项目(并且只有一个包来管理它们,而不是必须单独更改所有项目以匹配您的公共模式),并且还允许您与社区共享所有这些内容。
提示
为了创建 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) {
// (Optional!)
// Quasar compatibility check; you may need
// hard dependencies, as in a minimum version of the "quasar"
// package or a minimum version of Quasar App CLI
api.compatibleWith('quasar', '^2.0.0')
if (api.hasVite === true) {
api.compatibleWith('@quasar/app-vite', '^2.0.0-beta.1')
}
else { // api.hasWebpack === true
api.compatibleWith('@quasar/app-webpack', '^4.0.0-beta.1')
}
// We render some files into the hosting project
if (api.prompts.serviceA) {
api.render('./templates/serviceA')
}
if (api.prompts.serviceB) {
// we supply interpolation variables
// to the template
api.render('./templates/serviceB', {
productName: api.prompts.productName
})
}
// we always render the following template:
api.render('./templates/common-files')
}
请注意,我们使用提示来决定渲染到宿主项目中的内容。此外,如果用户选择了“service B”,那么我们还将拥有一个“productName”,我们可以在渲染 service B 的文件时使用它。
索引脚本
我们在索引脚本中执行了一些操作,例如扩展 /quasar.config 文件,挂接到众多 Index API 钩子之一(在本例中为 onPublish),以及链接 Webpack 配置
export default function (api) {
// (Optional!)
// Quasar compatibility check; you may need
// hard dependencies, as in a minimum version of the "quasar"
// package or a minimum version of Quasar App CLI
api.compatibleWith('quasar', '^2.0.0')
if (api.hasVite === true) {
api.compatibleWith('@quasar/app-vite', '^2.0.0-beta.1')
}
else { // api.hasWebpack === true
api.compatibleWith('@quasar/app-webpack', '^4.0.0-beta.1')
}
// Here we extend the /quasar.config file;
// (extendQuasarConf() will be defined later in this tutorial, continue reading)
api.extendQuasarConf(extendQuasarConf)
// Here we register the onPublish hook,
// only if user answered that he wants the publishing service
if (api.prompts.publishService) {
// onPublish() will be defined later in this tutorial, continue reading
api.onPublish(onPublish)
}
if (api.hasVite === true) {
api.extendViteConf(extendVite)
}
else { // api.hasWebpack === true
// we add/change/remove something in the Webpack configuration
// (chainWebpack() will be defined later in this tutorial, continue reading)
api.chainWebpack(chainWebpack)
}
// there's lots more hooks that you can use...
}
以下是extendQuasarConf
定义的示例
function extendQuasarConf (conf, api) {
conf.extras.push('ionicons-v4')
conf.framework.iconSet = 'ionicons-v4'
//
// We register a boot file. User does not need to tamper with it,
// so we keep it into the App Extension code:
//
// make sure my-ext boot file is registered
conf.boot.push('~quasar-app-extension-my-starter-kit/src/boot/my-starter-kit-boot.js')
// @quasar/app-vite does not need this
if (api.hasVite !== true) {
// make sure boot file get transpiled
conf.build.transpileDependencies.push(/quasar-app-extension-my-starter-kit[\\/]src/)
}
}
onPublish
函数
function onPublish (api, { arg, distDir }) {
// this hook is called when "quasar build --publish" is called
// your publish logic here...
console.log('We should publish now. But maybe later? :)')
// are we trying to publish a Cordova app?
if (api.ctx.modeName === 'cordova') {
// do something
}
}
extendVite
函数
function extendVite (viteConf, { isClient, isServer }, api) {
// viteConf is a Vite config object generated by Quasar CLI
}
chainWebpack
函数
function chainWebpack (cfg, { isClient, isServer }, api) {
// cfg is a Webpack chain Object;
// docs on how to use it: webpack-chain docs (https://github.com/neutrinojs/webpack-chain)
}
卸载脚本
卸载 App Extension 时,我们需要进行一些清理。但请注意您从应用程序空间中删除的内容!某些文件可能仍然需要。如果您决定使用卸载脚本,请务必小心操作。
// we yarn added it to our App Extension,
// so we can import the following:
const rimraf = require('rimraf')
export default function (api) {
// Careful when you remove folders!
// You don't want to delete files that are still needed by the Project,
// or files that are not owned by this app extension.
// Here, we could also remove the /src/services folder altogether,
// but what if the user has added other files into this folder?
if (api.prompts.serviceA) {
// we added it on install, so we remove it
rimraf.sync(api.resolve.src('services/serviceA.js'))
}
if (api.prompts.serviceB) {
// we added it on install, so we remove it
rimraf.sync(api.resolve.src('services/serviceB.js'))
}
// we added it on install, so we remove it
rimraf.sync(api.resolve.app('some-folder'))
// warning... we've added this folder, but what if the
// developer added more files into this folder???
}
请注意,我们正在请求rimraf
npm 包。这意味着我们已将其添加到我们的 App Extension 项目中(使用 yarn/npm)。