javascript 在谷歌浏览器中通过executeScript注入多个脚本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21535233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Injecting multiple scripts through executeScript in Google Chrome
提问by Douglas
I need to programmatically injectmultiple script files (followed by a code snippet) into the current page from my Google Chrome extension. The chrome.tabs.executeScript
method allows for a single InjectDetails
object (representing a script file or code snippet), as well as a callback function to be executed after the script. Current answerspropose nesting executeScript
calls:
我需要通过我的 Google Chrome 扩展程序以编程方式将多个脚本文件(后跟代码片段)注入当前页面。该chrome.tabs.executeScript
方法允许单个InjectDetails
对象(代表脚本文件或代码片段)以及在脚本之后执行的回调函数。当前的答案建议嵌套executeScript
调用:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, { file: "jquery.js" }, function() {
chrome.tabs.executeScript(null, { file: "master.js" }, function() {
chrome.tabs.executeScript(null, { file: "helper.js" }, function() {
chrome.tabs.executeScript(null, { code: "transformPage();" })
})
})
})
});
However, the callback nesting gets unwieldy. Is there a way of abstracting this?
然而,回调嵌套变得笨拙。有没有办法抽象这个?
回答by Douglas
This is my proposed solution:
这是我提出的解决方案:
function executeScripts(tabId, injectDetailsArray)
{
function createCallback(tabId, injectDetails, innerCallback) {
return function () {
chrome.tabs.executeScript(tabId, injectDetails, innerCallback);
};
}
var callback = null;
for (var i = injectDetailsArray.length - 1; i >= 0; --i)
callback = createCallback(tabId, injectDetailsArray[i], callback);
if (callback !== null)
callback(); // execute outermost function
}
Subsequently, the sequence of InjectDetails
scripts can be specified as an array:
随后,InjectDetails
脚本序列可以指定为一个数组:
chrome.browserAction.onClicked.addListener(function (tab) {
executeScripts(null, [
{ file: "jquery.js" },
{ file: "master.js" },
{ file: "helper.js" },
{ code: "transformPage();" }
])
});
回答by Ninh Pham
From Chrome v32, it supports Promise. We should use it for making code clean.
从 Chrome v32 开始,它支持Promise。我们应该使用它来使代码干净。
Here is an example:
下面是一个例子:
new ScriptExecution(tab.id)
.executeScripts("js/jquery.js", "js/script.js")
.then(s => s.executeCodes('console.log("executes code...")'))
.then(s => s.injectCss("css/style.css"))
.then(s => console.log('done'));
ScriptExecution
source:
ScriptExecution
来源:
(function() {
function ScriptExecution(tabId) {
this.tabId = tabId;
}
ScriptExecution.prototype.executeScripts = function(fileArray) {
fileArray = Array.prototype.slice.call(arguments); // ES6: Array.from(arguments)
return Promise.all(fileArray.map(file => exeScript(this.tabId, file))).then(() => this); // 'this' will be use at next chain
};
ScriptExecution.prototype.executeCodes = function(fileArray) {
fileArray = Array.prototype.slice.call(arguments);
return Promise.all(fileArray.map(code => exeCodes(this.tabId, code))).then(() => this);
};
ScriptExecution.prototype.injectCss = function(fileArray) {
fileArray = Array.prototype.slice.call(arguments);
return Promise.all(fileArray.map(file => exeCss(this.tabId, file))).then(() => this);
};
function promiseTo(fn, tabId, info) {
return new Promise(resolve => {
fn.call(chrome.tabs, tabId, info, x => resolve());
});
}
function exeScript(tabId, path) {
let info = { file : path, runAt: 'document_end' };
return promiseTo(chrome.tabs.executeScript, tabId, info);
}
function exeCodes(tabId, code) {
let info = { code : code, runAt: 'document_end' };
return promiseTo(chrome.tabs.executeScript, tabId, info);
}
function exeCss(tabId, path) {
let info = { file : path, runAt: 'document_end' };
return promiseTo(chrome.tabs.insertCSS, tabId, info);
}
window.ScriptExecution = ScriptExecution;
})()
If you would like to use ES5, you can use online compilerto compile above codes to ES5.
如果你想使用 ES5,你可以使用在线编译器将上述代码编译为 ES5。
Fork me on GitHub: chrome-script-execution
在 GitHub 上 fork 我:chrome-script-execution
回答by willlma
Given your answer, I expected synchronously injecting the scripts to cause problems (namely, I thought that the scripts might be loaded in the wrong order), but it works well for me.
鉴于您的回答,我预计同步注入脚本会导致问题(即,我认为脚本可能以错误的顺序加载),但它对我来说效果很好。
var scripts = [
'first.js',
'middle.js',
'last.js'
];
scripts.forEach(function(script) {
chrome.tabs.executeScript(null, { file: script }, function(resp) {
if (script!=='last.js') return;
// Your callback code here
});
});
This assumes you only want one callback at the end and don't need the results of each executed script.
这假设您最后只需要一个回调并且不需要每个执行脚本的结果。
回答by fregante
Fun fact, the scripts are injected in order and you don't need to wait for each one to be injected.
有趣的事实是,脚本是按顺序注入的,您无需等待每个脚本都被注入。
chrome.browserAction.onClicked.addListener(tab => {
chrome.tabs.executeScript(tab.id, { file: "jquery.js" });
chrome.tabs.executeScript(tab.id, { file: "master.js" });
chrome.tabs.executeScript(tab.id, { file: "helper.js" });
chrome.tabs.executeScript(tab.id, { code: "transformPage();" }, () => {
// All scripts loaded
});
});
This is considerably faster than manually waiting for each one. You can verify that they are loaded in order by loading a huge library first (like d3.js
) and then loading a small file after. The order will still be preserved.
这比手动等待每个人要快得多。您可以通过首先加载一个巨大的库(如d3.js
)然后加载一个小文件来验证它们是否按顺序加载。订单仍将保留。
Note:errors aren't caught, but this should never happen if all files exist.
注意:不会捕获错误,但如果所有文件都存在,则永远不会发生这种情况。
If you want to catch the errors, I'd suggest to use the Firefox' browser.*
APIs with their Chrome polyfill
如果您想捕获错误,我建议将 Firefox 的browser.*
API 与Chrome polyfill 一起使用
browser.browserAction.onClicked.addListener(tab => {
Promise.all([
browser.tabs.executeScript(tab.id, { file: "jquery.js" }),
browser.tabs.executeScript(tab.id, { file: "master.js" }),
browser.tabs.executeScript(tab.id, { file: "helper.js" }),
browser.tabs.executeScript(tab.id, { code: "transformPage();" })
]).then(() => {
console.log('All scripts definitely loaded')
}, error => {
console.error(error);
});
});
回答by Aytacworld
This is mostly an updated answer (on the other answer) :P
这主要是更新的答案(在另一个答案上):P
const executeScripts = (tabId, scripts, finalCallback) => {
try {
if (scripts.length && scripts.length > 0) {
const execute = (index = 0) => {
chrome.tabs.executeScript(tabId, scripts[index], () => {
const newIndex = index + 1;
if (scripts[newIndex]) {
execute(newIndex);
} else {
finalCallback();
}
});
}
execute();
} else {
throw new Error('scripts(array) undefined or empty');
}
} catch (err) {
console.log(err);
}
}
executeScripts(
null,
[
{ file: "jquery.js" },
{ file: "master.js" },
{ file: "helper.js" },
{ code: "transformPage();" }
],
() => {
// Do whatever you want to do, after the last script is executed.
}
)
Or return a promise.
或者返回一个承诺。
const executeScripts = (tabId, scripts) => {
return new Promise((resolve, reject) => {
try {
if (scripts.length && scripts.length > 0) {
const execute = (index = 0) => {
chrome.tabs.executeScript(tabId, scripts[index], () => {
const newIndex = index + 1;
if (scripts[newIndex]) {
execute(newIndex);
} else {
resolve();
}
});
}
execute();
} else {
throw new Error('scripts(array) undefined or empty');
}
} catch (err) {
reject(err);
}
});
};
executeScripts(
null,
[
{ file: "jquery.js" },
{ file: "master.js" },
{ file: "helper.js" },
{ code: "transformPage();" }
]
).then(() => {
// Do whatever you want to do, after the last script is executed.
})