前端模块化——CJS规范
1、模块化
常用的模块化规范有:ES6模块化,CJS模块化,不常用的有AMD,CMD.
ES6模块化:ES6的模块化分为导出(export)与导入(import)两个模块, export 可以有多个,export default
仅有一个。
export let myName="laowang";
import {myName} from "./test.js";
console.log(myName);//laowang
CJS模块化规范:每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。Node 应用由模块组成,采用 CommonJS 模块规范。分为导出(module.exports)与导入(require)两个模块。
- PS: 由于node每个文件都被认为是一个模块,所以每个文件里面的
this
,指的是module.exports
,而不是global
。
2、模块化特点
- 防止命名冲突
- 作用域问题
3、实现原理
因为node可以读取文件,所以node实现了CJS规范。然后使用node的path、fs、vm
模块进行查找文件,读取文件,然后使用vm
模块把字符串外层套了一层匿名自执行函数,返回一个module.exports
。
先创建一个文件a.js
, 里面输入module.exports = 'hello';
。
创建一个cjs.js
,开始写一个简易版的cjs原理。
const fs = require('fs');
const path = require('path');
const vm = require('vm');
function myReq(filePath){
// 拿到绝对路径
let absolutePath = path.resolve(__dirname, filePath);
// 拿到文件后需要创建一个模块,每一个模块都需要有一个exports属性
let module = new Module(absolutePath);
// 创建完模块加载模块的数据
module.load();
return module.exports;
}
// 创建模块
function Module(path){
this.path = path;
this.exports = {};
}
Module.prototype.load = function() {
// 读取到文件内部的代码
let jsStr = fs.readFileSync(this.path, 'utf8');
// 包裹一层函数
let fnStr = `(function(exports, module, require){
${jsStr}
})`;
// 转换成函数
let fn = vm.runInThisContext(fnStr);
fn.call(this.exports, this.exports, this, myReq);
}
let h = myReq('./a.js');
console.log(h);
如果是json文件的话直接JSON.parse
解析成对象,赋值为module.exports
3.1 vm模块
vm提供了一个沙箱环境(即安全环境,函数内部执行不会去查找外部的变量,只能在内部调用运行)。
执行字符串函数不能使用new Function
or eval
,是因为他们会调用外部作用域的同名变量的,容易引发错误。