VUE3+vite项目中动态引入组件和异步组件

一、全量注册,随用随取

1. 把项目中所有vue文件注册成异步组件。

const app = createApp(App);
function registerGlobalAsyncComponents(app: VueApp) {
    
    
  const modules = import.meta.glob('./**/*.vue');
  for (const path in modules) {
    
    
    const result = path.match(/.*\/(.+).vue$/);
    if (result) {
    
    
      const name = result[1];
      const component = modules[path];
      app.component(name, defineAsyncComponent(component));
    }
  }
}

2. 获取组件

在setup函数获取组件

const internalInstance = getCurrentInstance();
// 搂一眼,看看注册的组件名字是啥
console.log(internalInstance.appContext.components);
// 获取组件
internalInstance.appContext.components['你组件的名字']

3. 参考如下

Glob 导入
Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:

const modules = import.meta.glob('./dir/*.js')

以上将会被转译为下面的样子:

// vite 生成的代码
const modules = {
    
    
  './dir/foo.js': () => import('./dir/foo.js'),
  './dir/bar.js': () => import('./dir/bar.js')
}

你可以遍历 modules 对象的 key 值来访问相应的模块:

for (const path in modules) {
    
    
  modules[path]().then((mod) => {
    
    
    console.log(path, mod)
  })
}

匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk。如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),你可以传入 { eager: true } 作为第二个参数:

