1 ES Module 规范

ES Module 是目前使用较多的模块化规范,在 Vue、React 中大量使用,大家应该非常熟悉。TypeScript 中的模块化与 ES 类似。

1.1 导出模块

导出模块有两种方式:按需导出默认导出

按需导出是使用 export 关键字,将需要导出的成员(变量、函数、对象等)依次导出:

export const xxx = ''export const xxx = () => {}

一个模块中可以有多个按需导出,但只能有一个默认导出。假设默认导出 A 模块,当 B 模块直接导入模块 A 时,其导入的值就是模块 A 默认导出的值。

export default {}

1.2 导入模块

导入按需导出的模块:

import { xxx } from 'xxx'

上面语法中,花括号 {} 中的内容必须与导出的名称一致。

如果按需导出的成员较多,可以使用 as 一次性导入:

import * as xxx from 'xxx'

导入默认导出的模块:

import xxx from 'xxx'

也可以直接导入一个模块,并直接执行该模块的代码:

import 'xxxxx'

2 使用 Webpack 编译 ES Module2.1 初始化项目

创建 modules 目录,里面存放两个模块 module1.jsmodule2.js。入口文件与 modules 目录同级,名为 index.js。文件和目录结构如下:

05_ESM/  |- modules/    |- module1.js    |- module2.js  |- index.js  |- index.html

2.2 实现两个模块

module1.js 使用按需导出变量 str1 和函数 fun1,默认导出 user 对象:

console.log('in module1')export const str1 = 'hello module1'export const fun1 = (msg) => {  return `module1:${msg}`}const user = {  name: 'zhangsan',  age: 30}export default user

module2.js 使用默认导出,导出一个对象,这个对象包括属性 str2 和方法 fun2

console.log('in module2')const str2 = 'hello module2'const fun2 = (msg) => {  return `module2:${msg}`}export default {  str2,  fun2}

2.3 实现入口文件

在入口文件 index.js 中导入两个模块。由于 module1.js 是按需导出,故导入时需要使用 {}module2.js 是默认导出,故此处可以直接导入:

import { str1, fun1 } from './modules/module1'import m2 from './modules/module2'console.log(str1)console.log(fun1('程序员优雅哥'))console.log(m2.str2)console.log(m2.fun2('youyacoder'))

2.4 入口 HTML

创建 index.html 文件,使用 script 标签导入 index.js

在浏览器中访问 index.html 文件,控制台会提示如下错误:

Uncaught SyntaxError: Cannot use import statement outside a module (at index.js:1:1)

这是由于浏览器不认识 ESM 语法。可以使用 babel 将 ES6 语法编译为 ES5 的语法,然后使用 browserify 进行打包;也可以使用 webpack 打包。此处我使用 webpack 5

2.5 使用 Webpack 打包

使用 npmyarn 初始化项目:

yarn init -y

安装 webpackwebpack-cli 为开发依赖:

yarn add webpack webpack-cli -D 

使用 webpack 打包:

npx webpack ./index.js -o ./dist/ --mode development

上面的打包命令直接在命令中配置参数,省略了额外的配置的文件。该命令指定了打包的入口文件为:index.js;输出的目录为 dist 目录,打包模式为 development。关于 webpack 5 的使用,有兴趣的可以看优雅哥的 webpack 5 系列文章。

执行完打包命令后,会生成 dist 目录,并且在该目录中有个 main.js 文件。

index.html 中删除之前引入的 index.js,替换为 dist/main.js

重新在浏览器中访问 index.html, 控制台输出如下:

3 ES Module 总结

导出模块:

  • 默认导出:export default xxx
  • 按需导出 export const xxx

导入模块:

  • 默认导入: import xxx from ‘xxx’

  • 按需导入 import { xxx } from ‘xxx’

4 JS 模块化 4 大规范总结

前面优雅哥依次写了模块化的发展史,模块化的规范(可进主页查看每个规范详细版本),现进行一个大汇总方便大家查阅和总结:

01- 模块化前传

02 – Common JS 规范

03 – AMD 规范(Require JS 实现)

04 – CMD 规范 (Sea JS 实现)

05 – ESM 规范

模块化相关 demo 源码可以 github 搜索关键词 js-module-demo 或联系 程序员优雅哥 获取。

源码目录如下:

js-module-demo/|- 01_Histry/模块化发展史|- 02_CommonJS/CommonJS 规范|- 03_AMD/ADM 规范|- 04_CMD/CMD 规范|- 05_ESM/ESM 规范

各个模块化规范有相似之处,也有差异,模块定义与模块加载的语法如下:

4.1 Common JS 规范

定义模块的语法:

// 暴露函数module.exports = function () {}// 暴露对象module.exports = {  xxx: () => {}}exports.xxx = {}exports.xxx = function() {}

加载模块的语法:

const xxx = require('xxxx')

4.2 AMD 规范

定义模块的语法:

define(id?, dependencies?, factory)

加载模块的语法:

require([module], callback)

4.3 CMD 规范

定义模块的语法:

// 定义模块define(function(require, exports, module) {// 使用 exports 导出模块exports.xxx = xxx//也可以使用 return 导出模块// return xxx})

加载模块的语法:

// 同步加载模块const m1 = require('../xxx')// 异步加载模块require.async('../xxx', function (m2) {})

4.4 ESM 规范

导出模块:

// 按需导出export const xxx = ''export const xxx = () => {}// 默认导出export default xxx

导入模块:

import { xxx, yyy } from 'xxx'import * as xxx from 'xxx'import xxx from 'xxx'import 'xxx'

感谢你阅读本文,如果本文给了你一点点帮助或者启发,还请三连支持一下,点赞、关注、收藏,作者会持续与大家分享更多干货