前端模块化
模块化历史
- 最开始人们使用函数编程,但是这种方式,需要引入大量的js文件,并且致命的问题是不能使变量唯一化,会造成变量污染
- 之后人们开始通过命名空间的方式进行模块化开发,但是有个问题是,在外部可以访问到空间内部的变量,因为把变量挂载到了window上,这样不能保证模块内变量的唯一性
- 再后来人们开始使用闭包+自执行函数的方式进行模块化开发,这样既限制了外部任意修改内部的变量,有没有把变量挂载到全局,传参的话,可以在闭包里面把变量挂载到window上,要想获取模块内部的变量,只能在闭包里面书写获取的方法:例:
function module_A() {
var salary = 10000;
function fetchSalaryList() {
return {
statusCode: 200,
data: [
{ name: "zhangsan", salary: 2000 },
{ name: "lisi", salary: 2500 },
{ name: "wangwu", salary: 3000 },
],
};
}
function sumSalary(data) {
var total = 0;
data.forEach((item) => {
total += item.salary;
});
return total;
}
return {
// salary: salary,
fetchSalaryList: fetchSalaryList,
sumSalary: sumSalary,
getS: function () { //获取属性的方法
return salary;
},
setS: function (s) { //设置属性的方法
salary = s;
},
};
}
var _module_a = module_A();
console.log(_module_a);
_module_a.salary = 50;//这里并不能直接修改闭包里面的变量,改的只是外部的属性
console.log(_module_a.salary);
console.log(_module_a.getS());//1000
_module_a.setS(66);
console.log(_module_a.getS());//66
通过闭包与自执行函数进行模块化
//在A文件
(function (global) {
var name = "zhangsan";
function getName() {
return name;
}
global._module_B = { //这里把getName方法挂载在window._module_B上
getName,
};
})(window);
//在B文件中
(function (module_B) {
var salary = 10000;
function fetchSalaryList() {
return {
statusCode: 200,
data: [
{ name: "zhangsan", salary: 2000 },
{ name: "lisi", salary: 2500 },
{ name: "wangwu", salary: 3000 },
],
};
}
function sumSalary(data) {
var total = 0;
data.forEach((item) => {
total += item.salary;
});
return total;
}
console.log("****salary", salary);
const data = fetchSalaryList().data;
const total = sumSalary(data);
console.log("***total", total);
console.log(module_B.getName());//在这里使用A文件的模块
window._module_A = {
salary,
fetchSalaryList,
sumSalary,
};
})(window._module_B);//这里把window._module_B当作实参传递到这个模块中去,然后在这个模块中就可以使用
- 之后就是commonjs与ESModule
CommonJS特点
其原理就是闭包,是值的复制,并且只能在服务端运行,加载⽅式:同步加载 所有代码都运⾏在模块作⽤域,不会污染全局作⽤域
模块可以多次加载,但是只会在第⼀次加载时运⾏⼀次,然后运⾏结果就被缓存了,以后再加载,就直
接读取缓存结果。要想让模块再次运⾏,必须清除缓存
模块加载的顺序,按照其在代码中出现的顺序
模块输出的值是值的拷⻉:从⽽控制了数据的访问权限
3. require的内部处理流程
require命令是CommonJS规范之中,⽤来加载其他模块的命令。它其实不是⼀个全局命令,⽽是指向当
前模块的module.require命令,⽽后者⼜调⽤Node的内部命令Module._load。
Module._load = function(request, parent, isMain) {
- 检查 Module._cache,是否缓存之中有指定模块
/2. 如果缓存之中没有,就创建⼀个新的Module实例
-
将它保存到缓存
-
使⽤ module.load() 加载指定的模块⽂件,
读取⽂件内容之后,使⽤ module.compile() 执⾏⽂件代码
-
如果加载/解析过程报错,就从缓存删除该模块
-
返回该模块的 module.exports
};
⼀旦require函数准备完毕,整个所要加载的脚本内容,就被放到⼀个新的函数之中,这样可以避免污染
全局环境。该函数的参数包括require、module、exports,以及其他⼀些参数。
(function (exports, require, module, __filename, __dirname) {
// 你的代码被导⼊在这⾥
});
在浏览器中使⽤CommonJS(browserify介绍)
npm install browserify
browserify inputPath.js -o outputPath.j
ESModule规范详解
1. AMD规范介绍
AMD全称是Asynchronous Modules Definition异步模块定义,提供定义模块及异步加载该模块依赖的机
制,这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可⽤性、调试和跨
域访问等问题)。
AMD规范只定义了⼀个函数 "define",它是全局变量。模块通过 define 函数定义在闭包中,格式如
下:
define(id?: String, dependencies?: String[], factory: Function|Object);
//代表:require.js
2. CMD规范介绍
Common Module Definition,通⽤模块定义。
异步加载,可以像在 Node 环境中⼀样来书写模块代码。代码的书写格式如下:
define(function(require, exports, module) {
var $ = require('jquery');
exports.sayHello = function() {
$('#hello').toggle('slow');
};
});
//代表:sea.js
- ESModule介绍
在编译阶段确定依赖关系和输⼊输出。
export导出模块: export为普通导出、export default为默认导出
import加载模块
特点:
每⼀个模块只加载⼀次, 并执⾏⼀次,再次加载同⼀⽂件,直接从内存中读取;
每⼀个模块内声明的变量都是局部变量, 不会污染全局作⽤域;通过export导出模块,通过import导⼊模块
ES6模块只⽀持静态导⼊和导出,只可以在模块的最外层作⽤域使⽤import和export 4.支持异步导入
import("./person.js").then((mod) => {
console.log("****mod", mod, mod === NewPerson);
});
ESModule和CommonJS的区别
npm+webpack原理
1.npm 介绍
特点:所有模块都在仓库集中管理,统一分发使用 在package.json文件里面记录模块信息,依赖关系等 通过publish命令发布到仓库 通过install进行安装
package.json
xlit2ck2oi.feishu.cn/docx/Rmn4dB…
.npmrc文件 (可以在当前项目里面覆盖掉本电脑的npm配置)
例如:在.npmrc文件里设置
registy='www.baidu.com' //这样在这个项目里就会把原本的淘宝镜像覆盖掉
2.webpack 介绍
code spilting
code spiltting是webpack一个常用特性,就是webpack会把多种静态资源通过依赖关系转换成一个文件,从而提高用户的访问性能,但是有些你经常修改的文件,如果每次修改都要重新打包,那么将会非常不友好,所以可以通过code splitting将这个文件拆分出来。
核心概念
Entry:webpack的入口文件,从这个文件开始查找依赖关系
Output:输出的文件应该放在哪里
Module:模块,webpack是由多个模块组成,会从entry递归查找出所有的模块
Chunk:代码块,一个chunk有多个module组合而成,用于代码合并与分割,就是code splitting 分割的最小单位
Loader:可以让webpack处理那些非javascript文件,因为webpack只能处理js文件,先把所有模块转化为webpack可以处理的模块,然后webpack就可以打包处理为了转换浏览器不支持的代码,比如es6可以转换成ES5