vite 支持 externals

240 阅读1分钟

wepack 支持externals,比如 react 不使用 npm 包,使用window 中的 React。找了比较多的库,发现vite-plugin-externals库实现不错,思路清晰,使用简单。那么是怎么实现的呢。核心原理如下:

// 源代码
import Vue from 'vue'
// 转换后
const Vue = window['Vue']

怎么做到转换的呢,主要是用了两个库, ES Module Lexer 和 Acorn

找出 import 语句

为什么使用ES Module Lexer呢,因为速度快,先做一次初筛,提高效率,然后用Acorn做替换

ES Module Lexer的例子,会返回 imports 和 exports 两个数组,包含了 import 的信息

import { init, parse } from 'es-module-lexer';

(async () => {
  await init;

  const source = `
    import { name } from 'mod\u1011';
  `;

  const [imports, exports] = parse(source, 'optional-sourcename');

  // Returns "modထ"
  imports[0].n
  // Returns "mod\u1011"
  source.slice(imports[0].s, imports[0].e);
  // "s" = start
  // "e" = end

  // Returns "import { name } from 'mod'"
  source.slice(imports[0].ss, imports[0].se);
  // "ss" = statement start
  // "se" = statement end
 })();

使用 parse找出 imports,和配置比较,没有要替换的直接返回

const [imports] = parse(code)
imports.forEach(({
  d: dynamic,
  n: dependence,
  ss: statementStart,
  se: statementEnd,
}) => {})

替换为变量

使用Acorn把 imprt 语句转化为 ast 结构,替换为变量。 astexplorer.net/ 这个网址可以看 ast 结构,初次看,比较震撼的。在这里面写个 demo 会容易理解。

 const specifiers = (ast.body[0] as (ESTree.ImportDeclaration))?.specifiers as Specifiers
  if (!specifiers) {
    return ''
  }
  return specifiers.reduce((s, specifier) => {
    const { local } = specifier
    if (specifier.type === 'ImportDefaultSpecifier') {
      /**
       * source code: import Vue from 'vue'
       * transformed: const Vue = window['Vue']
       */
      s += `const ${local.name} = ${transformModuleName(externalValue)}\n`
    } else if (specifier.type === 'ImportSpecifier') {
      /**
       * source code:
       * import { reactive, ref as r } from 'vue'
       * transformed:
       * const reactive = window['Vue'].reactive
       * const r = window['Vue'].ref
       */
      const { imported } = specifier
      s += `const ${local.name} = ${transformModuleName(externalValue)}.${imported.name}\n`
    }

这样转换就完成了,短小精悍。