本指南適用於當您想要建立本質上是「starter kit」的東西,在官方 starter kit 之上新增內容(/quasar.config 檔案設定、資料夾、檔案、CLI hook)時。這讓您可以有多個專案共用通用結構/邏輯(並且只有一個套件來管理它們,而不是必須單獨變更所有專案以符合您的通用模式),並且也讓您可以與社群分享所有這些內容。
提示
為了建立 App Extension 專案資料夾,請先閱讀開發指南 > 簡介。
完整範例
若要查看我們將建置的範例,請前往 MyStarterKit 完整範例,這是一個包含此 App Extension 的 github 儲存庫。
我們將建立一個範例 App Extension,它會執行以下操作
- 它會提示使用者想要安裝此 App Extension 的哪些功能
- 根據他給的答案,將檔案渲染(複製)到託管資料夾中
- 它會擴充 /quasar.config 檔案
- 它會擴充 Webpack 設定
- 它使用 App Extension hook (onPublish)
- 當 App Extension 被解除安裝時,它會移除新增的檔案
- 它使用 prompts 來定義 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')
}
請注意,我們使用 prompts 來決定要渲染到託管專案中的內容。此外,如果使用者已選取「service B」,那麼我們也會有一個「productName」,可以在渲染 service B 的檔案時使用。
Index 腳本
我們在 index 腳本中做了一些事情,例如擴充 /quasar.config 檔案、hook 到許多 Index API hook 之一(在此範例中為 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 套件。這表示我們已將其 yarn/npm 新增到我們的 App Extension 專案中。