对我而言有效的方法是通过 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;
eval(thisScript?.script);
}
eval(mainAppScript);
try {
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
,您现在可以从用户脚本的上下文中运行它们。更重要的是,如果您在使用 eval
的函数内部初始化任何变量,这些变量也将对您可用(如特定于机器的代码)。或者,如果您之前 eval
过脚本,那么所有这些函数也将可用 - 本质上相当于导入它们。
<script>
标签插入页面即可。 - rampion