共计 4870 个字符,预计需要花费 13 分钟才能阅读完成。
模块化的背景
- 前端模块化是一种规范,不是实现
- 了解模块化是了解前端工程化的前提
- 前端模块化是前端我的项目规模化的必然结果
什么是前端模块化?
前端模块化就是将简单程序依据标准拆分成若干模块,一个模块包含输出和输入。而且模块的外部实现是公有的,它通过对外裸露接口与其余模块通信,而不是间接调用。当初在 HTML 文件中能够援用的 script 包含脚本和模块,其中模块具备更高的开发效率(可读性强、复用性高),而脚本具备更高的页面性能,因为模块绝对文件多,加载速度慢。须要留神的是模块在浏览器中运行会存在兼容性的问题,在 script 申明 type=”module” 即可应用 ES Module 模块化标准。
模块化的进化过程
- 全局 function 模式,将不同的性能封装成不同的函数,缺点是容易引发全局命名抵触。比方刚开始在一个 js 文件中中定义一个一个的函数作为模块,然而这些函数挂载 windows 上的,净化全局作用域,我在另一个 js 文件中定义同名的函数就会导致命名抵触,而且无奈治理模块依赖关系,进而早起模块化齐全依附约定。
function api(){
return {
data:{
a:1,
b:2
}
}
}
- 全局 namespace 模式,约定每个模块裸露一个全局对象,所有的模块成员裸露在对象下,毛病是内部可能批改模块外部数据。将函数放在对象模块中挂载到 window 上,然而这样内部也能批改对象外部的属性。
var __Module={api(){
return {
data:{
a:1,
b:2
}
}
}
}
那么咱们能够通过函数作用域加闭包的个性来解决。
- IIFE 模式,将模块成员放在函数公有作用域中,通过自执行函数创立闭包,对于裸露给内部的成员通过挂载到全局对象这种形式实现。这种形式实现了公有成员的概念,缺点是无奈解决模块间相互依赖问题。
(function(){
var a =1;
function getA(){return a;}
function setA(a){window.__module.a = a;}
window.__module = {
a,
getA,
setA
}
})();
- IIFE 模式加强,反对传入自定义依赖,缺点是多依赖传入时,代码浏览艰难,无奈反对大规模的模块化开发,无特定语法反对,代码简陋。
(function(global){
var a =1;
function getA(){return a;}
function setA(a){window.__module.a = a;}
global.__Module_API = {
a,
getA,
setA
}
})(window)
(function(global,moduleAPI){
global.__Module = {setA:moduleApi.setA}
})(window,window.__Module_API)
以上就是晚期没有工具和标准的状况下对模块化的落地形式。
通过约定的形式去做模块化,不同的开发者和我的项目会有不同的差别,咱们就须要一个规范去标准模块化的实现形式。针对与模块加载的形式,以上办法都是通过 script 标签手动引入的形式,模块加载不受代码的管制,工夫久了保护会十分麻烦。那么就须要一个模块化的规范和主动加载模块的根底库。
CommonJS 标准
介绍
CommonJS 标准是 Node.js 默认模块标准,他规定了每个文件就是一个模块,每个模块有本人的作用域,它的模块加载采纳同步加载形式,加载模块的时候必须模块加载实现后再执行后续的代码。它通过 require 来加载模块,通过 exports 或 module.exports 输出模块。
特点
- 所有代码都运行在模块作用域,不会净化全局作用域
- 模块能够屡次加载,第一次加载时会运行模块,模块输入后果会被缓存,再次加载时,会从缓存后果中间接读取模块输入后果
- 模块加载的程序是依照其在代码中呈现的程序
- 模块输入的值是值得拷贝,相似 IIFE 计划中的外部变量
打包
CommonJS 要想在浏览器中应用的话须要应用 browserify 来打包。
browserify 打包原理次要是:通过自执行函数实现模块化,将每个模块编号,存入一个对象,每个模块标记依赖模块。它外部实现了 require 办法,外围是通过 call 办法调用模块,并传入 require、module、exports 办法,通过 module 存储模块信息,通过 exports 存储模块输入信息。
AMD 标准
AMD(Asynchronous Module Definition)标准采纳异步加载模块,容许指定回调函数。Node 模块通常都位于本地,加载速度快,所以实用于同步加载,然而在浏览器运行环境中,用同步加载会阻塞浏览器渲染,所以在浏览器环境中,模块须要申请获取,实用于异步加载。因而就诞生了 AMD 标准,用于异步加载,其中 require.js 就是 AMD 的一个具体实现库。目前不论是 CMD 或是 AMD 用的都很少,在 Node 开发中通常用 CommonJS 标准,在浏览器中用 ES Module 标准。
援用了 require.js 之后,它会全局定义一个 define 函数和 require 函数,所有的模块要用 define 去定义。define 有三个参数:第一个模块名,第二个是数组用于申明模块依赖项,第三个是一个函数,函数参数与第二个参数依赖项一一对应,每一个参数为依赖项导出的成员。函数的作用能够了解为以后模块提供公有的空间,如果要向外导出成员能够通过 return 来实现。
define('module1',['jquery','./module2'],function($,module2){return {}
});
而 require 函数则用来加载模块,当调用 require 函数的时候,其外部会主动创立 script 标签来发送脚本文件的申请,并执行绝对应的模块代码。
require(['./module1'],function (module1){module1.start();
})
目前绝大部分第三方库都反对 AMD 标准,然而因为要应用频繁应用 require、define 导致 AMD 应用起来绝对简单。另外如果模块代码划分的很粗疏,那么在同一个页面中,JS 文件的申请次数绝对多,导致页面效率低下。
CMD 标准
CMD 标准整合了 CommonJS 和 AMD 的长处,通过异步加载模块。CMD 专门用于浏览器端,其中淘宝的 sea.js 就是 CMD 标准的一个具体实现库。
//CMD 标准,相似 CommonJS 标准
define(function(require,exports,module){
// 通过 require 引入依赖
var $ = require('jquery')
// 通过 exports 或者 module.exports 对外裸露成员
module.exports = function(){}
})
ESModule 标准
目前模块化标准规范曾经十分成熟对立了,在 Node.js 中遵循 CommonJS 组织模块,在浏览器端应用 ESModule 标准。目前前端模块化根本对立为 CommonJS + ESModule 标准。
ESModule 标准是目前利用最为宽泛的模块化标准,它的设计理念是在编译时就确定模块依赖关系及输入输出。而 CommonJS 和 AMD 因为采纳闭包的模式必须在运行时能力确定依赖和输出、输入。ESM 通过 import 加载模块,通过 export 输出模块。
应用 ESModule,通过给 script 标签增加 type=module 属性,就能够以 ESModule 的规范执行 JS 代码。
<script type="module"></script>
ESModule 有如下几个个性:
- ESM 主动采纳严格模式,疏忽 ’use strict’
- 每个 ESM 都是运行在独自的公有作用域中
- 在 ESM 中内部的 JS 模块是通过 CORS 的形式申请的
- ESM 的 script 标签会提早执行脚本,等同于 defer 属性
留神
在 ESM 中 export {}
它只是导出成员的语法,不是导出字面量对象,import {} from 'xxx'
,也是语法,不是解构对象,如果想导出对象能够应用 export default {}
,应用import xxx from 'xxx'
获取对象。其次通过 export {name}
导出的 name 值是援用的不是拷贝,它的值会受到导出模块外部批改的影响,而且 name 只是可读不能批改。
导入模块的用法
- 援用门路必须是残缺的文件名称不能省略
.js
扩展名,相对路径要是用./
,能够应用绝对路径和 url 的形式。
import {name} from './module.js';
import {name} from '/xxx/module.js';
import {name} from 'http://localhost:3000/module.js';
- 加载模块,并不导出成员
import './xxx.js';
- 应用 * 的形式提取所有的模块,应用 as 寄存在对象中
import * as mod from './xxx.js';
- 应用 import 函数,在运行阶段动静导入模块
import('./xxx.js').then((module)=>{console.log(module);
});
- 同时导出命名成员和默认成员
//a.js
...
export {name,age}
export default 'title'
//b.js
import {name,age,default as title} from './a.js';
// 或者
import title,{name,age} from './a.js';
导出模块的用法
通过 export 将目录下散落的模块在 index 文件中导出,不便内部应用
export {name,age} from './module.js';
如果要应用 commonjs 标准的话就要将对应的 js 文件改为 .cjs
后缀名。
Node.js 新版本反对
在 Node.js 新版本中,在 package.json 中增加 type="module"
字段就能够应用 ESModule 标准了。
CommonJS 和 ESModule 标准比照
- CommonJS 模块输入的是值得拷贝,ES6 模块输入的是值得引用
- CommonJS 模块是运行时加载,ES6 模块是编译时输入接口
- CommonJS 是单个值导出,ES6 Module 能够导出多个
- CommonJS 是同步加载,ES6 Module 是反对异步加载,通过 import()来实现
- CommonJS 的 this 是以后模块,ES6 Module 的 this 是 undefined
- CommonJS 和 ES6 Module 的语法不同
- CommonJS 是 Node.js 默认采纳的模块化标准,Node14 后默认反对 ESM。ESM 是浏览器默认采纳的模块化标准。
模块打包工具
模块化解决了咱们在简单利用当中的代码组织问题,然而随着引入模块化,又产生了新的问题。例如以下问题:
- ES Modules 存在环境兼容问题
- 通过模块化的形式划分的模块文件比拟多,且网络申请频繁
- 所有的前端资源都须要模块化,不仅仅是 JS 须要模块化,像 html 和 css 都须要模块化
针对第一个问题,咱们须要一个工具将代码进行编译,在开发阶段将新个性的代码转换为兼容绝大多数环境的代码。
针对第二个问题,将模块化的文件打包到一块。
针对第三个问题,将其余资源通过代码的形式进行管制,对立去保护。
针对以上问题就须要模块打包工具来解决,例如 webpack、parcel 和 rollup。
通过应用 webpack 就能够将零散的代码打包到 js 文件中,对于那些有环境兼容问题的代码就能够通过模块加载器 loader 去做兼容转换,他还具备代码拆分的能力,能够按咱们的须要去打包,不必放心将所有的代码打包到一块,导致文件比拟大的问题。咱们能够将利用加载过程中首次运行所必须的模块打包到一起,其余的模块独自寄存,等到利用运行过程中须要某个模块,再异步加载这个模块,从而实现增量加载或者渐进式加载。
对于资源文件 webpack 反对在 JS 中以模块化的形式去载入任意类型的资源文件。打包工具解决的是前端整体的模块化,并不单指 JavaScript 模块化。
看完感觉对您有所帮忙别忘记关注呦