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 服务端、浏览器端等 )下模块化的实现与应用 。

相关推荐
蓝胖子的多啦A梦2 分钟前
Vue+element 日期时间组件选择器精确到分钟,禁止选秒的配置
前端·javascript·vue.js·elementui·时间选选择器·样式修改
夏天想4 分钟前
vue2+elementui使用compressorjs压缩上传的图片
前端·javascript·elementui
The_cute_cat6 分钟前
JavaScript的初步学习
开发语言·javascript·学习
海天胜景9 分钟前
vue3 el-table 列增加 自定义排序逻辑
javascript·vue.js·elementui
今晚打老虎z13 分钟前
dotnet-env: .NET 开发者的环境变量加载工具
前端·chrome·.net
用户38022585982418 分钟前
vue3源码解析:diff算法之patchChildren函数分析
前端·vue.js
烛阴23 分钟前
XPath 进阶:掌握高级选择器与路径表达式
前端·javascript
小鱼小鱼干27 分钟前
【JS/Vue3】关于Vue引用透传
前端
JavaDog程序狗29 分钟前
【前端】HTML+JS 实现超燃小球分裂全过程
前端
独立开阀者_FwtCoder34 分钟前
URL地址末尾加不加 "/" 有什么区别
前端·javascript·github