JS模块化规范

451 阅读3分钟

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

本文基于rollup打包,配置其输出格式,浅谈一下模块化规范的差异

rollup.rollup

rollup提供 rollup.rollup API

  • 接收一个输入选项对象 inputOptions 作为参数
  • 返回一个 Promise
  • 解析出一个 bundle 对象,调用当中 bundle.write 方法,输出配置 outputOptions作为传参,将打包结果写入磁盘中

该 API 配合 Gulp,让自定义打包更为方便

示例代码

package.json添加命令

 "build:demo": "gulp --gulpfile build-demo.mjs"

demo.ts作为入口文件

export const add: Function = (...arr: number[]) => {
  return arr.reduce((acc, item) => acc + item, 0)
}

build-demo.mjs

import path from 'path'
import gulp from 'gulp'
import { rollup } from 'rollup'
import typescript from 'rollup-plugin-typescript2'
const { resolve } = path

const formats = ['es', 'amd', 'cjs', 'iife', 'umd', 'system'] //输出格式可选值
formats.map((format) => {
  gulp.task(`${format}`, () => {
    return rollup({
      input: './demo.ts',
      plugins: [
        typescript({
          tsconfigOverride: {
            compilerOptions: {
              declaration: false, // 不生成类型声明文件
            },
          },
        }),
      ],
    }).then((bundle) => {
      return bundle.write({
        file: resolve(`../dist/${format}.js`),
        format,
        name: 'demo',
        sourcemap: false,
      })
    })
  })
})

export default gulp.series(formats)

模块化规范

根据上述代码,执行 npm run build:demo,对应输出各种格式的打包文件

不同的输出格式,为了适配不同的运行环境

iife

var demo = (function (exports) {
  'use strict';
  const add = (...arr) => {
      return arr.reduce((acc, item) => acc + item, 0);
  };
  exports.add = add;
  Object.defineProperty(exports, '__esModule', { value: true });
  return exports;
})({}); 

模块化实现方式前期演变过程:

  • 文件划分方式:约定一个 JS 文件是一个模块

  • 命名空间方式:每个模块暴露一个全局对象,模块成员都挂载到这个全局对象

  • IIFE 方式:使用 立即执行函数表达式 为模块提供私有空间(见上输出:(function () {})()

    • 避免了外界访问当中的变量,且避免污染全局作用域

    • 在定义时立即执行,适合使用<script>标签直接引用

cjs

该格式的打包结果:

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const add = (...arr) => {
    return arr.reduce((acc, item) => acc + item, 0);
};
exports.add = add;

对应的是 CommonJS 规范,其特点:

  • 一个文件就是一个模块,每个模块都有单独的作用域

    • 通过 exports 导出,如上 exports.add = add

    • 使用模块时,需要通过 require 函数引入

  • 同步方式加载模块

    • 适合 服务端Node.js 同步加载,模块文件存储在本地磁盘,读取快

    • 不合适在浏览器端直接使用此规范:若引起大量的同步加载请求,受限于网络,效率低

amd

浏览器端适合异步加载模块,而 amd 格式对应的 AMD 规范:异步模块定义规范,故适合浏览器端

define(['exports'], (function (exports) { 'use strict';
  const add = (...arr) => {
      return arr.reduce((acc, item) => acc + item, 0);
  };
  exports.add = add;
  Object.defineProperty(exports, '__esModule', { value: true });
}));

  • 基于 Require.js 实现

  • define() 来定义模块

  • require() 函数用于自动加载模块

    • 当加载一个模块时,内部自动创建 script 标签去请求并执行相应模块的代码

umd

对应的规范是 UMD,即通用模块定义规范

此格式打包结果,可见具有较多条件(三元)运算符

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.demo = {}));
})(this, (function (exports) { 'use strict';
  const add = (...arr) => {
      return arr.reduce((acc, item) => acc + item, 0);
  };
  exports.add = add;
  Object.defineProperty(exports, '__esModule', { value: true });
}));

其判断:CommonJS ---> AMD ---> 其他

在定义模块时,检查当前使用环境和模块的定义方式

  • 集合 CommonJS、CMD规范于一身,可理解成一种规范集合

  • 适用于浏览器端、服务器端

es

es格式打包结果较简洁,基本上与示例代码一致

const add = (...arr) => {
    return arr.reduce((acc, item) => acc + item, 0);
};
export { add };

对应的规范就是目前主流浏览器环境遵循ES Modules 规范

ESM 特点:

  • 在 ECMAScript 2015(ES6)后定义的模块规范

  • exports 使得模块可让外界引用,而外界引用通过 import

    有import就有对应要有export,如:

    export { add };
    
    import { add } from 'es.js'
    
  • 需要把 type="module" 放到 <script> 标签中,来声明此 js 文件是一个模块

system

SystemJS 加载器格式,支持 AMD、CommonJS、ES Modules 等各种格式的JS模块加载

System.register('demo', [], (function (exports) {
  'use strict';
  return {
    execute: (function () {
      const add = exports('add', (...arr) => {
          return arr.reduce((acc, item) => acc + item, 0);
      });
    })
  };
}));

链接传送门

# JavaScript modules 模块

# Webpack 究竟解决了什么问题

# 什么是【UMD】 

# 前端工程化:CommonJS、AMD、CMD、UMD和ES Modules的区别

# SystemJS简介

Last but not least

如有不妥,请多指教~