您好,登錄后才能下訂單哦!
1. 循環require
在JavaScript中,模塊之間可能出現相互引用的情況,例如現在有三個模塊,他們之間的相互引用關系如下,大致的引用關系可以表示為 A -> B -> C -> A,要完成模塊A,它依賴于模塊C,但是模塊C反過來又依賴于模塊A,此時就出現了循環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引擎會一直循環require下去嗎?答案是不會的,如果我們以a.js為入口執行程序,C在引用A時,a.js已經執行,不會再重新執行a.js,因此c.js獲得的A對象是一個空對象(因為a.js還沒執行完成)。
2. 小程序中的坑
在正常情況下,JS引擎是可以解析循環require的情形的。但是在一些低版本的小程序中,居然出現程序一直循環require的情況,最終導致棧溢出而報錯,實在是天坑。
那如何解決呢,很遺憾,目前并未找到完美的方法來解決,只能找到程序中的循環require的代碼,并進行修改。為了快速定位程序中的循環引用,寫了一段NodeJs檢測代碼來檢測進行檢測。
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); } /** * 遞歸函數,解析文件的依賴 * @param {String} file 引用文件的路徑 * @param {Number} level 文件所在的引用層級 */ 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); } /** * 獲取文件的完整絕對路徑 * @param {String} filePath 文件路徑 * @param {String} baseDir 文件路徑的相對路徑 */ 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 ''; }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。