chrome扩展程序初体验
常被称为chrome插件,虽然名称定语为chrome,但是chrome扩展程序除了在chrome浏览器上使用,理论上还可以运行在wekit内核的浏览器上(毕竟chrome已经和webkit分道扬镳开始走Blink内核了)
chrome扩展主要是 增强浏览器的功能 ,能定制个人专属的浏览器不是很有意思:smirk:
用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对象中的特殊属性,:chestnut:
// 注入脚本 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 // 内容脚本引用code
// 注入脚本code
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) { // ... }); });
接收方的方法一致,:chestnut:
// 后台脚本 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… 介于这个扩展没有发布,只能给大家看一下前后对比图了:sweat_smile:
第一次写chrome扩展,总结的肯定不全面,大佬们多多指导。