前端模块化概述
模块作为现代化编程的基础复用单元,在"搬砖"的日常工作中可以起到明显的加速作用。随着前端工程化的不断完善,模块将在前端扮演越来越重要的角色。
当前的模块系统大体分为iife
、cjs
、amd
、umd
、esm
、system
iife(Immediately Invoked Function Expression)
即立即执行函数,是最早的模块编写方式,格式如下
(function () {
var name = "Soda";
})();
// 无法从外部访问变量 name
name // 抛出错误:"Uncaught ReferenceError: name is not defined"
表达式中的变量无法从外部访问。如果模块需要向外暴露变量,则可通过返回值直接返回。
var result = (function() {
var name = "Soda";
return name;
})();
//通过外部的变量接收iife的返回值
result // "Soda"
这便是最简单的模块,通过iife
将代码与全局"割裂",仅通过返回值来定制外部可访问的内容。在全局环境中用户可自定义模块的名称,避免了全局变量的污染。
cjs(CommonJS)
node.js
所参照的模块化标准,CommonJS
模块以文件为单位定义模块,其内部的变量、函数、类都是私有的,对其他文件不可见。在模块内部,module
变量代表当前模块,module.exports
代表对外的接口,exports
表示对module.exports
的引用,使用exports.<name>
也可以实现向外暴露属性或方法的目的。通过全局的require()
加载某个模块实际上是加载该模块的module.exports
属性。
模块的导出:
function func1(name) {
console.log(`Hello ${name}`);
}
function func2() {
console.log('function 2');
}
// 使用exports.<functionName>向外导出方法
exports.func1 = func1;
// 使用module.exports向外导出方法
module.exports = { func2 }
// 注:当两种导出方法同时存在时 由于module.exports所指向的对象地址发生了改变,会导致 exports 导出的方法失效
模块的导入
const lib = require('./lib');
lib.func2();// function 2
// 当仅有exports存在时
lib.func1('world') // Hello world
CommonJS
模块的特点如下:
优点:
所有代码都运行在模块作用域,不会污染全局作用域
模块可以多次加载,但只会在第一次加载时运行一次,运行的结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清楚缓存
模块加载的顺序,按照其在代码中出现的顺序。缺点
在服务端由于所有代码都存放在本地磁盘,读取模块的时间几乎可以忽略。但浏览器端运行由于网络和模块大小的因素,可能会有加载时间过长的问题,可能会导致浏览器处于"假死"状态,因此CommonJS
更适用于服务器端
amd(Asynchronous Module Definition)
异步模块定义
define(id?, dependencies?, function($) {
return function() {}
})
AMD的模块引入由define方法定义,在define API中
id: 模块名称,或者模块加载器请求的指定脚本的名字
dependencies: 模块所依赖的模块组
function: 为模块初始化所要执行的函数或对象,如果为函数,它应该只被执行一次,如果是对象,此对象应该为模块的输出值
AMD模块的特点如下
优点
依赖异步加载,不会造成浏览器加载卡顿,适用于浏览器
缺点
编写复杂
umd(Universal Module Definition)
通用模块规范
其本质是cmj
和amd
以及iife
的综合体。使其可以在任何开发环境中都可以以最优方式运行。
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
//AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
//Node, CommonJS之类的
module.exports = factory(require('jquery'));
} else {
//浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
//方法
function myFunc(){};
//暴露公共方法
return myFunc;
}));
其流程如下
先判断是否支持 AMD(define 是否存在),存在则使用 AMD 方式加载模块;
再判断是否支持 Node.js 模块格式(exports 是否存在),存在则使用 Node.js 模块格式;
前两个都不存在,则将模块公开到全局(window 或 global);
esm(ECMA Script Modules)
iife
、cjs
、amd
、umd
均为社区提供的模块化方案,历史上,JavaScript一直没有模块系统,直到ES6
在语言标准的层面上实现了模块化标准。其设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
Commonjs
和AMD
模块,都只能在运行时确定这些东西。
ECMA Script
模块是打包JavaScript代码的官方标准。使用import
和export
语句定义模块。配合现代化的打包工具,esm
将会有更多有趣的特性
默认导出
import React from 'react';
export default class test extends React.Component {
render() {
return (<div>Hello</div>)
}
}
导入
import Component from './lib';