从内容脚本访问后台脚本对象

时间:2016-09-22 17:30:38

标签: javascript google-chrome google-chrome-extension

如何从chrome扩展程序中的内容脚本访问后台脚本对象?

在内容脚本中我有:

    // this will store settings
    var settings = {};

    // load settings from background
    chrome.extension.sendMessage({
        name: "get-settings"
    }, function(response) {
        debugger;
        settings = response.data.settings;
    }); 

在后台脚本中我有:

    var Settings = function() {
    var me = this;
    // internal, default
    var settingList = {
        serverUrl : "http://automatyka-pl.p4",
        isRecordingEnabled : true,
        isScanEnabled : true
    };

    this.get = function( key ) {
        return settingList[key];
    };
    this.set = function( key , value ) {
        if (settingList[key] != value) {
            var setting = {};
            setting[key] = value;

            chrome.storage.sync.set(setting, function() {
                settingList[key] = value;
            });
        }
        return true;
    };

chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.name == 'get-settings') {
        sendResponse({
            data : {
                settings : settings
            }
        });
        return true;
    }
});

var settings = new Settings();

消息传递有效,我的意思是响应是发送但返回的对象是空的。你知道如何解决这个问题吗?

修改 根据您的评论和答案,我会尝试为我的问题添加不同的亮点。

实际问题是: 如何访问背景"模型"来自内容脚本

让我们假设内容脚本持续响应页面DOM更改。任何时候检测到更改,都会在内容脚本中进行一些处理。但此处理取决于扩展设置。这些设置可以通过页面动作弹出脚本设置,它会通知背景模型这些设置是什么。

因此,任何时候使用内容脚本处理页面时,都应该知道存储在后台脚本中的当前设置。

如前所述,从后台提取设置是一个异步过程,所以我需要一个回调来进一步处理内容脚本。进一步处理必须等待设置(所以这应该同步处理?)。

在这种情况下,我很难想象程序流应该是什么样子。

  1. 后台加载(设置已初始化)
  2. 页面加载 - >内容脚本加载
  3. 内容脚本请求设置 - >进一步处理在回调函数内完成。
  4. 用户更改设置,后台设置已更改
  5. 触发页面更改,内容脚本响应
  6. 内容脚本请求设置 - >进一步处理在回调函数内部完成 - 但它不能像pt中那样是相同的函数。 3(内容"型号"不必初始化)?

2 个答案:

答案 0 :(得分:2)

  1. sendMessage不传输对象本身,但只传输其JSON-ifiable表示, 有效objReceived = JSON.parse(JSON.stringify(objSent)),因为您的对象settingList在函数上下文之外是不可见的,所以它在序列化过程中丢失了。

    您可以通过展示可字符串属性

    来使其公开
    this.settingList = { foo: 'bar' };
    

    将成功传输到您的内容脚本。

  2. 由于消息传递是asynchronous,要在内容脚本中使用响应,您应该在响应回调中执行此操作:

    // this will store the settings
    var settings = {};
    
    // load settings from background
    chrome.runtime.sendMessage({
        name: "get-settings"
    }, function(response) {
        settings = response.data.settings;
        onSettingsReady();
    }); 
    
    function onSettingsReady() {
       // put your logic here, settings are set at this point
    }
    
  3. 要了解设置是否在内容脚本之外更改,请在background.js的设置设置器中将消息发送到标签的内容脚本:

    this.set = function( key , value ) {
        ...
        // notify active tab if settings changed
        chrome.tabs.query({"windowType":"normal"}, function(tabs){
        for( id in tabs ){
            if("id" in tabs[id]){
                chrome.tabs.sendMessage(tabs[id].id,{"action":"update-settings","settings":settings});
            }
        }
    });
        return true;
    };
    
  4. 在内容脚本中侦听并处理此消息:

    chrome.runtime.onMessage.addListener(function(msg){
        if("action" in msg && msg.action == 'update-settings'){
            // You are setting global settings variable, so on it will be visible in another functions too
            settings = msg.settings;
        }
    });
    
  5. 更多详情:https://developer.chrome.com/extensions/runtime#method-sendMessage

    P.S。使用chrome.runtime.sendMessage代替chrome.extension.sendMessage,因为后者在Chrome API中已弃用,而且在WebExtensions API(Firefox / Edge)中完全不受支持。

答案 1 :(得分:0)

在内容脚本中添加Settings的另一个实例可能更有意义。

毕竟,内容脚本中提供了chrome.storage API。

当然,您需要关注在扩展程序的其他部分中所做的更改 - 但是您应该这样做,因为您使用的是chrome.storage.sync,并且其值可以通过Chrome同步更改。

所以,建议的解决方案:

  1. chrome.storage.onChanged添加一个监听器并处理更改以根据需要更新您的settingList
  2. Storage逻辑移动到单独的JS“库”,例如storage.js
  3. 在内容脚本中加载storage.js并正常使用。
  4. 您可能还想调整存储逻辑,以便将实际的已保存数据考虑在内 - 现在它始终是默认值。你可以这样做:

    var defaultSettingList = {
        serverUrl : "http://automatyka-pl.p4",
        isRecordingEnabled : true,
        isScanEnabled : true
    };
    var settingList = Object.assign({}, defaultSettingList);
    chrome.storage.sync.get(defaultSettingList, function(data) {
      settingList = Object.assign(settingList, data);
      // At this point you probably should call the "ready" callback - initial
      //   load has to be async, no way around it
    });