目前现代化的JS开发方式大行其道。撸代码时,你可能也会不了解为啥要应用这些简约的现代化工具。
接下来咱们就来一起学习下js模块、模块化解决方案、模块加载器和模块打包器的区别。
本文的次要用意是帮大家疾速了解古代前端JS开发的概念,并不会深刻去探讨每种工具和模式。
什么是模块?
模块是一块可复用的代码。它封装了实现细节,对外提供公开的API以便其余代码能够轻松加载和应用。
为啥要应用模块?
从技术利用的角度来说,模块并不是必须的。
模块化是一种代码组织形式。开发者们从六七十年代开始在各种编程语言中以多种形式应用了。
在JS中,模块化现实状况下应该容许咱们:
- 形象代码:将性能实现抽离到特定的库,使用者无需去关注简单的外部实现逻辑。
- 封装代码:如果不心愿代码被批改,则将代码暗藏在模块中。
- 复用代码:以防止反复写雷同的代码。
- 治理依赖:轻松扭转依赖而无需重写咱们的代码。
ES5中的模块模式
ES5级更早之前的版本,在设计时并没有思考到模块机制。随着工夫的推移,开发者想出了多种形式在JS中模仿实现模块机制。
这里咱们介绍2个简略的实现形式。
Immediately Invoked Function Expression(IIFE)
(function(){ // ...dosomething})()
IIFE
实质上定义一个立刻执行的匿名函数。
要留神的是匿名函数是被括号围起来的,在JS中,如果代码行是以关键字function
开始,示意申明一个函数。
// 函数申明function(){ console.log('test');}
间接调用函数申明会报错:
// 间接调用函数申明function(){ console.log('test');}()// => Uncaught SyntaxError: Unexpected token )
通过给函数加上圆括号,使其成为一个函数表达式:
// 函数表达式(function() { console.log("test");});// => 返回 function(){ console.log('test') }
函数表达式返回一个函数,因而咱们能够间接调用它:
// IIFE:立刻执行函数表达式(function() { console.log("test");})();// => console中打印字符串 'test'
IIFE
带来的益处:
- 将代码的复杂性封装到
IIFE
中,咱们无需关注其实现。 - 在
IIFE
中定义变量,防止对全局作用域造成净化。
不过,IIFE
没有提供依赖管理机制。
Revealing Module partten
Revealing Module partten
与 IIFE
相似,不同之处是它把IIFE
的返回值赋给一个变量。
// 将模块导出为一个全局变量var singleton = (function() { // 外部逻辑 function sayHello() { console.log("Hello"); } // 对外裸露的 API return { sayHello: sayHello, };})();
这里咱们没有把匿名函数放在括号内,因为function
关键字没有在行首。
// 拜访模块的办法singleton.sayHello();// => Hello
除了导出单例对象,模块还能够导出一个构造函数:
// 将模块裸露为全局变量var Module = function() { // 外部逻辑 function sayHello() { console.log("Hello"); } // 导出 API return { sayHello: sayHello, };};
留神,咱们没有在申明时立刻执行函数。
相同,咱们应用模块的构造函数来实例化一个模块:
var module = new Module();
拜访模块的公共 API:
module.sayHello();// => Hello
这种模式的益处与 IIFE
雷同,不过同样它也不能给开发者提供依赖治理的机制。
随着JS的倒退,衍生了很多种模块定义语法,每种语法都有各自的长处和毛病。咱们称之为模块格局。
模块格局
模块格局是咱们用来定义一个模块的语法。
ES6呈现之前,JS没有提供官网的模块定义语法。因而,聪慧的开发者们提出了多种定义模块的形式。
一些广为应用的模块格局有:
- Asynchronous Module Definition(AMD)
- CommonJS
- Universal Module Definition(UMD)
- System.register
- ES6 Module
上面咱们疾速过一下每种模块定义的形式。
AMD
AMD模块运行在浏览器环境下,它应用define
函数来定义模块。
define(['dep1', 'dep2'], function (dep1, dep2) { return function () {};});
CommonJS
CommonJS模块运行在nodejs环境下,它应用 require
治理依赖,module.exports
来定义模块。
var dep1 = require('./dep1'); var dep2 = require('./dep2');module.exports = function(){ // ...}
UMD
UMD模块能够同时运行在浏览器和Nodejs环境下。
(function (root, factory) { if (typeof define === 'function' && define.amd) { define(['b'], factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory(require('b')); } else { root.returnExports = factory(root.b); }}(this, function (b) { return {};}));
System.register
System.register
模块的设计用意是在ES5中反对ES6 模块语法:
import { p as q } from './dep';var s = 'local';export function func() { return q;}export class C { }
ES6 module
从ES6开始,js提供了原生的模块机制。它应用export
向外裸露API:
// lib.js// 对外导出sayHelloexport function sayHello(){ console.log('Hello');}// 不对外导出function somePrivateFunction(){ // ...}
应用import
导入另一个模块中的API:
import { sayHello } from './lib';sayHello();
为导入的API应用别名:
import { sayHello as say } from './lib';say(); // => Hello
导入整个模块:
import * as lib from './lib';lib.sayHello(); // => Hello
模块中定义默认导出项:
// lib.js// 导出默认函数export default function sayHello(){ console.log('Hello');}// 导出非默认函数export function sayGoodbye(){ console.log('Goodbye');}
而后应用如下形式导入:
import sayHello, { sayGoodbye } from './lib';sayHello(); // => HellosayGoodbye(); // => Goodbye
导出所有你想导出的内容:
// lib.js// 导出默认函数export default function sayHello(){ console.log('Hello');}// 导出非默认函数export function sayGoodbye(){ console.log('Goodbye');}// 导出简略值export const apiUrl = '...';// 导出对象export const settings = { debug: true}
遗憾的是,不是所有的浏览器都反对了原生的模块语法。不过咱们在代码中应用原生的模块语法,而后借助babel等转译工具将ES6模块语法转译成ES5所反对的AMD或CommonJS等模块语法。
模块加载器
模块加载器负责解析和加载以特定模块语法定义的模块。模块加载器在运行时执行:
- 首先在浏览器中加载模块加载器
- 通知模块加载器利用的js入口文件
- 加载器去下载并解析js入口文件
- 加载器按需下载所有的js文件
关上浏览器的调试面板,你会看到加载器按需加载的js文件。
以下是两个常见的模块加载器:
- RequireJS:AMD模块加载器
- SystemJS:CommonJS、AMD、UMD以及System.register 模块加载器
模块打包器
打包器能够替换加载器。不过与加载器不同,打包器是在构建时运行:
- 应用打包器生成一个js文件(例如app.js)
- 在浏览器中加载该文件
如果你在浏览器的调试工具的网络面板中,只会看到浏览器只加载了一个文件。浏览器中不须要模块加载器。所有的代码都被打包在了一个js文件中。
在按需加载的场景下,打包器通常也会提供模块加载器。以按需申请对应的js文件。
列举几个常见打包器:
- webpack
- rollup
- browserify
总结
为了更好地了解古代JS开发环境中的各种工具,了解模块、模块格局、模块加载器和模块打包器之间的区别很重要。
模块是可复用的代码片段,它封装了实现细节,并对外裸露一个公开API,以便其余代码加载应用。
模块格局是用来定义模块的语法。很早之前就曾经呈现了各种模块格局,如AMD,CommonJS,UMD和System.register。从ES6开始原生提供了模块定义语法。
模块加载器在运行时解释并加载以特定模块格局编写的模块,常见的加载器有RequireJS和SystemJS。
模块打包器能够代替模块加载器。它在构建时生成一个 蕴含所有代码的JS包。常见的打包器有webpack、rollup和browserify。
好啦,读到这里置信你曾经对古代JS开发的常识有了更好地了解了。