zoukankan      html  css  js  c++  java
  • ChromeExtension那些事儿

    Chrome Extension是什么呢?

    简而言之,就是Chrome扩展,它是基于Chrome浏览器的,我们可以理解它为一个独立运行在Chrome浏览器下的APP,当然核心编程语言就是JavaScript咯,然后结合HTML以及CSS来开发。重点是,这个“APP”功能强大,可以独自运行,亦可以与打开的网页、Chrome控制面板(DevTools)、第三方插件等等进行通信,且,Google允许ChromeExtension不必受限于跨域限制,结合以上种种优点,固,我们可以使用ChromeExtension,结合自身业务,开发出许多提高工作效率的工具。

    部署ChromeExtension也很简单,如下:

    好了,现在来听听属于ChromeExtension它的故事。

    一、概要及manifest.json

    ChromeExtension是Chrome提出来的一个概念,其实正如上文所说,核心编程语言就是JavaScript,然后提供一切通信、存储接口,大致就差不多了。

    需要注意的是,ChromeExtension都包含一个Manifest文件——manifest.json,这个文件可以告诉Chrome关于这个扩展的相关信息,它是整个扩展的入口,也是Chrome扩展必不可少的部分。且必须包含name、version和manifest_version属性,其他常用的可选属性还有browser_action、page_action、background、permissions、options_page、content_scripts。

    所以我们可以保留一份manifest.json模板,当编写新的扩展时直接填入相应的属性值就OK了。

    manifest.json模板及相关属性解释如下:

    {
        "manifest_version": 2,
        //定义chrome扩展的名称
        "name": "My Extension",
        //定义chrome扩展的版本
        "version": "versionString",
        "default_locale": "en",
        //定义chrome扩展的描述
        "description": "A plain text description",
        //定义了扩展相关图标文件的位置
        "icons": {
            "16": "images/icon16.png",
            "48": "images/icon48.png",
            "128": "images/icon128.png"
        },
        /*
          Browser Actions将扩展图标置于Chrome浏览器工具栏中,地址栏的右侧。如果声明了popup页面,当用户点击图标时,在图标的下侧会打开这个页面。同时图标上面还可以附带badge——一个带有显示有限字符空间(只能显示4字节长度信息)的区域——用以显示一些有用的信息,如未读邮件数等。且Badge目前只能够通过JavaScript设定显示的内容,同时Chrome还提供了更改badge背景的方法。如果不定义badge的背景颜色,默认将使用红色。例如,显示了一个背景颜色为蓝色,内容为“Dog”的badge:
          chrome.browserAction.setBadgeBackgroundColor({color: '#0000FF'});
          chrome.browserAction.setBadgeText({text: 'Dog'});
        */
        "browser_action": {
            //通过setIcon方法可以动态更改扩展的图标,chrome.browserAction.setIcon(details, callback)
            "default_icon": {
                "19": "images/icon19.png",
                "38": "images/icon38.png"
            },
            //定义了当用户鼠标悬停于扩展图标上所显示的文字,chrome.browserAction.setTitle({title: 'This is a new title'})
            "default_title": "Extension Title",
            /*
              定义了当用户单击扩展图标时所显示页面的文件位置, 值得注意的是Chrome不允许将JavaScript代码段直接内嵌入HTML文档,所以我们需要通过外部引入的方式引用JS文件。由于其在关闭后,就相当于用户关闭了相应的标签页,这个页面不会继续运行。当用户再次打开这个页面时,所有的DOM和js空间变量都将被重新创建,所以不要在popup页面的js空间变量中保存数据,而是利用localStorage和chrome.storage将数据保存在用户的硬盘上。
            */
            "default_popup": "popup.html"
        },
        /*
          Page Actions与Browser Actions非常类似,除了Page Actions没有badge外,其他Browser Actions所有的方法Page Actions都有。另外的区别就是,Page Actions并不像Browser Actions那样一直显示图标,而是可以在特定标签特定情况下显示或隐藏,所以它还具有独有的show和hide方法。
            chrome.pageAction.show(integer tabId);
            chrome.pageAction.hide(integer tabId);
            另,tabId为标签(下面会具体讲解)id,可以通过tabs接口获取。
        */
        "page_action": {
            "default_icon": {
                "19": "images/icon19.png",
                "38": "images/icon38.png"
            },
            "default_title": "Extension Title",
            "default_popup": "popup.html"
        },
        /*
          在Manifest中指定background域可以使扩展常驻后台。background可以包含三种属性,分别是scripts、page和persistent。如果指定了scripts属性,则Chrome会在扩展启动时自动创建一个包含所有指定脚本的页面;如果指定了page属性,则Chrome会将指定的HTML文件作为后台页面运行。通常我们只需要使用scripts属性即可,除非在后台页面中需要构建特殊的HTML——但一般情况下后台页面的HTML我们是看不到的。persistent属性定义了常驻后台的方式——当其值为true时,表示扩展将一直在后台运行,无论其是否正在工作;当其值为false时,表示扩展在后台按需运行,这就是Chrome后来提出的Event Page。Event Page可以有效减小扩展对内存的消耗,如非必要,请将persistent设置为false。注意,persistent的默认值为true。
        */
        "background": {
            "scripts": ["background.js"]
        },
        /*
          可以指定将哪些脚本何时注入到哪些页面中,当用户访问这些页面后,相应脚本即可自动运行,从而对页面DOM进行操作。属性值为数组类型,数组的每个元素可以包含matches、exclude_matches、css、js、run_at、all_frames、include_globs和exclude_globs等属性其中matches属性定义了哪些页面会被注入脚本,exclude_matches则定义了哪些页面不会被注入脚本,css和js对应要注入的样式表和JavaScript,run_at定义了何时进行注入,all_frames定义脚本是否会注入到嵌入式框架中,include_globs和exclude_globs则是全局URL匹配,最终脚本是否会被注入由matches、exclude_matches、include_globs和exclude_globs的值共同决定.
    
        注意:content_scripts中的脚本只是共享页面的DOM(DOM中的自定义属性不会被共享),而并不共享页面内嵌JavaScript的命名空间。也就是说,如果当前页面中的JavaScript有一个全局变量a,content_scripts中注入的脚本也可以有一个全局变量a,两者不会相互干扰。当然你也无法通过content_scripts访问到页面本身内嵌JavaScript的变量和函数。
        */
        "content_scripts": [
            {
                "matches": ["http://www.google.com/*"],
                "css": ["mystyles.css"],
                "js": ["jquery.js", "myscript.js"]
            }
        ],
        /*
          有一些扩展允许用户进行个性化设置,这样就需要向用户提供一个选项页面。Chrome通过Manifest文件的options_page属性为开发者提供了这样的接口,可以为扩展指定一个选项页面。当用户在扩展图标上点击右键,选择菜单中的“选项”后,就会打开这个页面
        */
        "options_page": "options.html",
        /*
          浏览器出于安全考虑是不允许跨域, 但这个规则如果同样限制Chrome扩展应用,就会使其能力大打折扣,所以Google允许Chrome扩展应用不必受限于跨域限制。但出于安全考虑,需要在Manifest的permissions属性中声明需要跨域的权限。
        */
        "permissions": [
            "*://www.google.com/*"
        ],
        // 为notification服务,桌面通知功能
        "web_accessible_resources": [
            "images/*.png"
        ]
    }
    二、存储

    对于网站来说,用户的设置通常保存在Cookies中,或者保存在网站服务器的数据库中。对于JavaScript来说,一些数据可以保存在变量中。
    但,如果用户重新启动浏览器,这些数据就会消失。那么如何在扩展中保存用户的设置呢?我们可以使用HTML5新增的localStorage接口。
    当然,Chrome为扩展应用提供了存储API,以便将扩展中需要保存的数据写入本地磁盘。Chrome提供的存储API可以说是对localStorage的改进,它与localStorage相比有以下区别:
      1.如果储存区域指定为sync,数据可以自动同步;
      2.content_scripts可以直接读取数据,而不必通过background页面;
      3.在隐身模式下仍然可以读出之前存储的数据;
      4.读写速度更快;
      5.用户数据可以以对象的类型保存。
    对于第二点需要说明一下。首先localStorage是基于域名的,而content_scripts是注入到用户当前浏览页面中的,如果content_scripts直接读取localStorage,所读取到的数据是用户当前浏览页面所在域中的。所以通常的解决办法是“content_scripts”通过runtime.sendMessage和“background”通信,由“background”读写扩展所在域(通常是chrome-extension://extension-id/)的localStorage,然后再传递给content_scripts。

    使用Chrome存储API必须要在Manifest的permissions中声明"storage",之后才有权限调用。Chrome存储API提供了2种储存区域,分别是sync和local。两种储存区域的区别在于,sync储存的区域会根据用户当前在Chrome上登陆的Google账户自动同步数据,当无可用网络连接可用时,sync区域对数据的读写和local区域对数据的读写行为一致。对于每种储存区域,Chrome又提供了5个方法,
    分别是get、getBytesInUse、set、remove和clear,如下:

    /*
    StorageArea为sync或则local
    例, chrome.storage.sync.get(...)
    */
    chrome.storage.StorageArea.get(keys, function(result){
      console.log(result);
    });
    chrome.storage.StorageArea.getBytesInUse(keys, function(bytes){
      console.log(bytes);
    });
    chrome.storage.StorageArea.set(items, function(){
      //do something
    });
    chrome.storage.StorageArea.remove(keys, function(){
      //do something
    });
    chrome.storage.StorageArea.clear(function(){
      //do something
    });

    Chrome同时还为存储API提供了一个onChanged事件,当存储区的数据发生改变时,这个事件会被触发,如下:

    /*
    callback会接收到两个参数,第一个为changes,第二个是StorageArea。changes是个对象,键为更改的属性名称,值包含两个属性,分别为oldValue和newValue
    */
    chrome.storage.onChanged.addListener(function(changes, areaName){
      console.log('Value in '+areaName+' has been changed:');
      console.log(changes);
    });
    三、通信

    Chrome提供了4个有关ChromeExtension页面间相互通信的接口,分别是runtime.sendMessageruntime.onMessageruntime.connectruntime.onConnect。

    且,Chrome提供的大部分API是不支持在"content_scripts"中运行的,但runtime.sendMessageruntime.onMessage可以在"content_scripts"中运行,所以扩展的其他页面也可以同content_scripts相互通信。

    /*
    extensionId(optional)为所发送消息的目标扩展,如果不指定这个值,则默认为发起此消息的扩展本身;
    message(required)为要发送的内容,类型随意,内容随意
    options(optional)
    callback(optional)用于接收返回结果
    */
    chrome.runtime.sendMessage(extensionId, message, options, callback)
    /*
    callback(required)接收到的参数有三个,分别是message、sender和sendResponse。
    其中sender对象包含4个属性,分别是tab、id、url和tlsChannelId,tab是发起消息的标签(下节会详讲)
    */ chrome.runtime.onMessage.addListener(callback)

     例如,popup.html与backgroud可以如下通信:

    //popup.html
    chrome.runtime.sendMessage('Hello', function(response){
        document.write(response);
    });
    
    //background
    chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
        if(message == 'Hello'){
            sendResponse('Hello from background.');
        }
    });

    查看popup.html页面会发现有输出“Hello from background.”

    四、标签

    标签的意思就是在浏览器中打开的一个个页面,如下:

    Chrome通过tabs方法提供了管理标签的方法与监听标签行为的事件,大多数方法与事件是无需声明特殊权限的,但有关标签的urltitlefavIconUrl的操作(包括读取),都需要声明tabs权限。

    "permissions": [
        "tabs"
    ]

    获取标签信息。Chrome提供了三种获取标签信息的方法,分别是getgetCurrentqueryget方法可以获取到指定id的标签,getCurrent则获取运行的脚本本身所在的标签,query可以获取所有符合指定条件的标签。

    以getCurrent为例,代码如下:

    chrome.tabs.getCurrent(function(tab){
        console.log(tab);
    });

    重点是,ChromeExtension也可以与指定的标签通信(标签中注入了"content_scripts"),方法如下:

    chrome.tabs.sendMessage(tabId, message, function(response){
        console.log(response);
    });
    五、拓展阅读

    [1]. Chrome Extensions

    [2]. Chrome扩展及应用开发

  • 相关阅读:
    Java实现“睡排序”——线程池Executors的使用
    浅谈HashMap与线程安全 (JDK1.8)
    Ubuntu 16 Java Develop环境快速搭建
    Spring Boot在反序列化过程中:jackson.databind.exc.InvalidDefinitionException cannot deserialize from Object value
    Java 8 – Map排序
    vue指令优化网络图片加载速度
    如何实现小于12px的字体效果
    两种以上方式实现已知或者未知宽度的垂直水平居中
    C# winform窗体间传值(使用委托或事件)
    C#栈Stack的使用
  • 原文地址:https://www.cnblogs.com/giggle/p/8082672.html
Copyright © 2011-2022 走看看