Commonjs
什么是 CommonJs
- CommonJs 是 js 模塊化的社區(qū)規(guī)范
模塊化產(chǎn)生的原因
- 隨著前端頁面復(fù)雜度的提升,依賴的第三方庫的增加,導(dǎo)致的 js 依賴混亂,全局變量的污染,和命名沖突
- 單個 js 文件內(nèi)容太多,導(dǎo)致了維護困難,拆分成為多個文件又會發(fā)生第一點描述的問題
- v8 引擎的出現(xiàn),讓 js 有了媲美編譯型語言的運行速度,大大激勵了前端開發(fā)者
CommonJS 的使用環(huán)境
- nodejs 實現(xiàn)了 CommonJS 模塊化規(guī)范
CommonJs 有哪些規(guī)定
- 每一個文件就是一個模塊
- 模塊中的變量和函數(shù)不會污染全局(解決了全局污染和命名沖突)
- 提供給外部使用的內(nèi)容需要導(dǎo)出
- 使用其他模塊的內(nèi)容需要導(dǎo)入 (模塊的導(dǎo)入和導(dǎo)出共同解決了 js 依賴混亂的問題)
- 模塊不會重復(fù)加載,模塊第一次導(dǎo)入后會將第一次導(dǎo)入的結(jié)果緩存,下次導(dǎo)入時,直接使用緩存的結(jié)構(gòu)
- 省略一些細(xì)碎的內(nèi)容在下面代碼中提及.....
commonJS 語法
- 導(dǎo)入
//這是導(dǎo)入一個模塊,module.js;commonjs中規(guī)定require導(dǎo)入模塊時可以省略.js后綴
const module1 = require("./module1");
//如果沒有尋找到dir.js文件,而發(fā)現(xiàn)了dir路徑,則尋找dir路徑下package.json 的main屬性指定的文件
//如果package.json未指定路徑,則觸發(fā)默認(rèn)規(guī)則 依次查找查找 index.js index.json
const module2 = require("./dir");
//如果require不是相對路徑,則會去node_module中尋找該模塊,重復(fù)module1 和module2 的步驟
//如果沒有node_modules 或node_modules 中不存在模塊則繼續(xù)向上級目錄尋找node_modules,直到根目錄
const module3 = require("module3");
- 導(dǎo)出
module.exports = {
//這里輸入導(dǎo)出的內(nèi)容
};
//這也是導(dǎo)出
exports.a = "a";
//注意 module.exports導(dǎo)出和exports[屬性名]導(dǎo)出不可共存
//module.exports會覆蓋掉exports導(dǎo)出的內(nèi)容
簡易實現(xiàn)類 nodejs 模塊化環(huán)境
const fs = require("fs");
const Path = require("path");
const vm = require("vm");
const ModuleStack = [];
function isRootDirectory(path) {
// Windows 根路徑
const windowsRootDirectory = /^[a-zA-Z]:\\$/;
// Unix/Linux 根路徑/
const unixRootDirectory = /^\//;
return windowsRootDirectory.test(path) || unixRootDirectory.test(path);
}
function isRelativeDirectory(path) {
//匹配 ../ 或者 ./開頭的路徑
const relativeDirectory = /^(\.\.\/|\.\/).+/;
return relativeDirectory.test(path);
}
// 計算node_modules路徑
let computePaths = (dirname) => {
let paths = [];
let path = dirname;
let node_modules = "./node_modules";
while (
!isRootDirectory(path) ||
!paths.includes(Path.resolve(path, node_modules))
) {
paths.push(Path.resolve(path, node_modules));
path = Path.resolve(path, "../");
}
return paths;
};
function myRequire(path) {
let truelyPath;
if (isRelativeDirectory(path)) {
// 獲取真實路徑
truelyPath = Path.resolve(__dirname, path);
} else {
//獲取可能的node_modules路徑
let paths = computePaths(__dirname);
for (const item of paths) {
truelyPath = Path.resolve(item, path);
if (fs.existsSync(truelyPath)) {
break;
}
}
if (!truelyPath) {
throw new Error("Can't find module " + path);
}
}
// 如果緩存中有,直接返回
if (myRequire.cache[truelyPath]) {
return myRequire.cache[truelyPath].exports;
}
// 讀取文件內(nèi)容
const content = fs.readFileSync(path, "utf-8");
// 包裝代碼
const wrapper = [
"(function (exports, require, module, __filename, __dirname) { \n",
"\n})",
];
// 拼接代碼
const wrapperContent = wrapper[0] + content + wrapper[1];
// 獲取文件路徑和文件名
let dirname = Path.dirname(truelyPath);
let filename = truelyPath;
let parentModule =
ModuleStack.length > 0 ? ModuleStack[ModuleStack.length - 1] : null;
// 模塊對象
const Module = {
id: Object.keys(myRequire.cache).length > 0 ? filename : ".",
path: dirname,
exports: {},
parent: parentModule,
filename: filename,
loaded: false,
children: [],
paths: computePaths(dirname),
};
if (parentModule) {
parentModule.children.push(Module);
}
//模塊入棧
ModuleStack.push(Module);
// 需要運行的函數(shù)
const moduleScope = vm.runInThisContext(wrapperContent);
// 運行代碼
moduleScope.call(
Module.exports,
Module.exports,
myRequire,
Module,
filename,
dirname
);
// 標(biāo)記模塊已加載
Module.loaded = true;
//模塊出棧
ModuleStack.pop();
// 緩存模塊
myRequire.cache[truelyPath] = Module;
return Module.exports;
}
myRequire.cache = Object.create(null);
模塊化的意義
- 解決在模塊化出現(xiàn)之前的js依賴混亂,全局污染命名沖突的問題
- 模塊化的出現(xiàn)讓js代碼可以拆分為多個模塊共同協(xié)作,單個js文件過長的問題,降低了維護難度。
- 模塊化的出現(xiàn)讓js開發(fā)大型項目出現(xiàn)了可能
ps:當(dāng)前內(nèi)容為學(xué)習(xí)commonjs理解,內(nèi)容正確性請謹(jǐn)慎甄別。文章來源地址http://www.zghlxwxcb.cn/news/detail-854939.html
文章來源:http://www.zghlxwxcb.cn/news/detail-854939.html
到了這里,關(guān)于commonjs的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!