Javascript 避免在使用chrome.tabs.executeScript(…)时多次动态注入同一脚本
我正在构建一个Google Chrome扩展。基本的设置是,我有一个浏览器操作按钮,当点击活动选项卡时,它会将jQuery和另一点JavaScript注入活动选项卡 这是我的第一个Chrome扩展,但如果用户再次单击按钮采取行动,脚本将被重新注入。这是一个问题,因为将要使用的主要页面都是AJAX,因此页面内容会发生显著变化,但实际页面URL永远不会改变Javascript 避免在使用chrome.tabs.executeScript(…)时多次动态注入同一脚本,javascript,google-chrome-extension,Javascript,Google Chrome Extension,我正在构建一个Google Chrome扩展。基本的设置是,我有一个浏览器操作按钮,当点击活动选项卡时,它会将jQuery和另一点JavaScript注入活动选项卡 这是我的第一个Chrome扩展,但如果用户再次单击按钮采取行动,脚本将被重新注入。这是一个问题,因为将要使用的主要页面都是AJAX,因此页面内容会发生显著变化,但实际页面URL永远不会改变 这是一个合理的担忧,还是我想得太多了,有没有一种简单的方法可以防止脚本被多次注入同一页面?我是否应该将它们指定为内容脚本?这绝对是一个合理的问题
这是一个合理的担忧,还是我想得太多了,有没有一种简单的方法可以防止脚本被多次注入同一页面?我是否应该将它们指定为内容脚本?这绝对是一个合理的问题 简单的方法是使用类似于
#ifndef
的机制在C中包含防护
定义一个在执行内容脚本后设置的标志,并在执行任何操作之前检查它是否已定义。从同一扩展注入的所有脚本共享JS执行上下文,因此具有全局范围
您的内容脚本可能如下所示:
if (window.contentScriptInjected !== true) {
window.contentScriptInjected = true; // global scope
/* Do your initialization and work */
}
/* global function definitions */
检查应该使用一个显式的
true
值,以避免在页面上出现误报,因为页面上碰巧有一个属性为id
的元素意外地等于变量名-浏览器会创建一个指向该DOM元素的值,因此对于if
检查,它将是真实的。我相信更好的方法可能是这样做它位于扩展级别。如果您包括第三方库,那么您也将重新包括它们,并且永远不知道会发生什么
contentScript.js
(function () {
var Sz = Sizzle;
//listen for events from the extension
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (!request._extension || request._extension !== 'my-extension-name') {
console.log('Non extension request');
return;
}
var channel = (request._channel) ? request._channel : false;
if (channel === 'is-loaded') {
sendResponse({isLoaded: true});
return true;
} else if (channel === 'example') {
//do stuff
}
return true;
});
//other code goes here
});
Background.js
var util = {};
util.sendMessage = function (data, cb) {
chrome.tabs.query({active: true, currentWindow: true}, tabsQuery_cb);
data['_extension'] = 'my-extension-name';
function tabsQuery_cb(tabs) {
var tab = tabs[0];
console.log('sending message to ' + tab.id);
console.log(data);
//let the content script know to open the ad page
chrome.tabs.sendMessage(tabs[0].id, data, cb);
}
}
util.loadContentScript = function(cb) {
var attempts = 0;
checkIfLoaded();
//checks if ContentScript is loaded by sending a message to the current
function checkIfLoaded() {
util.sendMessage({ _channel: 'is-loaded'}, sendMessage_cb);
}
function sendMessage_cb(response) {
console.log('isLoadedCheck')
console.log(response);
if (response && response.isLoaded === true) {
console.log('Script already loaded!');
cb(true);
} else {
console.log('loading scripts');
loadScripts();
}
}
function loadScripts() {
attempts++;
console.log('loadScripts, attempt no. ' + attempts);
if (attempts > 1) {
console.log('Script couldnt be loaded');
return cb(false);
}
var scripts = [
"assets/sizzle-2.3.4.min.js",
"app/contentScript.js"
];
var i = -1;
scriptLoaded();
function scriptLoaded() {
i++;
if (i > scripts.length) {
//all scripts have loaded at this point, check if replies with message
checkIfLoaded();
} else {
console.log('Loading script ' + scripts[i]);
chrome.tabs.executeScript(null, { file: scripts[i] }, scriptLoaded);
}
}
}
}
当然,包括第三方库似乎是一种糟糕的做法,因为它可能会扭曲网站的脚本。但还有什么别的办法。也许您可以走极端,创建一个引导内容脚本,该脚本将检查库的存在,并告诉扩展脚本需要包括哪些内容。这是我在一个更严肃的项目中考虑的问题。你是否想要一个内容脚本或浏览器动作取决于脚本下的条件应该被注入。你确定吗?@皮特不,他们不确定。我原以为您在同一个选项卡中多次关注注入。我是,但注入是在浏览器action background.html的全局上下文中进行的,因此,如果有10个选项卡,使用单个变量来跟踪是否发生了注入似乎不起作用。我不是说将标记注入页面DOM本身,而是说通过chrome.tabs.execute script()动态加载内容脚本call@Pete你误解了。这是内容脚本代码。该变量存在于选项卡的上下文中,并保证脚本只在该选项卡中注入一次。也就是说,它可以被注入多次,但代码只运行一次。问题是,如果您还包括第三方库。>当然,包括第三方库似乎是一种不好的做法,因为它可能会与网站的脚本发生偏差。网页脚本中的内容脚本“在孤立的世界中工作”,它们甚至可以使用同一脚本的不同版本