前端模块化

82 阅读2分钟

前言

模块化发展历史

IIFE、AMD、CMD、CommonJS、UMD、ES Modules 模块化作用:抽取公共代码,作用域隔离,依赖隔离 各模块解决的问题及特点: IIFE是函数级别的作用域,解决了变量作用域问题,仅在该函数中使用 AMD:异步脚本加载
CMD:兼容cjs、amd
UMD:兼容cjs、amd导出
ESM:ES6模块化,适用于ES6静态编译特点\

分析模块化的思路:
# 为解决什么问题;
运行环境(浏览器、服务端常用实例); 语法;
加载特点:导入(同步、异步),导出(是否缓存);
注意事项;

cjs规范详解

运行环境:服务器端node,浏览器端webpack

语法:

(自定义模块)导出:
module.exports = myExportObj // module为模块对象
或者exports.myExportObj = myExportObj // exports为模块对象module.exports的引用,exports = 其他对象,则丢失导出对象的引用关系

导入自定义模块:const module1 = require('./module1.js')
导入第三方模块:const echarts = require('echarts')

运行特点:
  • 加载:文件即模块,模块加载同步:服务器端模块加载是运行时同步加载,浏览器模块加载是提前编译打包处理

  • 导入导出:exports = module.exports输出(不能给exports赋值,导致与module引用断开), require引入

  • 导入导出:require值会缓存,通过require引用文件时,会将文件执行一遍后,将结果通过浅克隆的方式,写入全局内存,后续require该路径,直接从内存获取,无需重新执行文件

      // a.js
      var name = 'morrain'
      var age = 18
      exports.name = name
    
      // b.js
      var a = require('./a.js')
      console.log(a.name) // 'morrain'
      a.name = 'rename'
      console.log(a.name) // 'rename'
      var b = require('./a.js')
      console.log(b.name) // 'rename'
    

对比esm,cjs运行时加载引入,esm编译阶段引入:
cjs引入时加载整个模块,生成一个对象,导出对象的方法和属性;esm通过export输出代码块,import导入代码块;
cjs导入对象会被缓存,多次导入的对象互相影响;esm导入为代码块,相当于深拷贝,不互相影响;
解决缓存问题:

delete require.cache[require.resolve("./module1.js"),require.resolve("./module2.js")]

amd规范详解

异步模块加载,实例:requireJs

语法:
导出模块
// a.js
define(function(){
	var add = function(x,y) {
		return x + y;
	};
	return {
		add : add
	}
});
导出模块
require(['app/a', 'app/b'], function (m, b){
	console.log(m.add(1,3), b);
});

cmd规范详解

整合了commonJS和AMD的导出,支持同步与异步加载,浏览器端运行,实例:seaJs

语法:
(function(self, factory) {
 if (typeof module === 'object' && typeof module.exports === 'object') {
	// 当前环境是 CommonJS 规范环境
	module.exports = factory();
 } else if (typeof define === 'function' && define.amd) {
	// 当前环境是 AMD 规范环境
	define(factory)
 } else {
	// 什么环境都不是,直接挂在全局对象上
	self.umdModule = factory();
 }
}(this, function() {
 return function() {
	return Math.random();
 }
}))

esm规范详解

var firstName = 'zhao'
var lastName = 'li'
var lastName1 = 'gao'
var lastName2 = 'yuan'
export {firstName, lastName as b, lastName1, lastName2}
export default firstName

import {firstName, lastName} from './module1' // 按需导入
import * as circle from './circle' // 默认模块的导入