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

简介: 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`);

相关文章
|
1天前
|
JavaScript 定位技术 开发者
vue项目使用腾讯地图获取定位
vue项目使用腾讯地图获取定位
4 0
|
1天前
|
JavaScript 开发者
如何新建一个vue+vant移动端项目
如何新建一个vue+vant移动端项目
6 0
|
1天前
|
JavaScript IDE 开发工具
你的第一个Vue项目HelloWorld看这一篇就够了
你的第一个Vue项目HelloWorld看这一篇就够了
6 0
|
8天前
|
开发工具 git
vite+vue3+ts从0到1搭建企业级项目(4)
vite+vue3+ts从0到1搭建企业级项目
25 0
|
10月前
|
JavaScript
Vue的非父子组件之间传值
全局事件总线 一种组件间通信的方式,适用于任意组件间通信
|
9月前
|
缓存 JavaScript 前端开发
Vue Props、Slot、v-once、非父子组件间的传值....
Vue Props、Slot、v-once、非父子组件间的传值....
53 0
|
10月前
|
JavaScript
Vue中父子组件传值
先在⽗组件中给⼦组件的⾃定义属性绑定⼀个⽗组件的变量
|
10月前
|
JavaScript
vue 组件传值
vue 组件传值
58 0
|
10月前
|
JavaScript
vue父子组件传值
vue父子组件传值
|
JavaScript
vue兄弟组件传值 方便快捷
vue兄弟组件传值 方便快捷