JS 模块化

一、JS 中的模块化规范

要明白我们的打包工具究竟做了什么,首先必须明白的一点就是 JS 中的模块化。

在 ES6 规范之前,我们有 CommonJS、AMD 等主流的模块化规范。

1、CommonJS(node.js 原生 & 同步加载 & 多次加载一次执行)

Node.js 是一个基于 V8 引擎,事件驱动 I/O 的服务端 JS 运行环境。

在 2009 年刚推出时,它就实现了一套名为 CommonJS 的模块化规范。

在 CommonJS 规范里:

  • 每个 JS 文件都是一个模块(module),每个模块内部都可以使用 require 函数和 module.exports 对象,来对模块进行导入和导出。

示例代码

javascript 复制代码
// index.js
require('./moduleA');
const str = require('./moduleB');
console.log('index', str);

// moduleA.js
const timestamp = require('./moduleB');
setTimeout(() => console.log('moduleA', timestamp), 1000);

// moduleB.js
module.exports = new Date().getTime();

执行说明

  • index.js 代表的模块通过执行 require 函数,分别加载了相对路径为 ./moduleA./moduleB 的两个模块,同时输出 moduleB 模块的结果。
  • moduleA.js 文件内也通过 require 函数加载了 moduleB.js 模块,在 1s 后也输出了加载进来的结果。
  • moduleB.js 文件内部定义了一个时间戳,使用 module.exports 对象导出。

运行结果(执行 node index.js

diff 复制代码
index 1738743973675
moduleA 1738743973675

模块特性

  • 每个模块都是单例的,当一个模块被多次加载的时候,最终执行实际上只会被执行一次。

2、AMD(浏览器端 & 异步 & 多次加载一次执行)

另一个为 WEB 开发者所熟知的 JS 运行环境就是浏览器了。浏览器并没有提供像 Node.js 里一样的 require 方法。

不过,受到 CommonJS 模块化规范的启发,WEB 端还是逐渐发展起来了 AMD、SystemJS 规范等适合浏览器端运行的 JS 模块化开发规范。

AMD 全称 Asynchronous module definition,意为异步的模块定义,不同于 CommonJS 规范的同步加载,AMD 正如其名所有模块默认都是异步加载,这也是早期为了满足 web 开发的需要,因为如果在 web 端也使用同步加载,那么页面在解析脚本文件的过程中可能使页面暂停响应。

示例代码

javascript 复制代码
// index.js
require(['moduleA', 'moduleB'], function(moduleA, moduleB) {
    console.log('index', moduleB);
});

// moduleA.js
define(function(require) {
    const timestamp = require('moduleB');
    setTimeout(() => console.log('moduleA', timestamp), 1000);
});

// moduleB.js
define(function(require) {
    return new Date().getTime();
});

使用说明

如果想要使用 AMD 规范,我们还需要添加一个符合 AMD 规范的加载器脚本在页面中,符合 AMD 规范实现的库很多,比较有名的就是 require.js

3、ESModule

前面我们说到的 CommonJS 规范和 AMD 规范有这么几个特点:

  1. 语言上层的运行环境实现的模块化规范,模块化规范由环境自己定义。
  2. 相互之间不能兼容使用。例如不能在 Node.js 运行 AMD 模块,不能直接在浏览器运行 CommonJS 模块。

在 EcmaScript 2015(也就是我们常说的 ES6)之后,JS 有了语言层面的模块化导入导出语法以及与之匹配的 ESModule 规范。

使用 ESModule 规范,我们可以通过 importexport 两个关键词来对模块进行导入与导出。

示例代码(基于 ESModule 规范改写之前的例子)

javascript 复制代码
// index.js
import './moduleA';
import str from './moduleB';
console.log(str);

// moduleA.js
import timestamp from './moduleB';
setTimeout(() => console.log('moduleA', timestamp), 1000);

// moduleB.js
export default new Date().getTime();

Webpack 配置(webpack.config.js

javascript 复制代码
const path = require('path');

module.exports = {
    mode: 'none',
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'index.bundle.js', // 注意 filename 是小写不是 fileNane
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader', // 所有的 js 文件进 webpack 处理的过程中都使用 babel 进行编译
                    options: {
                        presets: ['@babel/preset-env'] // 支持使用环境参数来处理不同的内容
                    }
                }
            }
        ]
    }
};

依赖安装与运行

bash 复制代码
# 安装依赖
yarn add webpack webpack-cli @babel/core babel-loader @babel/preset-env 

# 编译打包
./node_modules/.bin/webpack 

# 启动静态服务器(可使用 Python 简单启动)
python3 -m http.server 

关于 JS 运行环境与解析器

每个 JS 的运行环境都有一个解析器,否则这个环境也不会认识 JS 语法。

它的作用就是用 ECMAScript 的规范去解释 JS 语法,也就是处理和执行语言本身的内容。

例如按照逻辑正确执行 var a = "123"; function func() { console.log("hahaha"); } 之类的内容。

在解析器的上层,每个运行环境都会在解释器的基础上封装一些环境相关的 API。

例如 Node.js 中的 global 对象、process 对象,浏览器中的 window 对象、document 对象等等。

这些运行环境的 API 规范各自规范的影响,例如浏览器端的 W3C 规范,它们规定了 window 对象和 document 对象上的 API 内容,以使得我们能让 document.getElementById 这样的 API 在所有浏览器上运行正常。

以上就是对 JS 模块化相关规范(CommonJS、AMD、ESModule )以及 JS 运行环境、解析器等相关内容的梳理 ,通过不同规范的对比,能更清晰理解在不同场景(Node.js 服务端、浏览器端等 )下模块化的实现与应用 。

相关推荐
#麻辣小龙虾#2 小时前
基于vue3.0开发一款【固废与废气运维管理系统】(支持源码)
前端·vue.js·vue3
Cosolar2 小时前
Docsify零构建文档站完全指南:从快速搭建到企业级部署
前端·开源·github
weixin_471383032 小时前
Taro-02-页面路由
前端·taro
星栈独行2 小时前
Makepad 应用如何读文件、调接口、保存数据
前端·程序人生·ui·rust·github
IT_陈寒3 小时前
Vite热更新失效?可能你在用Windows
前端·人工智能·后端
烬羽4 小时前
后端返回的 JSON 字符串,浏览器怎么"看懂"的?——Ajax 全链路拆解
javascript
tedcloud1234 小时前
taste-skill部署教程:打造个性化AI推荐工作流
服务器·前端·人工智能·系统架构·edge
xinhuanjieyi4 小时前
html修复游戏种太阳错误
前端·游戏·html
半个落月4 小时前
一个新手用 Bun + Axios 调通 DeepSeek API 的实践记录
javascript
不好听6134 小时前
深入理解链表:线性数据结构的另一面
javascript·数据结构