const modules = import.meta.glob(‘./dir/*.js’, { eager: true })
以上会被转译为下面的样子:

// vite 生成的代码
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
    
    
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}
  • Glob 导入形式
    import.meta.glob 都支持以字符串形式导入文件,类似于 以字符串形式导入资源。在这里,我们使用了 Import Reflection 语法对导入进行断言:
const modules = import.meta.glob('./dir/*.js', {
    
     as: 'raw' })

上面的代码会被转换为下面这样:

// code produced by vite(代码由 vite 输出)
const modules = {
    
    
  './dir/foo.js': 'export default "foo"\n',
  './dir/bar.js': 'export default "bar"\n'
}

{ as: ‘url’ } 还支持将资源作为 URL 加载。

  • 多个匹配模式
    第一个参数可以是一个 glob 数组,例如:
const modules = import.meta.glob(['./dir/*.js', './another/*.js'])
  • 反面匹配模式
    同样也支持反面 glob 匹配模式(以 ! 作为前缀)。若要忽略结果中的一些文件,你可以添加“排除匹配模式”作为第一个参数:
const modules = import.meta.glob(['./dir/*.js', '!**/bar.js'])
// vite 生成的代码
const modules = {
    
    
  './dir/foo.js': () => import('./dir/foo.js')
}
  • 具名导入
    也可能你只想要导入模块中的部分内容,那么可以利用 import 选项。
const modules = import.meta.glob('./dir/*.js', {
    
     import: 'setup' })
// vite 生成的代码
const modules = {
    
    
  './dir/foo.js': () => import('./dir/foo.js').then((m) => m.setup),
  './dir/bar.js': () => import('./dir/bar.js').then((m) => m.setup)
}

当与 eager 一同存在时,甚至可能可以对这些模块进行 tree-shaking。

const modules = import.meta.glob('./dir/*.js', {
    
     import: 'setup', eager: true })
// vite 生成的代码
import {
    
     setup as __glob__0_0 } from './dir/foo.js'
import {
    
     setup as __glob__0_1 } from './dir/bar.js'
const modules = {
    
    
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}

设置 import 为 default 可以加载默认导出。

const modules = import.meta.glob('./dir/*.js', {
    
    
  import: 'default',
  eager: true
})
// vite 生成的代码
import __glob__0_0 from './dir/foo.js'
import __glob__0_1 from './dir/bar.js'
const modules = {
    
    
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}
  • 自定义查询
    你也可以使用 query 选项来提供对导入的自定义查询,以供其他插件使用。
const modules = import.meta.glob('./dir/*.js', {
    
    
  query: {
    
     foo: 'bar', bar: true }
})
// vite 生成的代码
const modules = {
    
    
  './dir/foo.js': () =>
    import('./dir/foo.js?foo=bar&bar=true').then((m) => m.setup),
  './dir/bar.js': () =>
    import('./dir/bar.js?foo=bar&bar=true').then((m) => m.setup)
}

二、使用@rollup/plugin-dynamic-import-vars插件

1.介绍

一个rollup插件,支持rollup中动态导入的变量。

This plugin requires an LTS Node version (v10.0.0+) and Rollup v1.20.0+.

2.安装

npm install @rollup/plugin-dynamic-import-vars --save-dev

3.使用

创建一个rollup.config.js配置文件并导入插件:

import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';

export default {
    
    
  plugins: [
    dynamicImportVars({
    
    
      // options
    })
  ]
};

Options
include
Type: String | Array[…String]
Default: []

Files to include in this plugin (default all).包含在这个插件中的文件(默认全部)。

exclude
Type: String | Array[…String]
Default: []

Files to exclude in this plugin (default none).该插件中要排除的文件(默认为无)。

warnOnError
Type: Boolean
Default: false

By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched.
默认情况下,当遇到错误时,插件会退出构建过程。如果将此选项设置为true,则会抛出一个警告,并保持代码不变。

4.How it works

When a dynamic import contains a concatenated string, the variables of the string are replaced with a glob pattern. This glob pattern is evaluated during the build, and any files found are added to the rollup bundle. At runtime, the correct import is returned for the full concatenated string.

Some example patterns and the glob they produce:

`./locales/${
      
      locale}.js` -> './locales/*.js'
`./${
      
      folder}/${
      
      name}.js` -> './*/*.js'
`./module-${
      
      name}.js` -> './module-*.js'
`./modules-${
      
      name}/index.js` -> './modules-*/index.js'
'./locales/' + locale + '.js' -> './locales/*.js'
'./locales/' + locale + foo + bar '.js' -> './locales/*.js'
'./locales/' + `${
      
      locale}.js` -> './locales/*.js'
'./locales/' + `${
      
      foo + bar}.js` -> './locales/*.js'
'./locales/'.concat(locale, '.js') -> './locales/*.js'
'./'.concat(folder, '/').concat(name, '.js') -> './*/*.js'

Code that looks like this:

function importLocale(locale) {
    
    
  return import(`./locales/${
      
      locale}.js`);
}

Is turned into:

function __variableDynamicImportRuntime__(path) {
    
    
  switch (path) {
    
    
    case './locales/en-GB.js':
      return import('./locales/en-GB.js');
    case './locales/en-US.js':
      return import('./locales/en-US.js');
    case './locales/nl-NL.js':
      return import('./locales/nl-NL.js');
    default:
      return new Promise(function (resolve, reject) {
    
    
        queueMicrotask(reject.bind(null, new Error('Unknown variable dynamic import: ' + path)));
      });
  }
}

function importLocale(locale) {
    
    
  return __variableDynamicImportRuntime__(`./locales/${
      
      locale}.js`);
}

Limitations
To know what to inject in the rollup bundle, we have to be able to do some static analysis on the code and make some assumptions about the possible imports. For example, if you use just a variable you could in theory import anything from your entire file system.

function importModule(path) {
    
    
  // who knows what will be imported here?
  return import(path);
}

To help static analysis, and to avoid possible foot guns, we are limited to a couple of rules:

Imports must start with ./ or …/.
All imports must start relative to the importing file. The import should not start with a variable, an absolute path or a bare import:

// Not allowed
import(bar);
import(`${
      
      bar}.js`);
import(`/foo/${
      
      bar}.js`);
import(`some-library/${
      
      bar}.js`);

Imports must end with a file extension
A folder may contain files you don’t intend to import. We, therefore, require imports to end with a file extension in the static parts of the import.

// Not allowed
import(`./foo/${
      
      bar}`);
// allowed
import(`./foo/${
      
      bar}.js`);
Imports to your own directory must specify a filename pattern

If you import your own directory you likely end up with files you did not intend to import, including your own module. It is therefore required to give a more specific filename pattern:

// not allowed
import(`./${
      
      foo}.js`);
// allowed
import(`./module-${
      
      foo}.js`);

Globs only go one level deep
When generating globs, each variable in the string is converted to a glob * with a maximum of one star per directory depth. This avoids unintentionally adding files from many directories to your import.

In the example below this generates ./foo//.js and not ./foo/**/*.js.

import(`./foo/${
      
      x}${
      
      y}/${
      
      z}.js`);

猜你喜欢

转载自blog.csdn.net/qq_32594913/article/details/126039629