详解小程序循环require之坑
作者:没有角的牛 发布时间:2024-02-24 03:38:29
标签:小程序,循环,require
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 '';
}
来源:https://segmentfault.com/a/1190000018426421


猜你喜欢
- 实例如下:<?php /** * @name thumb 缩略图函数 * @param sting  
- Firefox 3 有一个很让人讨厌的bug:基于某种目的,在表单提交时 disable 掉提交按钮,通过后退键回到这个页面后,这个提交按钮
- 最近尝试了mysql的最新版本5.5.581.最新发布的mysql 5.5.8没有适合solaris 9的版本;这个在安装时需要注意2.它发
- Python 提供了多个图形开发界面的库。Tkinter就是其中之一。 Tkinter 模块(Tk 接口)是 Python 的标准 Tk G
- 我们在编写asp程序时经常会使用到这个功能,一般我们这样判断如:if a="" then ,而这个函数考虑的就比较全面了
- 最近在做一个项目,用双通道神经网络,每个通道输入不同数据训练,具有相同label。开始没想到如何实现,网上很多例子都是单通道,即便找到双通道
- 本方法只做学习研究之用,不得用于商业用途若经济条件允许,请支持并购买正版,链接地址:https://www.jetbrains.com/py
- 背景考虑这样一种情况,产品同学希望达到以下功能:在我们的网页中有一个固定区域,这个区域会用于渲染从后端拉取的含有图片等资源的富文本字符串。他
- 在本文中,我挑选了15个最有用的软件包,介绍它们的功能和特点1. DashDash 是比较新的软件包,它是用纯 Python 构建数据可视化
- 在网页设计发展到一定阶段的时候就必然会和其他学科或领域只是产生交汇和共鸣,在阅读《超越CSS:web设计艺术精髓》这本书的时候,发现原来we
- 输入字母验证码,俺觉得特烦,特别还要输入大写字母。于是找到文件并修改成数字验证码。 修改文件 验证码文件位置 include\va
- 一、Tesseract简介Tesseract是一个OCR库(OCR是英文Optical Character Recognition的缩写),
- CGArt®2008“贺岁刊”玉鼠闹春,700页再造巅峰本期CGArt杂志信息:下载地址:http://cgart.cgfi
- 1. 不使用全局变量,适当封装2. 兼容性还行~~3. 代码短,可读性凑合~~呵呵~~~~~a. 拖动效果,16行JS<!DOCTYP
- 1.前端接口调用2.register访问入口//查询一个用户下所有的subnetws.Route(ws.GET("/subnets
- mutation.js代码:changeRoute(state, val) { let routeList = s
- 如下所示:两个时间相差一整天,结果却是相差时间为零 !!!!这里是使用错误, .seconds只会计算 小时分钟秒 部分之间的差值在这里使用
- 目录range函数的使用第一种创建方式第二种创建方式第三种创建方式判断指定的数有没有在当前序列中循环结构总结range函数的使用作为循环遍历
- 本文实例讲述了Go语言图片处理和生成缩略图的方法。分享给大家供大家参考。具体实现方法如下:package mainimport ( 
- 目录用Python写一个简单的通讯录一、构思1、定义空列表和一个空字典来存储2、定义功能选项3、添加通讯录功能4、 循环,调用所有的函数功能