詳解小程序循環(huán)require之坑
1. 循環(huán)require
在JavaScript中,模塊之間可能出現(xiàn)相互引用的情況,例如現(xiàn)在有三個(gè)模塊,他們之間的相互引用關(guān)系如下,大致的引用關(guān)系可以表示為 A -> B -> C -> A,要完成模塊A,它依賴于模塊C,但是模塊C反過(guò)來(lái)又依賴于模塊A,此時(shí)就出現(xiàn)了循環(huán)require。
// a.js
const B = require('./b.js');
console.log('B in A', B);
const A = {
name: 'A',
childName: B.name,
};
module.exports = A;
// b.js
const C = require('./c.js');
console.log('C in B', C);
const B = {
name: 'B',
childName: C.name,
}
module.exports = B;
// c.js
const A = require('./a.js');
console.log('A in C', A);
const C = {
name: 'C',
childName: A.name,
};
module.exports = C;
那JS引擎會(huì)一直循環(huán)require下去嗎?答案是不會(huì)的,如果我們以a.js為入口執(zhí)行程序,C在引用A時(shí),a.js已經(jīng)執(zhí)行,不會(huì)再重新執(zhí)行a.js,因此c.js獲得的A對(duì)象是一個(gè)空對(duì)象(因?yàn)閍.js還沒(méi)執(zhí)行完成)。
2. 小程序中的坑
在正常情況下,JS引擎是可以解析循環(huán)require的情形的。但是在一些低版本的小程序中,居然出現(xiàn)程序一直循環(huán)require的情況,最終導(dǎo)致棧溢出而報(bào)錯(cuò),實(shí)在是天坑。
那如何解決呢,很遺憾,目前并未找到完美的方法來(lái)解決,只能找到程序中的循環(huán)require的代碼,并進(jìn)行修改。為了快速定位程序中的循環(huán)引用,寫(xiě)了一段NodeJs檢測(cè)代碼來(lái)檢測(cè)進(jìn)行檢測(cè)。
const fs = require('fs');
const path = require('path');
const fileCache = {};
const requireLink = [];
if (process.argv.length !== 3) {
console.log(`please run as: node ${__filename.split(path.sep).pop()} file/to/track`);
return;
}
const filePath = process.argv[2];
const absFilePath = getFullFilePath(filePath);
if (absFilePath) {
resolveRequires(absFilePath, 0);
} else {
console.error('file not exist:', filePath);
}
/**
* 遞歸函數(shù),解析文件的依賴
* @param {String} file 引用文件的路徑
* @param {Number} level 文件所在的引用層級(jí)
*/
function resolveRequires(file, level) {
requireLink[level] = file;
for (let i = 0; i < level; i ++) {
if (requireLink[i] === file) {
console.log('**** require circle detected ****');
console.log(requireLink.slice(0, level + 1));
console.log();
return;
}
}
const requireFiles = getRequireFiles(file);
requireFiles.forEach(file => resolveRequires(file, level + 1));
}
/**
* 獲取文件依賴的文件
* @param {String} filePath 引用文件的路徑
*/
function getRequireFiles(filePath) {
if (!fileCache[filePath]) {
try {
const fileBuffer = fs.readFileSync(filePath);
fileCache[filePath] = fileBuffer.toString();
} catch(err) {
console.log('read file failed', filePath);
return [];
}
}
const fileContent = fileCache[filePath];
// 引入模塊的幾種形式
const requirePattern = /require\s*\(['"](.*?)['"]\)/g;
const importPattern1 = /import\s+.*?\s+from\s+['"](.*?)['"]/g;
const importPattern2 = /import\s+['"](.*?)['"]/g;
const requireFilePaths = [];
const baseDir = path.dirname(filePath);
let match = null;
while ((match = requirePattern.exec(fileContent)) !== null) {
requireFilePaths.push(match[1]);
}
while ((match = importPattern1.exec(fileContent)) !== null) {
requireFilePaths.push(match[1]);
}
while ((match = importPattern2.exec(fileContent)) !== null) {
requireFilePaths.push(match[1]);
}
return requireFilePaths.map(fp => getFullFilePath(fp, baseDir)).filter(fp => !!fp);
}
/**
* 獲取文件的完整絕對(duì)路徑
* @param {String} filePath 文件路徑
* @param {String} baseDir 文件路徑的相對(duì)路徑
*/
function getFullFilePath(filePath, baseDir) {
if (baseDir) {
filePath = path.resolve(baseDir, filePath);
} else {
filePath = path.resolve(filePath);
}
if (fs.existsSync(filePath)) {
const stat = fs.statSync(filePath);
if (stat.isDirectory() && fs.existsSync(path.join(filePath, 'index.js'))) {
return path.join(filePath, 'index.js');
} else if (stat.isFile()){
return filePath;
}
} else if (fs.existsSync(filePath + '.js')) {
return filePath + '.js';
}
return '';
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
每天一篇javascript學(xué)習(xí)小結(jié)(Array數(shù)組)
這篇文章主要介紹了javascript中的Array數(shù)組知識(shí)點(diǎn),對(duì)數(shù)組的基本使用方法,以及各種方法進(jìn)行整理,感興趣的小伙伴們可以參考一下2015-11-11
JavaScript實(shí)現(xiàn)下拉菜單的顯示隱藏
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)下拉菜單的顯示隱藏,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
JavaScript數(shù)組及非數(shù)組對(duì)象的深淺克隆詳解原理
JavaScript中數(shù)組的方法種類(lèi)眾多,在ES3-ES7不同版本時(shí)期都有新方法;并且數(shù)組的方法還有原型方法和從object繼承的方法,本文介紹了JavaScript數(shù)組及非數(shù)組對(duì)象的深淺克隆,希望讀者能從中有所收獲2021-10-10
javascript contains和compareDocumentPosition 方法來(lái)確定是否HTML節(jié)點(diǎn)間的關(guān)
一個(gè)很棒的 blog 文章,是 PPK 兩年前寫(xiě)的,文章中解釋了 contains() 和 compareDocumentPosition() 方法運(yùn)行在他們各自的瀏覽器上。2010-02-02

