如何在内容脚本加载Chrome扩展程序之前避免闪现错误的内容?

时间:2017-02-02 21:35:23

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

我正在尝试创建我的第一个Chrome浏览器扩展程序。它应该使用内容脚本来操作指定域的每个页面上的DOM。最初我在style.css中创建了一个样式规则,只使用了我正在操作的页面中已经存在的选择器 - 这个方法按预期工作。

然后我决定通过添加选项来扩展功能,允许用户从3个与3种不同风格规则相关的状态中进行选择。我添加了scripts.js来根据所选的选项设置一个类,我将用它作为选择器来应用适当的样式规则。问题是现在我必须等待在我的自定义类应用之前从chrome存储中读取状态,这意味着在我的样式生效之前页面上有默认样式的闪存。

在我的样式加载之前,我应该使用什么方法来防止延迟?

manifest.json (部分)

"content_scripts": [
  {
    "js": [ "scripts.js" ],
    "css": [ "style.css" ],
    "matches": [ "https://example.com/*" ]
  }
]

scripts.js中

chrome.storage.sync.get("state", function (obj) {
  var elem = document.getElementById('targetId');

  if (obj.state === 'targetState') {
    elem.className += ' myClass';
  }

});

的style.css

.myClass {
  /* do something */
}

1 个答案:

答案 0 :(得分:1)

时尚镀铬扩展solved使用以下步骤解决此问题:

  1. 在后台页面脚本变量中缓存状态,并将其发送回内容脚本,该内容脚本会在启动时询问数据。
  2. 可选择从chrome.webNavigation.onCommitted向内容脚本发送消息,该消息在页面开始加载之前发生,有时甚至在内容脚本运行之前,因此此方法只是一个额外的措施。但是,您可以通过使用例如多次发送相同的消息来使其成为唯一的方法。后台页面脚本中的setInterval。
  3. 使用"persistent": true background page。可以说,它是在此通信场景中可靠地避免FOUC的唯一方法,因为非持久性事件页面需要一些时间来加载。
  4. 声明要在"document_start"注入的内容脚本 当内容脚本执行时文档为空,没有头,没有正文。此时,时尚扩展,其功能是样式注入,只需在<html>
  5. 下直接添加样式元素

    在您的情况下,还需要一个额外的步骤:

    1. 使用MutationObserver处理正在加载的页面(exampleperformance info)。
    2. 的manifest.json:

      "background": {
          "scripts": ["background.js"]
      },
      "content_scripts": [
          {
              "js": ["contents.js"]
              "matches": ["<all_urls>"],
              "run_at": "document_start",
              "all_frames": true,
          }
      ],
      

      内容脚本:

      var gotData = false;
      
      chrome.runtime.sendMessage({action: 'whatDo'}, doSomething);
      
      chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
          if (msg.action == 'doSomething') {
              doSomething(msg);
          }
      });
      
      function doSomething(msg) {
          if (gotData || !msg || !msg.data)
              return;
      
          gotData = true;
      
          new MutationObserver(onMutation).observe(document, {
              childList: true, // report added/removed nodes
              subtree: true,   // observe any descendant elements
          });
      
          function onMutation(mutations, observer) {
              // use the insanely fast getElementById instead of enumeration of all added nodes
              var elem = document.getElementById('targetId');
              if (!elem)
                  return;
              // do something with elem
              .............
              // disconnect the observer if no longer needed
              observer.disconnect();
          }
      }
      

      后台页面脚本:

      var state;
      
      chrome.storage.sync.get({state: true}, function(data) {
          state = data.state;
      });
      
      chrome.storage.onChanged.addListener(function(changes, namespace) {
          if (namespace == 'sync' && 'state' in changes) {
              state = changes.state.newValue;
          }
      });
      
      chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
          if (msg.action == 'whatDo') {
              sendResponse({action: 'doSomething', data: state});
          }
      });
      
      chrome.webNavigation.onCommitted.addListener(function(navDetails) {
          chrome.tabs.sendMessage(
              navDetails.tabId,
              {action: 'doSomething', data: state},
              {frameId: navDetails.frameId}
          );
      });
      

      重复消息传递,这是一个不检查消息是否已处理的简单示例:

      chrome.webNavigation.onCommitted.addListener(function(navDetails) {
          var repetitions = 10;
          var delayMs = 10;
          send();
      
          function send() {
              chrome.tabs.sendMessage(
                  navDetails.tabId,
                  {action: 'doSomething', data: state},
                  {frameId: navDetails.frameId}
              );
              if (--repetitions)
                  setTimeout(send, delayMs);
          }
      });