常被称为chrome插件,虽然名称定语为chrome,但是chrome扩展程序除了在chrome浏览器上使用,理论上还可以运行在wekit内核的浏览器上(毕竟chrome已经和webkit分道扬镳开始走Blink内核了)
chrome扩展主要是增强浏览器的功能,能定制个人专属的浏览器不是很有意思😏
用chrome扩展可以==对页面原有DOM进行修改==,比如本例是对大搜车全体开发人员的语雀个人中心进行扩展,在原页面的基础上将开发人员的日常动态集中在一个页面中,包括gitlab的git热力图。直接重写整个页面的成本过大,所以就用chrome扩展对原页面进行二次修改,并能避免第三方接口跨域的问题。
上述情景包括Chrome扩展的开发,嵌入的iframe页面的开发,下面主要写的是chrome开发总结,适合chrome开发新手。
入门须知:
chrome扩展程序的主要组成:
- manifest.json // 说明整个程序的配置,必选 (具体各配置项参考官方配置API文档
- background.js // 后台脚本,默认持续运行在后台的脚本
- content.js // 内容脚本,注入到匹配吻合页面的脚本
- popup.html // 扩展图标点击查看的视图窗
脚本类型
注入到原网页上下文的脚本
与原页面自带脚本同等的脚本,可以访问原页面脚本中的所有变量,这种脚本只能使用网页的通用API,无法使用chrome扩展提供的API,使用时==需要在mainfest.js中配置web-accessible-resources==,表明扩展程序的某些脚本可以从网页访问
"web_accessible_resources": [
"js/sideTag.js"
]
重新载入,不属于扩展程序的脚本
content脚本
注入到匹配的当前页面中的脚本,但是并不能完成融入到当前页面的脚本中,而是运行在一个被隔离的环境中,能共享当前页面的DOM对象,但==无法获取当前页面的全部数据==,例如当前页面脚本中的变量数据,在这种脚本中可以使用部分chrome扩展的API,可以用来解决跨域问题(后文详解),在mainfest中配置content
"content_scripts": [{
"matches": ["*://souche.yuque.com/*"],
"js": ["modifyDom.js"],
"run_at": "document_end"
}]
重新载入,不完全属于扩展程序的脚本
background脚本
运行在后台的脚本,与当前浏览页面无关,但是这类脚本==可以保持持续运行或者活动时运行==,持续运行在浏览器页面打开到关闭的时间里,活动状态运行为一种非持续运行后台脚本,可以在不活动状态释放占用的资源,提高性能;后台脚本可以使用全部的chrome扩展API,在mainfest中配置background
"background": {
"scripts": ["background.js"],
"persistent": false
// true表明持续运行,false非持续运行
}
在非持续运行时,每次页面活动会触发重新载入,持续运行时加载一次,完全属于扩展程序的脚本
popup脚本
也就是我们常见的在点击扩展图标时弹出的视图窗口,它和background脚本一样可以使用全部的chrome扩展API,==一般会用来用户授权==,但是最好在用户授权之后将授权信息保存在本地,不然每次打开都需要用户授权就很不友好了(这次没实现popup,只能说说我所了解的),在mainfest中配置popup
"browser_action" : {
"default_title" : "搜车KM",
"default_popup" : "popup.html",
"default_icon" : {
"16" : "KM.png"
}
}
用户每次点击图标都会重新载入脚本,完全属于扩展程序的脚本
再往下分析一下实践中一定会碰到的通信和跨域问题,毕竟这些坑都踩了--
通信问题
注入脚本与原页面脚本:
可以从注入脚本中获取到原页面脚本的变量,通过window.addEventListener和 window.postMessage来实现,例如获取window对象中的特殊属性,🌰
// 注入脚本 sideTag.js
window.postMessage({ "sLogin": window.appData.me.login }, '*');
// 内容脚本 modifyDom.js
var sLogin = '';
window.addEventListener("message", function (event){
if (event.data.sLogin) {
sLogin = event.data.sLogin
}
}, false);
之前为了获取原页面的window.appData数据,用了一种很ZZ的方法——模版字符串,能实现,但是代码看起来真的累啊。。。来个对比吧 -_-|
// 内容脚本使用模版字符串code
image
// 内容脚本引用code
image
// 注入脚本code
image
PS: 同一个扩展程序的不同注入脚本是运行在同一个上下文的,注意变量不要重复声明
内容脚本与“完全属于扩展程序脚本”的通信:
这两者之前是可以互通消息的,==内容脚本可以调用chrome.runtime的API==,后者可以调用chrome.*的全部API,只是两者作为发送方时发送的方式不一样
// 内容脚本 modifyDom.js
chrome.runtime.sendMessage(
{
type: 'get',
url: 'https://skm.souche.com/user/' + urlParam // 需要请求的url
},
response => {
// ...
}
)
// 后台脚本 background.js
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
// ...
});
});
接收方的方法一致,🌰
// 后台脚本 background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type == 'get') {
fetch(request.url)
.then(response => {
if (response.status === 200) return response.text()
else if (response.status === 401) {
return Promise.resolve(response.status)
}
})
.then(text => {
sendResponse(text)
})
.catch(error => console.error('error', error))
return true // 异步
}
})
“完全属于扩展程序脚本”之间的通信:
他们之间函数是可以==直接互相访问==的,并且可以互相访问对方的DOM元素;所以他们之间事实上不存在什么复杂的通信
跨域问题
跨域感觉是使用chrome扩展的大收益,毕竟有些第三方API不允许服务端直接跨域请求啊。。。
在扩展脚本中使用XHR发起跨域请求,第三方域名需要在mainfest的permissions中配置一下
"permissions": [
"*://souche.yuque.com/*",
"*://skm.souche.com/*",
"*://git.souche-inc.com/*",
"*://f2e-assets.souche.com/*"
]
BUT,前不久chrome扩展已经不支持内容脚本中xhr的跨域请求了,开发中的我不得已换了跨域的方法,也就是前面通信中提到的chrome.runtime的调用,==在后台脚本中处理内容脚本的外部服务器请求==,(此处自行回看上文代码)再将处理后的数据发送给内容脚本,前端同学生活不易啊,说不定一觉起来你的代码就跑不动了。。。
emmm... 给大家看一下前后对比图吧😅
image
image
第一次写chrome扩展,总结的肯定不全面,大佬们多多指导。
网友评论