由于插件的js运行环境有区别,所以消息传递机制是一个重要内容。阅读了很多博文,大家已经说得很清楚了,直接转一篇@姬小光 的博文,总结的挺好。后面附一个自己写过的demo,基本就对消息传递能够熟悉了。
在开发 Chrome 扩展时经常需要在页面之间进行通讯,比如 background 与 content script 之间,background 与 popup 之间等等,本文结合官方文档中的例子介绍了 chrome 扩展开发中消息传递的基本实现。
一般情况下,我们会让 background 来控制一切,将扩展的主要逻辑都放在 background 中比较便于管理。其它页面可以通过消息传递的机制与 background 进行通讯。
理论上 content script 与 popup 之间也可以传递消息,但不建议这么做。
对于 background 和 popup 之间,其实可以直接相互调用对方的方法,也不需要消息传递。那么消息传递其实主要就是 content script 和 background 之间进行的了。
在 Chrome 扩展内部的消息传递分为两种,一种是单次的消息请求,另外一种是长连接。下面详细举例说明。
简单的单次请求(SIMPLE ONE-TIME REQUESTS)
如果只是想简单的给扩展的其它页面发送一次消息,那么可以使用 runtime.sendMessage 或者 tabs.sendMessage 方法,它允许你发送单次的JSON序列化后的数据,可以从 content script 发送给扩展页面,反之亦然。也可以选择性地传入一个处理响应的回调函数。
从 content script 发送请求代码如下:
1
2
3
4
5
|
contentscript
.
js
===
===
===
===
===
=
chrome
.
runtime
.
sendMessage
(
{
greeting
:
"hello"
}
,
function
(
response
)
{
console
.
log
(
response
.
farewell
)
;
}
)
;
|
从扩展页面发送给 content script 也很类似,只是需要指定发送给那个 tab,下面的例子即为向选中的 tab 发送消息:
1
2
3
4
5
6
7
|
background
.
html
===
===
===
===
===
chrome
.
tabs
.
getSelected
(
null
,
function
(
tab
)
{
chrome
.
tabs
.
sendMessage
(
tab
.
id
,
{
greeting
:
"hello"
}
,
function
(
response
)
{
console
.
log
(
response
.
farewell
)
;
}
)
;
}
)
;
|
注:官方例子中的 getSelected 方法已经被废弃了,可以使用 chrome.tabs.query({active:true}, function(tab) {}) 来替代。
在消息接收完毕时,需要设置一个 runtime.onMessage 事件监听器来处理消息,这部分在 content script 和扩展页面种都是一样的:
1
2
3
4
5
6
7
8
|
chrome
.
runtime
.
onMessage
.
addListener
(
function
(
request
,
sender
,
sendResponse
)
{
console
.
log
(
sender
.
tab
?
"from a content script:"
+
sender
.
tab
.
url
:
"from the extension"
)
;
if
(
request
.
greeting
==
"hello"
)
sendResponse
(
{
farewell
:
"goodbye"
}
)
;
}
)
;
|
注意:如果有多个页面同时监听 onMessage 事件,那么只有第一个调用 sendResponse() 的页面可以成功返回响应信息,其它的都会被忽略。
小技巧:当事件监听器返回时函数就不可用了,不过如果让函数 return: true 的话,可以让该函数异步响应,直到调用 sendResponse 后才结束,具体说明请见文档。
长连接(LONG-LIVED CONNECTIONS)
有时候我们需要在 content script 和扩展页面之间保持一个长期的通讯信道,那么你可以分别使用 runtime.connect 和 tabs.connect 来建立长连接信道。可以选择性地给信道命名,方便你区分不同类型的连接。
每个连接建立完成后都会返回一个 runtime.Port 对象,用来在连接之间发送和接收消息。
下面的例子展示了如何从 content script 建立连接,并发送和接收消息:
1
2
3
4
5
6
7
8
9
10
|
contentscript
.
js
===
===
===
===
===
=
var
port
=
chrome
.
runtime
.
connect
(
{
name
:
"knockknock"
}
)
;
port
.
postMessage
(
{
joke
:
"Knock knock"
}
)
;
port
.
onMessage
.
addListener
(
function
(
msg
)
{
if
(
msg
.
question
==
"Who's there?"
)
port
.
postMessage
(
{
answer
:
"Madame"
}
)
;
else
if
(
msg
.
question
==
"Madame who?"
)
port
.
postMessage
(
{
answer
:
"Madame... Bovary"
}
)
;
}
)
;
|
从扩展页面建立连接也很类似,只是要指定要连接到哪个tab。最简单地可以把上面的连接的代码改成 tabs.connect 即可。
为了能够处理连接请求,需要设置一个 runtime.onConnect 事件监听器,在 content script 和扩展页面中中都是一样的。当扩展的某一部分调用了 “connect() ”,该事件即被触发,然后就可以使用 runtime.Port 对象来发送和接收消息了。响应连接的代码大致如下所示:
1
2
3
4
5
6
7
8
9
10
11
|
chrome
.
runtime
.
onConnect
.
addListener
(
function
(
port
)
{
console
.
assert
(
port
.
name
==
"knockknock"
)
;
port
.
onMessage
.
addListener
(
function
(
msg
)
{
if
(
msg
.
joke
==
"Knock knock"
)
port
.
postMessage
(
{
question
:
"Who's there?"
}
)
;
else
if
(
msg
.
answer
==
"Madame"
)
port
.
postMessage
(
{
question
:
"Madame who?"
}
)
;
else
if
(
msg
.
answer
==
"Madame... Bovary"
)
port
.
postMessage
(
{
question
:
"I don't get it."
}
)
;
}
)
;
}
)
;
|
或许有时你还想知道连接何时关闭,那么你可以监听 runtime.Port.onDisconnect 事件,当通讯的任意一端调用 runtime.Port.disconnect,或者包含该端口的页面已被卸载。onDisconnect 事件可以保证在每个端口上只触发一次。
另外,消息传递还包括不同扩展之间的消息传递,还有 Chrome 与本地程序之间的消息传递,这里就不介绍了,感兴趣的同学可以直接查看官方文档。
—————————————–分割线——————————————-
DEMO:
几个最基本的文件
在这里,先假设大家对chrome插件开发的最基本知识已有所掌握。例如什么是manifest.json,什么是background.html等。
manifest.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
{
"name"
:
"A browser action with a popup that changes the page color."
,
"version"
:
"1.0"
,
"permissions"
:
[
"tabs"
,
"<all_urls>"
]
,
"browser_action"
:
{
"default_icon"
:
"icon.png"
}
,
"background"
:
{
"page"
:
"background.html"
}
,
"content_scripts"
:
[
{
"matches"
:
[
"<all_urls>"
]
,
"js"
:
[
"jquery-1.7.1.js"
,
"injectscript.js"
]
}
]
,
"manifest_version"
:
2
}
|
background.html
1
2
3
4
5
6
7
8
9
10
|
<
!
DOCTYPE
html
>
<
html
>
<
head
>
<
title
>
bg
<
/
title
>
<
script
type
=
"text/javascript"
src
=
"bg.js"
>
<
script
>
<
/
head
>
<
body
>
hello
<
body
>
<
/
html
>
|
这里引用了一个后台处理程序,bg.js,后面会讲到。
扩展程序发送请求数据到内容脚本,内容脚本给出回应
扩展程序后台脚本bg.js
1
2
3
4
5
6
7
8
9
10
|
(
function
(
)
{
chrome
.
browserAction
.
onClicked
.
addListener
(
function
(
tab
)
{
// 扩展向内容脚本发送消息
chrome
.
tabs
.
sendMessage
(
tab
.
id
,
{
greeting
:
"hello to content script!"
}
,
function
(
response
)
{
console
.
log
(
response
.
farewell
)
;
}
)
;
}
)
;
}
)
(
)
;
|
内容脚本injectscript.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
(
function
(
)
{
console
.
log
(
"injected"
)
;
var
resOK
=
{
farewell
:
"content script send response back..."
}
;
var
resError
=
{
farewell
:
"content script hasError!"
}
;
chrome
.
extension
.
onMessage
.
addListener
(
function
(
request
,
sender
,
sendResponse
)
{
console
.
log
(
"Request comes from extention "
+
sender
.
tab
.
url
)
;
if
(
request
.
greeting
===
"hello to content script!"
)
{
sendResponse
(
resOK
)
;
}
else
{
sendResponse
(
resError
)
;
}
}
)
;
}
)
(
)
;
|
扩展程序向内容脚本发送一条消息hello to content script!
,内容脚本接收到这条消息后去判断是不是那句话,如果是,就返回resOK
对象,如果不是,就返回resError
对象。
这时,扩展程序收到内容脚本的一条回应,至此,此番通话就结束了。
看一下结果截图
内容脚本发送请求数据到扩展程序,扩展程序给出回应
扩展程序后台脚本bg.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
(
function
(
)
{
var
resOK
=
{
farewell
:
"extension send response back..."
}
;
var
resError
=
{
farewell
:
"extension hasError!"
}
;
chrome
.
extension
.
onMessage
.
addListener
(
function
(
request
,
sender
,
sendResponse
)
{
console
.
log
(
"Request comes from content script "
+
sender
.
tab
.
url
)
;
if
(
request
.
greeting
===
"hello to extention!"
)
{
sendResponse
(
resOK
)
;
}
else
{
sendResponse
(
resError
)
;
}
}
)
;
}
)
(
)
;
|
内容脚本injectscript.js
1
2
3
4
5
6
7
|
(
function
(
)
{
console
.
log
(
"injected"
)
;
chrome
.
extension
.
sendMessage
(
{
greeting
:
"hello to extention!"
}
,
function
(
response
)
{
console
.
log
(
response
.
farewell
)
;
}
)
;
}
)
(
)
;
|
内容脚本向扩展程序发送一条消息hello to extention!
,扩展程序接收到这条消息后去判断是不是那句话,如果是,就返回resOK
对象,如果不是,就返回resError
对象。
这时,内容脚本收到扩展程序的一条回应,至此,此番通话就结束了。
特别应该注意的是:扩展程序向内容脚本发送请求数据时用的是chrome.tabs.sendMessage
,反过来,用的是chrome.extension.sendMessage
。
看一下结果截图
如果以后还有一些chrome插件的学习总结,还会写在这里。
demo地址:
点击下载demo