我们在项目中使用require.js,我们需要覆盖第705行中的 setTimeout ,这是我们需要忽略/忽略这个代码总是setTimeout(我的意思是在它上面运行),问题是如果我在开源代码中更改它时显式更改版本代码将丢失,我应该如何从外部仅为require.js文件覆盖此setTimout只要我使用这个lib,就可以保留它,是否可以在全球范围内以优雅的方式完成它?
https://github.com/jrburke/requirejs/blob/master/require.js
这是第705行
//If still waiting on loads, and the waiting load is something
//other than a plugin resource, or there are still outstanding
//scripts, then just try back later.
if ((!expired || usingPathFallback) && stillLoading) {
//Something is still waiting to load. Wait for it, but only
//if a timeout is not already in effect.
if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
checkLoadedTimeoutId = setTimeout(function () {
checkLoadedTimeoutId = 0;
checkLoaded();
}, 50);
}
}
仅供参考,我们这样做的原因是 Chrome: timeouts/interval suspended in background tabs?
答案 0 :(得分:5)
您已经声明过,您的目标是解决Chrome在setTimeout
针对后台标签执行的限制。我不认为这样做是个好主意,但如果你必须这样做,那么你绝对应该修补RequireJS而不是全局地弄乱setTimeout
。你说:
如果我在开源代码中更改它,当我更改版本时,代码将丢失
仅当您不使用合理的方法执行更改时才会出现这种情况。可以理智地做到这一点。例如,您可以使用Gulp获取require.js
中安装的node_modules
文件(在使用npm
安装RequireJS之后)并在build
中生成修补文件。然后在应用程序中使用此修补文件。这是gulpfile.js
:
var gulp = require("gulp");
// Bluebird is a good implementation of promises.
var Promise = require("bluebird");
// fs-extra produces a `fs` module with additional functions like
// `ensureDirAsync` which is used below.
var fs = require("fs-extra");
// Make it so that for each the function in fs that is asynchronous
// and takes a callback (e.g. `fs.readFile`), a new function that
// returns promise is created (e.g. `fs.readFileAsync`).
Promise.promisifyAll(fs);
var to_replace =
"if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {\n\
checkLoadedTimeoutId = setTimeout(function () {\n\
checkLoadedTimeoutId = 0;\n\
checkLoaded();\n\
}, 50);";
var replace_with =
"if (isBrowser || isWebWorker) {\n\
checkLoaded();";
gulp.task("default", function () {
// Use `fs.ensureDirAsync` to make sure the build directory
// exists.
return fs.ensureDirAsync("build").then(function () {
return fs.readFileAsync("node_modules/requirejs/require.js")
.then(function (data) {
data = data.toString();
// We use the split/join idiom to a) check that we get
// the string to be replaced exactly once and b)
// replace it. First split...
var chunks = data.split(to_replace);
// Here we check that the results of splitting the
// chunk is what we expect.
if (chunks.length < 2) {
throw new Error("did not find the pattern");
}
else if (chunks.length > 2) {
throw new Error("found the pattern more than once");
}
// We found exactly one instance of the text to
// replace, go ahead. So join...
return fs.writeFileAsync("build/require.js",
chunks.join(replace_with));
});
});
});
运行前需要运行npm install gulp fs-extra bluebird requirejs
。无论如何,你可以使用Gulp,你可以使用Grunt,或者你可以使用你想要执行构建的任何其他系统。要点是:
您有一个可重复且自动化的方法来修补RequireJS。如果您使用npm
安装新版本的RequireJS,则在重建软件时会自动应用修补程序,只要RequireJS的代码不会以阻止应用补丁的方式改变。如果更改阻止应用修补程序,请参阅下一点。
此方法比在运行时覆盖setTimeout
更强大。假设James Burke决定使用较新版本的RequireJS将checkLoaded
重命名为checkDone
并重命名关联的变量(以便checkLoadedTimeoutId
变为checkDoneTimeoutId
)。上面的gulpfile会在你再次运行时引发异常,因为它找不到要替换的文本。您必须更新要替换的文本和替换文本,以便修补程序与新版本的RequireJS一起使用。这样做的好处是,您可以获得事先已发生变化的早期警告,并且需要查看补丁。 您在游戏后期不会感到意外,也许在您已经向客户提供新版软件之后。
在运行时覆盖setTimeout
的方法将无声地无法完成其工作。他们正在寻找一个包含checkLoadedTimeoutId
的函数,该函数在新版本中不再存在。因此,他们只会让RequireJS的行为与默认情况相同。失败将是一个微妙的失败。 (我已经使用建议的setTimeout
自定义版本运行RequireJS,项目在未优化的情况下加载了超过50个模块。我发现使用库存setTimeout
和RequireJS使用RequireJS之间没有明显区别自定义setTimeout
。)
此方法不会减慢setTimeout
的每次使用速度。 setTimeout
由其他代码使用而不是RequireJS。无论你如何削减它,在setTimeout
的自定义替换中添加代码,开始在传递给它的每个函数中查找字符串将使{em>所有使用setTimeout
的速度变慢。< / p>
答案 1 :(得分:1)
您可以覆盖setTimeout
并检查作为回调传递的函数是否包含require.js(checkLoadedTimeoutId
)中该函数中使用的变量。如果是,立即调用函数,否则调用原始setTimeout
函数。
(function(setTimeoutCopy) {
setTimeout = function(fn, timeout) {
if (fn.toString().indexOf("checkLoadedTimeoutId") >= 0) {
return fn();
} else {
return setTimeoutCopy.apply(null, arguments);
}
};
}(setTimeout));
请注意,此代码存在多个问题。如果将函数传递给包含setTimeout
的{{1}},它也会立即执行。此外,如果require.js代码被缩小并且变量被重命名,则它将不起作用。
总而言之,没有好办法做到这一点。也许试着找到一种不同的方式来实现你想要的。还要注意,正如Madara Uchiha所说:
改变全局函数和对象几乎从不是一个好主意。
答案 2 :(得分:0)
如果你真的需要这个...在加载requirejs之前尝试添加:
function isTimeoutIgnoredFunction(fn, time) {
// you should configure this condition for safety, to exclude ignoring different timeOut calls!
return time == 50 && fn.toString().indexOf('checkLoadedTimeoutId') > -1;
}
window.setTimeoutOriginal = window.setTimeout;
window.setTimeout = function(fn, time) {
if (isTimeoutIgnoredFunction(fn, time)) {
return fn(); // or return true if you don't need to call this
} else {
return window.setTimeoutOriginal.apply(this, arguments);
}
};
如果没有提供requirejs minify,那么应该在最后的Chrome,Firefox,IE中使用...你需要为你支持的浏览器和缩小的require.js文件重写函数“isTimeoutIgnoredFunction”。
要查看可以使用的字符串:
console.log((function () {
checkLoadedTimeoutId = 0;
checkLoaded();
}).toString());
但在某些浏览器中,它可能就像“功能”一样。
如果你没有多少setTimeout,它可能是合适的解决方案......
答案 3 :(得分:0)
实现它的其他方法是创建库的ajax请求并在加载库之前对其进行修补,但是你需要加载一种方法来发出请求(vanilla js或jquery ......)
下一个代码是加载requirejs的示例,并在将其加载到DOM之前使用regexp对其进行修补。
$(function(){
// here you can put the url of your own hosted requirejs or a url from a CDN
var requirejsUrl = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.22/require.js';
function patch(lib){
var toReplace = /if\s*\(\(isBrowser\s*\|\|\s*isWebWorker\)\s*\&\&\s*!checkLoadedTimeoutId\)\s*\{([^{}]|\{[^{}]*\})*\}/;
var by = 'if (isBrowser || isWebWorker) { checkLoaded();}';
return lib.replace(toReplace, by);
}
$.get(requirejsUrl, function(lib){
var libpatched = patch(lib);
var script=document.createElement('script');
script.innerText=libpatched;
$('body').append(script);
console.log(window.require); // requirejs patched is loaded
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>