模块化,就是将独立的功能代码封装成一个独立的文件,其他模块需要使用,在进行引用。
模块化有利于代码的拆分和架构上的解耦,模块化在服务端领域已经早已成熟,nodejs 也已经支持模块化。
而在浏览器上,js 脚本是异步载入的,脚本按照编码顺序依次执行,依赖关系只能按照编码顺序来控制。
commonjs
伴随 nodejs 而诞生的 commonjs 规范。commonjs 规范应用于 nodejs 应用中,在 nodejs 应用中每个文件就是一个模块,拥有自己的作用域,文件中的变量、函数都是私有的,与其他文件相隔离。
CommonJS规范规定,每个模块内部, module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
let name = 'now';let age = 18;let fun = () => {console.log('into fun');name = 'change'}module.exports = {name,fun}console.log(module)
var { name, fun } = require('./util/index.js')

- module.id 模块的识别符,通常是带有绝对路径的模块文件名。
- module.loaded 返回一个布尔值,表示模块是否已经完成加载。
- module.parent 返回一个数组,表示调用该模块的模块,如果改该模块没有被引用,那么 parent 就是 null module.children 返回一个数组,表示该模块要用到的其他模块。
- module.exports 表示模块对外输出的值。
- module.paths 这个用于 require 查找该文件的位置。
在开发中我们常使用的就是 module.exports , 通过 module.exports 输出的对象就是引用方 require 出来的值
require
既然有 module.exports 导出,那么就有与之相对应的 require 导入
module.exports 和 exports
我们还可以导出 exports 直接使用,exports 是已经定义的常量,在导出的时候不能在给它定义
隔离性
commonjs 规范是在运行时加载的,在运行时导出对象,导出的对象与原本模块中的对象是隔离的
在浏览器中使用 commonjs 规范 browserify
因为浏览器中缺少 module exports require global 这个四个变量, 所以在浏览器中没法直接使用 commonjs 规范,非要使用就需要做个转换,使用 browserify ,它是常用的 commonjs 转换工具,可以搭配 gulp webpack 一起使用
ES6 模块化
ECMA推出了官方标准的模块化解决方案,使用 export 导出,import 导入
ES6 支持异步加载模块 的模块不是对象,而是在编译的时候就完成模块的引用,所以是编译时才加载的。
import只能在模块顶层导入,require是动态载入的
浏览器中使用
不使用 webpack ,使用 gulp 等构建流工具,那么我们需要使用babel将 es6 转成 es5 语法
ES6 模块规范和 commonjs 规范 运行机制的区别
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
- 运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
- 编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
AMD-require.js 和 CMD-sea.js
聊到 AMD 和 CMD 这两个规范都离不开 require.js 和 sea.js,这是早些年,为了解决浏览器异步加载模块而诞生的方案。随着打包工具的发展,commonjs和es6都可以在浏览器上运行了,所以 AMD、CMD 将逐渐被替代。
AMD规范的模块化:用 require.config()指定引用路径等,用define()定义模块,用require()加载模块。
CMD规范的模块化:用define()定义模块, seajs.use 引用模块。
模块兼容处理
const appJsBridge = function(){};if (typeof define === "function") {// AMD CMDdefine(function() {return appJsBridge;})} else if (typeof exports != "undefined") {// commonjsmodule.exports = appJsBridge;} else {// 没有模块化window.appJsBridge = appJsBridge;}
新问题:打包代码怎么去除定义了但是没有使用的模块
静态分析
Tree-Shaking “树摇”,消除无用的js代码。无用代码消除广泛存在于传统的编程语言编译中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为DCE(dead code elimination)。
- 函数消除
- 类消除
Dead Code 一般具有以下几个特征
- 代码不会被执行,不可到达
- 代码执行的结果不会被用到
- 代码只会影响死变量(只写不读)
编译打包工具实现原理: js代码转成AST,然后做代码死区分析,消除无用代码块。
