1、模块化标准出现以前使用的模块化方案:
1)文件划分: 将不同的模块定义在不同的文件中,然后使用时通过script标签引入这些文件
缺点:
模块变量相当于是定义在全局的,容易造成变量名冲突(即不同模块存在相同名称的变量); 模块之间的依赖关系和加载顺序不好管理;如果模块之间存在依赖关系,则在引入文件时需要手动调整,否则会出现运行错误 如果引入了多个模块,则无法清楚知道某个变量来自哪个模块,需要逐个模块进行排查;
2)命名空间:
window. moduleA = {
data : 'A' ,
...
}
window. moduleB = {
data : 'B' ,
...
}
...
console. log ( moduleA. data)
console. log ( moduleB. data)
优点:
解决了文件划分造成的全局变量命名冲突的问题; 使用时能清楚知道某个变量来自哪个模块,方便开发调试
3)IIFE-立即执行函数,使变量私有化,只对外界暴露变量的访问入口
(
function ( ) {
const data = 'moduleA'
function method ( ) {
console. log ( data + ' execute' ) ;
}
window. moduleA = {
method
}
}
) ( )
(
function ( ) {
const data = 'moduleB'
function method ( ) {
console. log ( data + ' execute' ) ;
}
window. moduleB = {
method
}
}
) ( )
...
< body>
< script src= "./js/module.a.js" > < / script>
< script src= "./js/module.b.js" > < / script>
< script>
console. log ( moduleA. data) ;
moduleB. method ( )
< / script>
< / body>
优点:
解决了全局变量命名冲突的问题 能清楚知道使用的变量来自哪个模块,(即变量作用域清晰了)
总结:命令空间和IIFE均没有解决模块存在依赖关系时,模块加载顺序的问题、
2、主流的前端模块规范
1)CommonJs规范:
使用module.exports导出模块;require()导入模块;
const data = 'hello CommonJs'
function getData ( ) {
return data
}
module. exports = {
getData
}
const { getData } = require ( './module.a' )
console. log ( getData ( ) )
特点
同步加载模块;即模块加载是阻塞式的,需等待模块加载完才能执行后续操作; 适用于服务器端;因为模块是在服务器本地无需进行网络IO,另外在服务启动时才会进行模块加载,而通常服务启动后就会一直运行,所以同步加载对服务的性能影响不大;但是在浏览器环境中,同步模块加载会造成浏览器JS解析过程的阻塞,导致页面加载速度缓慢; 模块缓存,避免重复加载和执行
2)AMD模块规范(Asynchronous Module Definition)
define ( [ './js/print.js' ] , function ( module ) {
module. print ( 'AMD' )
} )
define ( [ ] , function ( ) {
return {
print : function ( msg ) {
console. log ( 'print ' + msg) ;
}
}
} )
...
< body>
< ! -- 由于AMD 没有得到浏览器的原生支持,所以使用AMD 规范时,需要引入第三方库,requireJS -- >
< ! -- data- main= "./js/main" :指定主模块路径,标识main. js文件位于js目录下 -- >
< script src= "./lib/require.js" data- main= "./js/main" > < / script>
...
< / body>
特点:
异步加载模块,即模块加载时不会阻塞页面渲染 未得到浏览器的原生支持,需要借助第三方库requireJS来实现 代码书写和阅读稍显复杂,增加了开发人员的心智负担
同期出现的其他规范: CMD,UMD
CMD: 由淘宝出品的SeaJs实现,解决的问题与AMD类似;延迟执行和按需加载,即模块是在代码需要使用时才会加载执行,不会预先加载; UMD:通用模块定义,兼容AMD和CommonJs的模块化方案,可以同时运行在浏览器和Node.js环境
3)ES模块(ES Module)
由ECMAScript官方提出的模块化规范,已经得到现代浏览器的内置支持 如果在html的script标签中加上type="module"属性,那么浏览器将按照ES的模块化规范加载和解析该脚本 能够同时运行在浏览器端和Node.js环境中,拥有天然的跨平台能力 使用 export 导出模块;import导入模块;
const method = ( ) => {
console. log ( 'Hello ES Module' )
}
export {
method
}
const method = ( ) => {
console. log ( 'Hello ES Module' )
}
export default {
method
}
import { method as methodA } from './module.a.js'
methodA ( )
import B from './module.b.js'
B . method ( )
...
< body>
< ! -- type= "module" 在html中标识这个脚本文件是一个ES 模块; -- >
< script type= "module" src= "./js/main.js" > < / script>
< / body>
{
"type" : "module"
...
}
优点:
导入,导出语法简洁明了,代码可读性强 静态分析和优化;模块的依赖关系在编译时就可以确定,这使得编译器可以进行更好的静态分析,并进行代码优化,打包和压缩等操作; 模块的按需加载; 可以同时运行在浏览器和Node.js环境
参考文章: