一、前言
提起Chrome擴(kuò)展插件(Chrome Extension),每個(gè)人的瀏覽器中或多或少都安裝了幾個(gè)插件,像一鍵翻譯、廣告屏蔽、錄屏等等,通過(guò)使用這些插件,可以有效的提高我們的工作效率;但有時(shí)候,我們想要的某個(gè)功能市面上沒(méi)有現(xiàn)成的插件,作為開發(fā)者自然而然想到,自己是否可以動(dòng)手開發(fā)一個(gè)定制化的插件?網(wǎng)上目前很多不錯(cuò)的關(guān)于Chrome插件的開發(fā)教程,可以幫助我們快速上手開發(fā)一個(gè)插件, 本文換個(gè)思路,從應(yīng)用著手,通過(guò)講解插件的特性來(lái)啟發(fā)讀者在工作中哪些場(chǎng)景可以通過(guò)插件來(lái)解決。
本文側(cè)重點(diǎn)不是Chrome插件的基礎(chǔ)開發(fā),而是聚焦于原理及應(yīng)用,會(huì)從插件的一些重要特性講起,結(jié)合實(shí)際的插件案例,來(lái)分析這些特性的作用,從而能夠啟發(fā)讀者利用這些特性開發(fā)出自己的效率工具,打造自己的趁手利器。
二、什么是Chrome擴(kuò)展插件
什么是Chrome擴(kuò)展插件?在我們印象中,它就像跑在瀏覽器中的應(yīng)用,可以把瀏覽器想象成手機(jī),那么插件就像是應(yīng)用,我們從Chrome應(yīng)用商店中下載,然后安裝到Chrome瀏覽器中,就可以在瀏覽器中進(jìn)行運(yùn)行了。
我們看看官方解釋:
Chrome Extension是一個(gè)小的軟件程序,它可以用來(lái)定義瀏覽器的瀏覽體驗(yàn),讓用戶可以根據(jù)個(gè)人需求或者偏好定制Chrome瀏覽器的功能和行為,主要使用的技術(shù)棧是HTML、Javascript和CSS。
一句話總結(jié):Chrome擴(kuò)展插件是用前端的技術(shù)棧,來(lái)定制瀏覽器的功能,改善用戶體驗(yàn)。
可能大家還聽(tīng)過(guò)一個(gè)詞:Chrome Plugin。翻譯過(guò)來(lái)是Chrome插件,和Chrome擴(kuò)展插件很相近,特別容易搞混,那么他們之間有什么區(qū)別呢?
Chrome Extension僅僅是用來(lái)增強(qiáng)瀏覽器網(wǎng)頁(yè)的功能,它是利用瀏覽器提供的已有功能和和各種API,進(jìn)行功能組合,從而改善瀏覽器體驗(yàn),停留在瀏覽器層面;
- Chrome Plugin不僅能增強(qiáng)網(wǎng)頁(yè)的功能,同時(shí)能夠擴(kuò)展瀏覽器本身的功能;當(dāng)瀏覽器提供的功能已經(jīng)無(wú)法滿足你的需求,就需要你通過(guò)C/C++這樣的編譯語(yǔ)言來(lái)擴(kuò)展瀏覽器的功能,例如我們常用的Flash 插件,Chrome Plugin工作在內(nèi)核層面。
三、Chrome擴(kuò)展插件組成及核心機(jī)制
3.1 Chrome擴(kuò)展插件的組成
一個(gè) Chrome 擴(kuò)展插件通常由 3 類文件組成:
1) 配置文件 manifest.json,用于配置擴(kuò)展的名稱、版本號(hào)、作者、圖標(biāo) icon、彈出界面、權(quán)限、腳本路徑等信息;
2) 圖片、css 等資源文件;
3)js腳本文件,其中包含:
popup.js:用于搭配 popup.html 使用,點(diǎn)擊插件圖標(biāo)的時(shí)候展示頁(yè)面及頁(yè)面邏輯控制;
background.js:用于定義一個(gè)后臺(tái)頁(yè)面,相當(dāng)于一個(gè)常駐頁(yè)面,生命周期和瀏覽器一致;
- content_scripts.js:用于向頁(yè)面中注入 JS 腳本,它可以操作頁(yè)面dom,但不會(huì)和頁(yè)面中的腳本產(chǎn)生沖突。
3.2 Chrome擴(kuò)展插件的核心機(jī)制
Chrome擴(kuò)展插件中比較核心的幾個(gè)概念:Extension Page、background.js、Content_script.js ,它們?cè)谑裁磿r(shí)機(jī)觸發(fā),扮演著什么角色,彼此之間如何進(jìn)行通信?可以看一下下面的關(guān)系圖:
從圖中可以看出,存在三個(gè)進(jìn)程:擴(kuò)展進(jìn)程(Extension Process)、頁(yè)面渲染進(jìn)程(Render Process)、瀏覽器進(jìn)程(Browser Process)。
1)擴(kuò)展進(jìn)程中運(yùn)行Extension Page,Extension Page主要包括backgrount.html和popup.html:
backgrount.html中沒(méi)有任何內(nèi)容,是通過(guò)background.js創(chuàng)建生成,當(dāng)瀏覽器打開時(shí),會(huì)自動(dòng)加載插件的background.js文件,它獨(dú)立于網(wǎng)頁(yè)并且一直運(yùn)行在后臺(tái),它主要通過(guò)調(diào)用瀏覽器提供的API和瀏覽器進(jìn)行交互;
- popup.html則不同,它有內(nèi)容,是一個(gè)實(shí)實(shí)在在的頁(yè)面,和我們普通的web頁(yè)面一樣,由html、css、Javascript組成,它是按需加載的,需要用戶去點(diǎn)擊地址欄的按鈕去觸發(fā),才能彈出頁(yè)面。
2)渲染進(jìn)程主要運(yùn)行Web Page,當(dāng)打開頁(yè)面時(shí),會(huì)將content_script.js加載并注入到該網(wǎng)頁(yè)的環(huán)境中,它和網(wǎng)頁(yè)中引入的Javascript一樣,可以操作該網(wǎng)頁(yè)的DOM Tree,改變頁(yè)面的展示效果;
3)瀏覽器進(jìn)程在這里更多起到橋梁作用,作為中轉(zhuǎn)可以實(shí)現(xiàn)Extension Page和Content_script.js之間的消息通信。
四、Chrome擴(kuò)展插件能做什么
Chrome擴(kuò)展插件的使用方向主要包含兩個(gè)部分:
改變?yōu)g覽器的外觀:
brower Actions
page Actions
content menus
桌面通知
Omnibox
- override 替代頁(yè)
和瀏覽器進(jìn)行交互:
Cookie控制
標(biāo)簽控制
書簽控制
下載控制
事件監(jiān)聽(tīng)
網(wǎng)絡(luò)請(qǐng)求
- 代理...
下面我們通過(guò)實(shí)例來(lái)分析這些功能的使用案例:
實(shí)例1:替換頁(yè)面
使用替代頁(yè),可以將Chrome默認(rèn)的一些特定頁(yè)面替換掉,改為使用擴(kuò)展提供的頁(yè)面。這讓開發(fā)者可以開發(fā)更多有趣或者實(shí)用的基本功能頁(yè)面。
"chrome_url_overrides": { "newtab": "newTab.html", //替換新標(biāo)簽頁(yè) "bookmarks":"bookmarks.html", //替換書簽管理器頁(yè)面 "history":"history.html" //替換歷史記錄頁(yè)面 },
下面是一個(gè)替換新標(biāo)簽頁(yè)的效果圖:
實(shí)例2:Cookie控制
通過(guò)Cookie的API,可以對(duì)瀏覽器的Cookie進(jìn)行增刪改查工作。例如我們?cè)陂_發(fā)工作中,經(jīng)常需要頻繁的清除瀏覽器緩存,每次都需要先找到清除按鈕,彈出對(duì)話框,進(jìn)行確認(rèn),操作很繁瑣,如果開發(fā)一個(gè)chrome擴(kuò)展插件,就可以輕松實(shí)現(xiàn)一鍵快捷清除瀏覽器Cookie等緩存,可以參考Clear Store插件。
實(shí)例3:標(biāo)簽控制?
使用chrome.tabs API與瀏覽器的標(biāo)簽系統(tǒng)進(jìn)行交互,可以查詢,創(chuàng)建、修改和重新排列瀏覽器中的標(biāo)簽頁(yè);我們?cè)谑褂脼g覽器時(shí),經(jīng)常會(huì)打開很多標(biāo)簽頁(yè),顯得很混亂,中途想要找打開的某個(gè)頁(yè)面時(shí),效率低且痛苦,如果能將這些標(biāo)簽頁(yè)進(jìn)行整理并有序的展示該多好,這里給大家推薦一個(gè)Chrome擴(kuò)展插件:OneTab,該插件將所有打開的標(biāo)簽頁(yè)在新的頁(yè)面中有序的排列出來(lái),如下圖,一目了然。
我們甚至可以通過(guò)tabs 實(shí)現(xiàn)頁(yè)簽之間的交互,出于安全考慮,tab的屬性中沒(méi)有document, 因此無(wú)法在擴(kuò)展中直接獲取某個(gè)標(biāo)簽頁(yè)面中的dom元素,但是可以通過(guò)發(fā)送事件請(qǐng)求來(lái)實(shí)現(xiàn):
chrome.tabs.sendRequest(tab_id, { hello: "ok" }, function(response){ // response處理});
chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { if (request.hello == "ok"){ sendResponse({ data: $("#hello") // 獲取id是hello的元素發(fā)過(guò)去 }); } });
實(shí)例4:攔截請(qǐng)求或者反向代理
在在頁(yè)面性能點(diǎn)檢時(shí),我們經(jīng)常會(huì)檢查頁(yè)面圖片資源是否存在尺寸過(guò)大,例如200K,獲取一個(gè)過(guò)大的圖片列表頁(yè)面。
chrome.webRequest API只能在background.js中使用,所以可以通過(guò)圖片攔截,將鏈接通過(guò)消息傳給當(dāng)前頁(yè)面的content_script.js,然后在content_script.js中進(jìn)行圖片下載和大小檢查。
// background.jschrome.webRequest.onBeforeRequest.addListener( function(details) { // url就是圖片下載的鏈接 const { url ,tabId} = details // 向content_script.js發(fā)送下載圖片鏈接 chrome.tabs.sendMessage(tabId, {picUrl: url}, function(response) { //... }); return {cancel: isCancel}; }, {urls: ["http://baidu.com"],types: "image"}, ["blocking"]);
// content_script.jschrome.runtime.onMessage.addListener(function(request, sender, sendResponse){ if(sender.tab && request.picUrl && request.picUrl == sender.tab.id){ //獲取圖片大小并下載 }});
實(shí)例5:頁(yè)面元素操作
利用Content_script.js可操作dom元素,進(jìn)行對(duì)頁(yè)面元素進(jìn)行操作,實(shí)現(xiàn)自動(dòng)化登錄,解放雙手。
//輸入function input(inputElement, content) { let event = document.createEvent(HTMLEvents); event.initEvent(input, true, true); inputElement.value = content; inputElement.dispatchEvent(event)}const usernameDom = document.getElementById("userName"); //用戶名const pwdDom = document.getElementById("password"); //密碼const btnDom = document.getElementById("submitBtn");//按鈕//輸入后,點(diǎn)擊確認(rèn)input(usernameDom, "姓名");input(pwdDom, "密碼");//登錄btnDom.click();
五、業(yè)務(wù)實(shí)踐
痛點(diǎn):我目前主要負(fù)責(zé)vivo全球商城的業(yè)務(wù),全球化的業(yè)務(wù)都會(huì)面臨國(guó)際化語(yǔ)言的問(wèn)題,我們自主開發(fā)了一個(gè)多語(yǔ)言管理后臺(tái),配置key-value,前端通過(guò)接口獲取多語(yǔ)言在頁(yè)面展示;如果運(yùn)營(yíng)查看頁(yè)面,覺(jué)得某個(gè)文案不太合適,想要修改,需要進(jìn)行如下圖的一系列操作:
可以看到當(dāng)運(yùn)營(yíng)想要修改文案時(shí),他先要知道該文案對(duì)應(yīng)的key值,而頁(yè)面上面無(wú)法獲取到key值,需要讓開發(fā)提供,然后需要到多語(yǔ)言管理平臺(tái)去更新對(duì)應(yīng)key的值。?
這樣遇到兩個(gè)問(wèn)題:
不能所見(jiàn)即所得,看到頁(yè)面不能知道key值;
- 所見(jiàn)無(wú)法直接修改,需要到另一個(gè)管理平臺(tái)去修改 ;
目前這個(gè)在修改內(nèi)容少的情況下,還是可以操作的,當(dāng)修改內(nèi)容很多時(shí),這樣操作起來(lái)很繁瑣,效率很低。
思考:
1)運(yùn)營(yíng)是否可以直接在頁(yè)面上修改并生效?
2)如果可以修改,怎么去實(shí)現(xiàn)跨域請(qǐng)求?
3)怎么實(shí)現(xiàn)登錄授權(quán)?
如果對(duì)Chrome擴(kuò)展插件熟悉,會(huì)發(fā)現(xiàn)Chrome就是為這量身定制,可以完美解決這些問(wèn)題。
實(shí)現(xiàn)方案:?
1)對(duì)頁(yè)面中涉及文案dom進(jìn)行修改,綁定多語(yǔ)言key值。
{{ language.addressDeleteButton }}
2)利用Content_script,js可以操作頁(yè)面dom元素,當(dāng)啟動(dòng)插件時(shí),將頁(yè)面所有可修改的文案的dom增加contenteditable屬性,支持可編輯,如圖;
3)創(chuàng)建一個(gè)修改面板,獲取當(dāng)前選中的key值和value值,和修改后的值,如上圖,右上角面板。
4)利用Chrome插件支持跨站請(qǐng)求的特性,向多語(yǔ)言平臺(tái)直接發(fā)送修改請(qǐng)求。
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.setRequestHeader(Content-Type, application/x-www-form-urlencoded); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { try{ sendResponse(JSON.parse(xhr.response)); }catch(e) { // 異常處理 } } }; xhr.send(new URLSearchParams(params)); return true });
5)利用Chrome插件可以獲取瀏覽器中Cookie特性,新開一個(gè)標(biāo)簽頁(yè)打開多語(yǔ)言后臺(tái),進(jìn)行登錄,登錄成功后就可以實(shí)現(xiàn)請(qǐng)求的授權(quán)修改了。
六、總結(jié)
最后總結(jié)一下,生活中經(jīng)常會(huì)感嘆:看過(guò)好多人生道理,依然過(guò)不好這一生。同樣,使用過(guò)很多Chrome插件,依然碼不好自己的一個(gè)插件,所以最后再送給你一個(gè)閱讀Chrome插件源碼的插件,堪稱插件中的插件,插件中的王者——Chrome extension source viewer。通過(guò)它可以很方便的查看其它插件的源碼,讓我們能夠站在巨人的肩膀上往前走~~
作者:vivo互聯(lián)網(wǎng)前端團(tuán)隊(duì)-Zhang hao