vue/quasar - 动态注入组件

vue/quasar - dynamically injecting components

我正在尝试找出一种方法来将 vue 组件动态加载到我的布局中,而无需实际专门导入它们。采取这种结构

// Directory structure
src/
  layouts/
    MainLayout.vue
  modules/
    client/
      inject.ts
      components/
        AddButton.vue
    property/
      inject.ts
      components/
        AddButton.vue
  boot/
    injectors.ts

// modules/client/inject.ts (client/)
import {defineAsyncComponent} from "vue";

export default [
  {
    component: defineAsyncComponent(() => import('./components/AddButton.vue')),
    id: 'client-add-button',
    to: 'action-bar',
  }
]

// modules/property/inject.ts
import {defineAsyncComponent} from "vue";

export default [
  {
    component: defineAsyncComponent(() => import('./components/AddButton.vue')),
    id: 'property-add-button',
    to: 'action-bar',
  }
]

// boot/injectors.ts

export default boot(() => {
  
  // Somehow loop through all folders in src/modules/ and find inject files
  // 
  const injections = // concat the exported arrays into a single array
  app.provide('$injections', injections);
});

// src/layouts/MainLayout.vue

<template>

  <div>
    <template v-for="comp in injected" :key="comp.id">
      <component :is="comp.component" />
    </template>
  </div>

</template>

<script lang="ts">
import {
  computed, defineComponent, ref,
} from 'vue';

export default defineComponent({
  name: 'MainLayout',
  setup() {
    const injectables = computed( () => {
      return $injections.filter( injection => injection.to === 'action-bar');
    });
  }
});

</script>

想法是“模块”不会污染全局 view/layout space,而是被赋予注入其中的能力。

我不知道的部分是让它变得动态。我不想不断地向“引导”文件中添加文件。我想用 inject.ts 个文件创建模块让它工作

我觉得必须在 Webpack 中而不是在“启动”模块中完成此动态,但我有 NFI 从这里开始

好的。这会很长,但我想通了。我需要创建一个扩展 - 我将其命名为 InjectMe。

// injectme/index.js

/**
 * Quasar App Extension index/runner script
 * (runs on each dev/build)
 *
 * Docs: https://quasar.dev/app-extensions/development-guide/index-api
 * API: https://github.com/quasarframework/quasar/blob/master/app/lib/app-extension/IndexAPI.js
 */

path = require('path');
fs = require('fs');

const extendConf = async function (conf) {

   console.log('    Starting InjectMe installation');
   // make sure boot & component files transpile
    conf.boot.push('~quasar-app-extension-injectme/src/boot/setup.ts');
    conf.build.transpileDependencies.push(/quasar-app-extension-injectme[\/]src/);

    const baseDir = 'src/modules/';

    try {
       const modules = await fs.promises.readdir(baseDir);

       for (const file of modules) {
        const filePath = path.join(baseDir, file);
        // eslint-disable-next-line no-await-in-loop
        const stat = await fs.promises.stat(filePath);

         if (stat.isDirectory()) {
            const moduleFiles = await fs.promises.readdir(filePath);

            for (const moduleFile of moduleFiles) {
               const moduleFilePath =  path.join(filePath, moduleFile);
               if (moduleFile.includes('inject.ts')) {
                   conf.boot.push('../../' + moduleFilePath);
                   conf.build.transpileDependencies.push(moduleFilePath);
                   console.log(`    : InjectMe Installed ${moduleFilePath}`);
               }
           }
         }
      } // End for...of
    } catch (e) {
      console.error(e);
    }

    conf.boot.push('~quasar-app-extension-injectme/src/boot/provide.ts')
}

/**
 * Quasar App Extension index/runner script
 * (runs on each dev/build)
 *
 * Docs: https://quasar.dev/app-extensions/development-guide/index-api
 * API: https://github.com/quasarframework/quasar/blob/master/app/lib/app-extension/IndexAPI.js
 */

module.exports = function (api) {
    // extend quasar.conf
    api.extendQuasarConf(extendConf)
}

// injectme/src/boot/setup.ts
import { boot } from 'quasar/wrappers';

export default boot( ({app}) => {
     app.config.globalProperties.$injectme = [];
});
// injectme/src/boot/provide.ts
import { boot } from 'quasar/wrappers';

export default boot( ({app}) => {
   app.provide('injectme', app.config.globalProperties.$injectme);
});

我将此扩展程序放置在我的类星体应用程序上方的几个目录中(因此我可以在本地安装而无需将其放在 NPM 存储库中)。

// Directory structure looks like this
extensions/
  injectme/
    src/
      /boot

web/ # quasar app
  src/
    modules/
      client/  

现在在我的类星体应用程序中,我将扩展名添加到 package.json 文件:

    "quasar-app-extension-injectme": "file:../extensions/injectme", 

接下来我将扩展程序调用到应用程序中:

quasar ext invoke injectme

现在要加载动态模块,我需要在模块/文件夹中创建一个 inject.ts 文件

# web/src/modules/client/inject.ts


import { boot } from 'quasar/wrappers';
import { defineAsyncComponent } from 'vue';
import { routes } from './routes';

export default boot(({ app }) => {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
  app.config.globalProperties.$injectme.push({
    id: 'client-add-new-button',
    component: defineAsyncComponent(() => import('./components/AddButton.vue')),
    // Anything
    to: 'action-bar',
  });

 );

现在,使用“quasar dev”重新加载应用程序,您应该会看到 injectme 拾取注入文件。

它现在将组件添加到全局 $injectme,然后作为“可注入”提供回 Vue。

因此,转到应用程序完全不同部分的新文件,然后对其进行测试。

// web/src/layout/MainLayout.vue

<template>
  <div>
    <template v-for="comp in components.filter(c => c.to === 'action-bar')" :key="comp.id">
      <component :is="comp.component" />
    </template>
  </div>
</template>

<script lang="ts">

...
setup() {

  const components = inject('injectme');
  return { components };

}

</script>

好了。我还没有做过内存测试,也没有做过任何性能测试,所以这真的是你自己承担风险。

它实际上是为了在这里和那里添加按钮,而不是用于包含许多元素等的大型组件。