我目前正在尝试弄清楚如何包括javaScript,在我的scriptish/greasemonkey脚本中,我在网络服务器上都有,并每次调用Userscript时都会重新加载脚本。
我正在编辑网络服务器上的脚本,我真的不想每次更改随附的脚本时重新安装userscript。
有什么办法解决这个问题?我一直在寻找答案,但到目前为止没有运气。
可以清楚地显然,我的用户本看起来像这样:
// ==UserScript==
// @id HET
// @name SettingsHandler
// @version 1.0
// @namespace HET
// @require http://urltoscript/scripts/he/lib.js
// @run-at document-end
// ==/UserScript==
和我的外部脚本看起来像这样:
alert('got it');
对于测试目的而言,这仍然非常容易。此设置有效,但仅是第一次,并且当我更改Lib.js脚本时,那么Userscript仍然读取旧的脚本。有没有办法防止用户订阅缓存外部脚本?还是还有其他可以帮助我的metatag?
预先感谢dave
这是唯一可行的答案https://github.com/tampermonkey/tampermonkey/issues/475
建议是选项4
有几种减轻疼痛的方法。:)
- 您可以在保存脚本和全部之前增加版本号 外部资源将被重新加载。
- 将"配置模式"设置为 "高级"您可以配置外部更新间隔。笔记: "始终"仍然是指在使用资源之后。所以你可能需要 执行/加载页面两次。
- 如果您使用tampermonkey beta(chrome或 Firefox)您可以编辑到适当的外部资源(因为那里 现在是删除按钮以外的编辑按钮。
- 复制资源 并在本地存储它们。启用了"本地文件访问"之后 Chrome的扩展管理页面或Tampermonkey的设置页面(如果 您正在使用Firefox)您可以通过本地文件@require @require://uri。
不确定如何使用GM/Userscript指令来完成此操作,但是您可以轻松地自己添加脚本并将时间戳附加到URL上,以防止浏览器缓存:
var remoteScript = document.createElement('script');
remoteScript.src = 'http://domain.com/path/to/script.js?ts='+(+new Date());
remoteScript.onload = init;
document.body.appendChild(remoteScript);
function init() {
... do stuff
}
Rob M.的答案对我不起作用,因为tampermonkey脚本位置和目标站点被注入它可能会有所不同。例如,就我而言,我也有一个本地运行的Web服务器可以在IDE中使用TampermonKey进行Firefox开发,而无需从浏览器中访问TamperMonkey的任何文件系统。
该脚本应注入第三方网站,例如example.com,在该网站上应用修改。因此,浏览器将阻止此脚本,因为它来自与example.com不同的域。
在开发过程中,我想在没有任何缓存的情况下获取我的脚本,以立即应用更改。通过使用`gm.xmlhttprequest获取脚本内容来解决此问题。此外,具有当前时间戳的获取参数充当缓存buster:
let url = `http://localhost/myscript.js?ts=${(+new Date())}`
GM.xmlHttpRequest({
method: "GET",
url: url,
onload: function(response) {
let remoteScript = document.createElement('script')
remoteScript.id = 'tm-dev-script'
remoteScript.innerHTML = response.responseText
document.body.appendChild(remoteScript)
}
})
请注意,由于GM.xmlHttpRequest
可以绕过相同的原始策略,因此访问Musst在您的脚本的头部明确宏伟:
// @grant GM.xmlHttpRequest
对我有用的是通过GM_xmlhttpRequest
以文本格式检索我的文件,将其包装在eval()
中,然后执行函数,就好像使用@require
一样。
值得注意的是,使用eval
非常危险,几乎不应使用。话虽如此,这对我来说是一个例外,因为风险很低。
就我而言,我有几个不同的脚本需要在我的主应用程序脚本之前运行(例如实用程序函数)。因此,类似于您将<script>
标签放在订单中的方式,具体取决于您希望它们运行的时间,我命令它们首先运行至Array
。
另外,我将所有TM功能都包装在initialize()
函数中,这就是我在末尾所说的启动脚本的所谓。
(async function() {
try {
const scriptsToExecute = [
{ resource: 'waitForElement', url: 'https://www.example.org/waitForElement.js', },
{ resource: 'utils', url: 'https://www.example.org/utils.js', },
{ resource: 'main', url: 'https://www.example.org/mainApp.js'},
];
const getScripts = await retrieveScripts(scriptsToExecute).catch(e => {debugger;console.error('Error caught @ retrieveScripts',e);});
if (getScripts?.status !== "success" || !Array.isArray(getScripts?.scripts || getScripts?.find(f => f.status !== "success"))) throw {getScripts};
try {
const scripts = getScripts?.scripts;
const mainAppScript = scripts?.find(f => f?.resource === "main");
const scriptsToExecute = scripts?.filter(f => f?.resource !== "main");
for (let i in scriptsToExecute){
if (scriptsToExecute[i]?.status !== "success" || !scripts[i]?.retrieved) throw {"erroredScript": scripts[i]}
const thisScript = scripts[i]?.retrieved;
// eslint-disable-next-line
eval(thisScript?.script);
}
// eslint-disable-next-line
eval(mainAppScript);
try {
// once you've eval'd the script, you can call functions inside that script from within your UserScript environment
// all my main app scripts are wrapped inside of a function called `initialize()`.
// though once you've eval'd the script, you can call whatever you want.
initialize();
} catch (err){debugger; console.error('Error caught @ attempting to initialize', err);}
} catch(err){debugger; console.error('Error caught @ top level', err);}
} catch (err) {debugger}
async function retrieveScripts(scriptsToRetrieve){
try {
const scriptsContent = await Promise.all(scriptsToRetrieve.map(m => retrieveScript(m))).catch(e => {debugger;});
if (!Array.isArray(scriptsContent) || scriptsContent?.length !== scriptsToRetrieve?.length && scriptsContent?.find(f => f.status !== "success")) {debugger;return {status: "error", msg: "unable to retrieve the script(s) requested.", scriptsContent,};}
else return {status: "success", "scripts": scriptsContent};
}
catch (err){debugger;return {status: "error", msg: "(caught) unable to retrieve the script(s) requested.", scriptsToRetrieve, "error": err, "errorStringified": String(err)};}
function retrieveScript(scriptToRetrieve){
if (!scriptToRetrieve?.url) return {status: "error", msg: "no url found", scriptToRetrieve};
try {
return new Promise((resolve,reject) => {
GM_xmlhttpRequest({
method: "GET",
url: scriptToRetrieve.url,
onerror: function (response) {debugger;return reject({status: "error", response, scriptToRetrieve });},
onload: function (response) {
if (response?.status !== 200) {debugger;return reject({status: "error", response, scriptToRetrieve });}
else {
try {
if (response?.response) {
scriptToRetrieve.script = response.response;
return resolve({status: "success", "retrieved": scriptToRetrieve})
}
else throw {status: "error", "response": response.response, scriptToRetrieve }
} catch (err) {return reject(err);}
}
}
});
});
} catch (err){debugger}
}
}
})();
您 eval
'd脚本后,您现在可以在您的用户标题的上下文中运行它们。更重要的是,如果您从使用eval
的函数中初始化任何变量,则它们也可用于您(例如特定于机器的代码)。或者,如果您在此之前具有eval
'D脚本,那么所有这些功能也将可用 - 本质上导入它们。
您可以将以下内容添加到.htaccess文件:
<FilesMatch "filename.js">
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
</FilesMatch>
并完成开发后将其删除。