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.sendMessage
、runtime.onMessage
、runtime.connect
和runtime.onConnect。
且,Chrome提供的大部分API是不支持在"content_scripts"
中运行的,但runtime.sendMessage
和runtime.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
方法提供了管理标签的方法与监听标签行为的事件,大多数方法与事件是无需声明特殊权限的,但有关标签的url
、title
和favIconUrl
的操作(包括读取),都需要声明tabs
权限。
"permissions": [ "tabs" ]
获取标签信息。Chrome提供了三种获取标签信息的方法,分别是get
、getCurrent
和query
。get
方法可以获取到指定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扩展及应用开发