切换页面内Angular Chrome扩展的可见性

Toggle visibility of an in-page Angular Chrome extension

本文关键字:扩展 可见性 Chrome Angular 换页      更新时间:2023-09-26

我有一个bookmarklet,我迁移到Chrome扩展。它是一个Angular应用程序。目前,它使用浏览器操作和后台脚本来允许用户调用应用程序注入到页面中。

一旦注入到页面中,扩展应该允许用户再次单击浏览器操作来切换界面的可见性。目前,多次点击浏览器操作会看到后台脚本试图将所有JavaScript文件重新注入页面,抛出如下错误:

警告:尝试加载angular不止一次

我如何在后台脚本中保持一些状态,允许注入接口的切换,而不需要它每次执行扩展的初始化?

Manifest

在我们陷入困境之前,我们需要配置Chrome扩展,使其具有…

  1. 以权限activeTab访问活动选项卡,
  2. 扩展将注入的web_accessible_resources(脚本和样式表)列表,
  3. 访问webNavigation事件,以便我们知道用户何时刷新或在选项卡内导航,
  4. background.scripts属性下定义的背景脚本。

让我们看看实际的manifest.json文件*:

是什么样子的
// manifest.json
"background": {
    "scripts": ["background.js"]
},
"web_accessible_resources": [
    "angular.min.js",
    "jquery.min.js",
    "main.js",
    "style.css"
],
"permissions": [
    "activeTab",
    "webNavigation",
    "http://*/*"
]

*部分清单。json显示

__

选项卡的生命周期状态

这是可能的维护状态(例如,浏览器的操作被点击)为一个标签的生命类似于你如何在其他情况下维护状态,通过维护id主题的哈希和他们的会话数据。在本例中,主题是一个选项卡。我们可以从浏览器操作的click处理程序中访问选项卡的id。例如:

// background.js
var clicked = {};
chrome.browserAction.onClicked.addListener(function (tab) {
    clicked[tab.id] = (clicked[tab.id] || 0) + 1;
});

选项卡的状态将在导航到其他网页时持续存在,所以我们还需要侦听一个事件,告诉我们导航已经发生,这意味着扩展不再存在于页面中,并重置选项卡的状态:

// background.js continued
// Reset state of our tab data when the page is refreshed
chrome.webNavigation.onDOMContentLoaded.addListener(function (details) {
    clicked[details.tabId] = 0;
});

__

条件脚本注入(background.js)

使用上面的示例,我们可以通过存储一个布尔值来指示扩展是否已经在活动选项卡上初始化,从而执行有条件的脚本注入到页面中。在初始化时,我们想要注入所需的所有脚本,并且在后续的操作调用中只注入我们自己的客户端脚本——假设我们有一个文件main.js,它构成了扩展自己的有效负载(它不包含只应该注入一次的供应商脚本):

// background.js
var initialised = {};
chrome.webNavigation.onDOMContentLoaded.addListener(function (details) {
    initialised[details.tabId] = false;
});
chrome.browserAction.onClicked.addListener(function (tab) {
    // When extension already exists in page, load
    // a script that will toggle the interface
    if (initialised[tab.id]) {
        chrome.tabs.executeScript(tab.id, {file: 'main.js'});
        return;
    }
    // Mark this tab as 'initialised'
    initialised[tab.id] = true;
    var javascripts = [
        "jquery.min.js",
        "angular.min.js",
        "main.js"
    ];
    var stylesheets = [
        "style.css"
    ];
    var i;
    for (i = 0; i < javascripts.length; i++) {
        chrome.tabs.executeScript(tab.id, {file: javascripts[i]});
    }
    for (i = 0; i < stylesheets.length; i++) {
        chrome.tabs.insertCSS(tab.id, {file: stylesheets[i]});
    }
});

__

通过注入脚本(main.js)切换或初始化扩展

注入完成后,我们将处于两个位置之一:

  1. 扩展需要完整的初始化,因为它是用户第一次在活动选项卡上调用它。(在这种情况下,所有供应商文件客户端脚本将被注入。)
  2. 扩展应该在视图内或视图外切换。(在这种情况下,只有客户端脚本会被注入。)

初始化扩展应该通过声明模块和插入所有模板等来完成。(如果扩展完全是自定义的,则按照您认为合适的方式执行初始化。)

因此,我们的main.js文件应该执行以下操作:
  1. 寻找一个全局标志(例如_my_chrome_extension),表示扩展已经存在于页面中。
  2. 如果标志存在:
    1. 获取Angular应用的作用域,并检索属性isVisible(或类似的)。
    2. 将标志设置为当前值(isVisible = !isVisible)的相反值。
  3. 如果标志不存在:
    1. 引导Angular应用程序。
    2. 在Angular应用中(例如在run块或类似的地方)设置属性isVisible为true,这样界面就会自动出现。

和一个代码示例…

// main.js
(function (angular, $) {
    var scope;
    // Check if application exists in page already    
    if (typeof window['_my_chrome_extension'] !== 'undefined') {
        var elem = document.querySelector('._extension_container');
        scope = angular.element(elem).scope();
    }
    if (scope) {
        // TOGGLE VISIBILITY
        scope.isVisible = !scope.isVisible;
        scope.$applyAsync();
    } else {
        window['_my_chrome_extension'] = true;
        // PERFORM INITIALISATION
        // ANGULAR APP GOES HERE
    }
})(angular, jQuery);
作为一个快速的题外话,在你的模板中,你需要像下面这样的东西,根据isVisible的值显示/隐藏接口:
// template.html
<div id="_my_chrome_extension" ng-show="isVisible"></div>