# 为了解决 CommonJS 和 UMD 兼容性问题

CommonJSUMD 兼容性: 开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此 Vite 必须先将作为 CommonJSUMD 发布的依赖项转换为 ESM

# 什么是 ESM

ES-Module 简称为 ESM, ESM 是浏览器原生支持模块功能, 浏览器能够最优化的方式加载模块,使它比使用库更有效率, 使用库通常需要做额外的客户端处理。

# 利用浏览器ESM加载方式

ESM 加载的方式与通常的 <script> 加载的机制一样, 但唯一的区别在于需要 <script> 起始标签加一个 type="module" 的属性, 这样就可以通过 importexport 这两个关健字进行模块化导入导出的管理。

# 错误加载示例

<script>
  import HelloWorld from './index.js'
</script>

错误提示

Uncaught SyntaxError: Cannot use import statement outside a module

importexport 方式进行模块化方式导入与导出需要在 <script> 标签加上 type="module"

# 什么是CommonJS

CommonJS 简称为 CJS

CommonJS 也是一种模块的形式, 其特性为: 核心思想是通过 require 方法来同步加载依赖的其他模块, 通过 module.exports 导出需要暴露的代码模块。

CommonJS 大多数用于 Node.js 环境下, 大多数 NPM 第三方模块采用了 CommonJS 规范, 但是这样的导入导出的方式无法运行在浏览器环境下。

# 浏览器环境下使用 import 语法导入 CommonJS 格式的模块

<script type="module">
  const HelloWrold = require('./index.js')
</script>

错误提示

Uncaught ReferenceError: require is not defined

没有定义 require, 浏览器并没有全局实现 require 方法, 不支持此方法.

# 什么是UMD

由于 CommonJSAMD 风格都同样流行,似乎还没有达成共识。这带来了对支持两种风格的通用模式的推动,这让我们想到了通用模块定义。

诚然,该模式很丑陋,但与 AMDCommonJS 兼容,并且支持旧式的全局变量定义。

# 浏览器环境下使用 import 语法导入 UMD 格式模块

// index.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery', 'underscore'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS之类的
        module.exports = factory(require('jquery'), require('underscore'));
    } else {
        // 浏览器全局变量(root 即 window)
        root.returnExports = factory(root.jQuery, root._);
    }
}(this, function ($, _) {
    //    方法
    function a(){};    //    私有方法,因为它没被返回 (见下面)
    function b(){};    //    公共方法,因为被返回了
    function c(){};    //    公共方法,因为被返回了

    //    暴露公共方法
    return {
        b: b,
        c: c
    }
}));
<script type="module">
  const HelloWrold = require('./index.js')
</script>

错误提示

Uncaught SyntaxError: The requested module './index.js?v=43ffb0a3' does not provide an export named 'default'

提示报错:

index.js 文件是一个umd 格式模块, 通过import的语法进行在浏览器导入, 则会提示没有提供默认的导出名称。

# 总结

以上三种方式可以得出只有通过 <script type="module"></script> 方式引入, 并且要符合 ESM 的导入导出规范, 浏览器才会符合预期效果进行模块加载, 其余的 CommonJS 规范还是 UMD 规范, 浏览器都不能进行识别,对于UMD 而言可以通过 <script src> 引入的方式可以达到全局变量预期。

所以 Vite 进行对模块的预构建也是同样的道理, 市面上的 NPM 三方模块包大多数都是 CommonJSUMD 的规范, 为了让所有 NPM 模块包在浏览器下使用 ESM 的语法进行导入, 通过预构建的方式把 CommonJSUMD 的模块包转化为 ESM 的导出的模式, 这样浏览器就可以使用 ESM 的模块加载机制。