這篇文章是介紹一個chrome瀏覽器插件的實現過程,主要包括:javascript
瀏覽器插件,是基於瀏覽器的原有功能,另外增長新功能的工具。它不是獨立的軟件,須要依附於相應的瀏覽器才能發揮做用。目前主流的瀏覽器都容許使用插件,以加強瀏覽器的功能,讓你的瀏覽器的功能更加多樣化。 開發瀏覽器插件,其實就是相似於開發一個web應用,都是由HTML+JS+CSS構成,本文將介紹一個圖片採集功能插件的實現。該插件主要用於採集網頁圖片並存儲到服務端。css
本文實現的chrome瀏覽器插件的目錄以下圖所示:html
根目錄下有一個manifest.json文件,裏面提供了整個插件的功能和配置文件清單,很是重要,是插件應用必不可少的文件且必須放置在根目錄。其中manifest_version、name、version這3個是必不可少的, description和icons是推薦選項。java
{ "manifest_version": 2, // 清單文件的版本,這個必須寫,並且必須是2 "name": "圖片採集", // 插件的名稱 "version": "1.0.0", // 插件的版本 "description": "這是一個圖片採集插件", // 插件描述 "icons": { // 插件圖標 "16": "static/img/icon.png" "48": "static/img/icon.png" "128": "static/img/icon.png" }, } 複製代碼
template目錄則用來放置頁面模板,經常使用的有popup.html和background.html。jquery
popup.html是窗口網頁,點擊插件圖標時彈出,焦點離開窗口網頁就馬上關閉,用於和用戶交互。在圖片採集插件中提供了」預覽所有圖片「和」展現採集按鈕「兩個按鈕供用戶操做。web
經過manifest的default_popup字段來指定pupup頁面:ajax
{ "browser_action": { "default_icon": "static/img/icon.png", "default_title": "", "default_popup": "template/popup.html" }, } 複製代碼
background.html是後臺頁面,是一個常駐頁面,它的生命週期是插件中全部類型頁面中最長的,隨着瀏覽器的打開而打開,隨着瀏覽器的關閉而關閉,因此一般把須要一直運行的、啓動就運行的、全局的代碼放置在background.html裏面,能夠理解爲插件運行在瀏覽器中的一個後臺腳本。好比有時候,插件須要和服務端交互進行數據同步等操做,這種操做是用戶無感知的,就能夠放在後臺頁面來運行這部分的邏輯。chrome
經過manifest的background字段來指定background頁面:json
{ // 2種指定方式,若是指定JS,那麼會自動生成一個背景頁 "page": "template/background.html" // "scripts": ["static/js/background.js"] } 複製代碼
static目錄放置靜態文件包括圖片、js腳本、css樣式。瀏覽器
icon.png爲插件圖標。
content-script.js是插件注入到web頁面的js腳本,經過使用標準的DOM,它能夠讀取web頁面的細節或者修改頁面DOM結構,web頁面和插件的通訊也能夠經過它來實現。在圖片採集插件中,主要用來操做網頁拿到圖片。
popup.js爲popup.html的js腳本。在圖片採集插件中,主要用戶收集用戶交互,通知content-script.js去操做網頁採集圖片。
background.js爲background.html的js 腳本。在圖片採集插件中,主要用於存儲採集到的圖片到服務端。
content-script.css爲插件注入到web頁面的css樣式。
conten-script可經過manifest配置的方式注入:
{ "content_scripts": [{ "matches": ["<all_urls>"], // <all_urls> 表示匹配全部地址 "js": ["static/js/jquery-1.8.3.js", "static/js/content-script.js"], // 多個js順序注入 "css": ["static/css/content-script.css"], //css注入 "run_at": "document_end" // 代碼注入的時間,可選值: document_start, document_end, or document_idle,最後一個表示頁面空閒時,默認document_idle }] } 複製代碼
popup.js和background.js都運行在插件的上下文中, 由於是運行在同一個線程中,因此它們之間的通訊相對比較簡單,頁面之間能夠直接相互調用方法來傳遞信息。 好比chrome.extension.getViews()方法能夠返回屬於你的插件的每一個活動頁面的窗口對象列表,而chrome.extension.getBackgroundPage()方法能夠返回background頁。
// background.js var views = chrome.extension.getViews({type:'popup'}); // 返回popup對象 if(views.length > 0) { console.log(views[0].location.href); } function test(){ console.log('我是background'); } // popup.js var bg = chrome.extension.getBackgroundPage(); bg.test(); // 訪問background的函數 console.log(bg.document.body.innerHTML); // 訪問background的DOM 複製代碼
content-script.js是嵌入在web頁面的腳本,因此它實際是運行在web頁面的上下文中,與插件上下文是徹底隔離的,沒辦法像插件上下文相關頁面那樣能夠相互調用方法來實現通訊,它須要藉助通訊通道來輔助通訊。在圖片採集插件中content-script.js接收來自popup.js的消息去採集網頁圖片,併發消息給background.js存儲圖片。
popup.js或者background.js向content-script.js主動發消息:
function sendMsgToContentScript(message, callback){ chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ chrome.tabs.sendMessage(tabs[0].id, message, function(response){ if(callback) callback(response); }) }) } sendMsgToContentScript({type: 'popMsg', value: '你好, 我是popup'}, function(response){ console.log('來自content的回覆:' + response); }) 複製代碼
content-script.js接收消息:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){ if(request.type === 'popMsg'){ console.log('收到來自popup的消息:' + request.value); } sendResponse('我是content-script,已收到你的消息'); }) 複製代碼
content-script.js主動發消息給popup.js或者background.js:
chrome.runtime.sendMessage({type: 'contentMsg', value: '你好,我是content-script'},function(response){ console.log('收到來自後臺的回覆:'+ response); }) 複製代碼
popup.js和background.js接收消息:
chrome.runtime.onMessage.addListener(function(request, sender, sendResonse){ console.log() if(request.type === 'contentMsg'){ console.log('收到來自content-script的消息:' + request.value); } sendResonse('我是後臺,已收到你的消息。'); }) 複製代碼
須要注意的是content-script.js向popup.js主動發消息的前提是popup彈窗頁面必須是打開的,不然須要利用background.js做中轉。
一、打開插件管理頁面。經過瀏覽器菜單進入插件管理頁面,也能夠直接在地址欄輸入chrome://extensions訪問。
二、勾選開發者模式。勾選後便可以直接加載插件應用文件夾,不然只能安裝.crx格式的壓縮文件,沒法及時同步插件更新。
三、插件更新。開發過程當中代碼有任何改動都須要從新加載插件,點擊插件的更新按鈕便可,以防萬一最好能夠把頁面也刷新一下。
content-script.js是運行在web頁面的腳本,打開web頁面的開發者工具就能夠進行調試了。
因爲background.js和content-script.js不是運行在同一個上下文中,所以web頁面的調試窗口是看不到background.js的。調試background.js須要打開插件調試窗口。在插件管理頁面點擊你的插件的「查看視圖template/background.html」,就會出現插件調試窗口了,接下來的操做就和普通web頁面調試同樣了。
雖然popup.js和background.js是處於同一個上下文中,可是想要看到popup.js,還須要多一步操做「點擊審查彈出內容」才能夠:
popup窗口網頁提供了用戶操做界面,主要包含了如下功能:
<!-- popup.html --> <div> <div id="preViewAllImg"> <img src="https://s10.mogucdn.com/mlcdn/c45406/190219_4949lfk7le758fei1825i6dkd4g9i_40x42.png" />預覽所有已加載圖片 </div> <div class="collect_btn_wrap" id="showCollectBtn"> <div class="collect_btn"> <div class="show_collect_check"><img class="collect_check_img" src="" /></div>頁面顯示收藏按鈕 </div> </div> </div> <script type="text/javascript" src="../static/js/jquery-1.8.3.js"></script> <script type="text/javascript" src="../static/js/popup.js"></script> <!-- popup.js --> $(function () { function sendMsgToContentScript(message, callback){ chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ chrome.tabs.sendMessage(tabs[0].id, message, function(response){ if(callback) callback(response); }) }) } // 一鍵收集所有圖片 $('#preViewAllImg').click(e => { sendMsgToContentScript({type: 'REQUEST_PREVIEW_ALL_IMG', title: '我是popup, 請採集全部已加載圖片並進行預覽'}, function(response){ console.log('我是popup, 來自content的回覆:' + response); }); }); // 是否顯示採集按鈕 $('#showCollectBtn').click(() => { var src = $('.collect_check_img').attr('src'); var status = src === ''; // status: true 顯示 $('.collect_check_img')[0].src = src === '' ? 'https://s10.mogucdn.com/mlcdn/c45406/190219_0728g95i8bkl3i08jic6lhjhh7gae_24x18.png' : ''; // 向content-script發送消息顯示or隱藏單個商品的收藏按鈕 sendMsgToContentScript({type: 'REQUEST_SWITCH_COLLECT_BTN', title: `我是popup, 請${status?'顯示':'隱藏'}採集按鈕`, status}, function(response){ console.log('我是popup, 來自content的回覆:' + response); }); }); }) 複製代碼
content-script會監聽來自popup.js的消息,根據消息通知操做web頁面的DOM,執行讀取圖片連接、添加圖片採集按鈕、採集圖片等操做,併發送消息給background通知其存儲採集到的圖片連接。
<!-- content-script.js --> chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { if(request.type === 'REQUEST_PREVIEW_ALL_IMG'){ console.log('我是content-script,收到來自popup的消息:' + request.title); sendResponse('我是content-script, 已收到你的預覽所有圖片的消息'); PreviewAllImg(); }else if(request.type === 'REQUEST_SWITCH_COLLECT_BTN'){ console.log('我是content-script,收到來自popup的消息:' + request.title); sendResponse(`我是content-script, 已收到你${request.status ? '顯示':'隱藏'}採集按鈕的消息`); if(request.status){ ShowCollectBtn(); }else{ ClearCollectBtn(); } }else if(request.type === 'COLLECT_RESULT'){ console.log('我是content-script,收到來自background的消息:' + request.title); sendResponse(`我是content-script, 已收到你的${request.title}的消息`); } }); // 預覽全部已加載圖片 function PreviewAllImg(){ GetAllAttrList('img', ['src', 'data-src']).then((res) => { ShowImgPanel(res); }) } // 展現採集按鈕 function ShowCollectBtn(){ $('img').each((index, item) => { let src = $(item).attr('src') || $(item).attr('data-src'); $($(item).parent()).css('position', 'relative'); $($(item).parent()).find('.collect_img_btn').remove(); $($(item).parent()).append('<div class="collect_img_btn" data-src="'+src+'">採集</div>'); }); $('.collect_img_btn').click((e) => { e.stopPropagation(); e.preventDefault(); let src = $(e.target).data('src'); chrome.runtime.sendMessage({type: 'SEND_IMG', src: src},function(response){ console.log('我是content-script, 收到來自後臺的回覆:' + response); }) }); } // 清除採集按鈕 function ClearCollectBtn(){ $('.collect_img_btn').remove(); } // 展現預覽圖片對話框 function ShowImgPanel(list){ var panelDOM = $('<div id="collect_img_panel">' + '<div class="collect_img_panel_close">x</div>' + '<div class="collect_img_panel_content">x</div>' + '</div>'); $('body').append(panelDOM); $('body').append('<div id="collect_img_panel_mask"></div>'); let $item = ''; $.each(list, function(index, item) { $item = $item + '<div class="collect_img_panel_item">' + '<div class="collect_img_panel_item_img" hljs-string">')"></div>' + '<div class="collect_img_panel_item_mask"></div>' + '<div class="collect_img_panel_item_btn" data-src="'+ item+'">採集圖片</div>' + '</div>'; }); $('.collect_img_panel_content').html($item); $('.collect_img_panel_item_btn').click((e)=>{ let src = $(e.target).data('src'); chrome.runtime.sendMessage({type: 'SEND_IMG', src: src},function(response){ console.log('我是content-script, 收到來自後臺的回覆:' + response); }) }); $(".collect_img_panel_close").click(function() { $('#collect_img_panel').remove(); $('#collect_img_panel_mask').remove(); }); } // 根據標籤和屬性採集全部符合條件的對象 function GetAllAttrList(obj, attrArr){ return new Promise((resolve) => { let list = []; $(obj).each((index, item) => { GetAttrContent(item, attrArr).then(res => { list.push(res); if(index === $(obj).length - 1){ resolve(list); } }); }); }); } // 獲取對象的屬性內容 function GetAttrContent(obj, attrArr){ return new Promise((resolve) => { $.each(attrArr, (attrIndex, attrItem) => { let attrContent = $(obj).attr(attrItem); if(attrContent){ resolve(attrContent); } }) }); } 複製代碼
background後臺頁面監聽來自content-script的消息,將採集到的圖片存儲到服務端。
<!-- background.html --> <div> <script type="text/javascript" src="../static/js/jquery-1.8.3.js"></script> <script type="text/javascript" src="../static/js/background.js"></script> </div> <!--background.js--> chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { if(request.type === 'SEND_IMG'){ alert('我是background,採集到圖片連接:' + request.src); sendResponse('正在採集圖片'); requestStoreImg(request.src); } }); // 存儲圖片 function requestStoreImg(src) { $.ajax({ type: 'get', url: 'https:/www.mogu.com/*/store', data: { url: src }, success: function(res) { if (res.status && res.status.code && res.status.code === 1001) { sendMsgToContentScript({type: 'COLLECT_RESULT', title: '圖片採集成功'}, function(response){ alert('我是background,來自content的回覆:' + response); }); } else { sendMsgToContentScript({type: 'COLLECT_RESULT', title: '圖片採集失敗'}, function(response){ alert('我是background,來自content的回覆:' + response); }); } }, error: function(res){ sendMsgToContentScript({type: 'COLLECT_RESULT', title: '接口異常採集失敗'}, function(response){ alert('我是background,來自content的回覆:' + response); }); } }) } function sendMsgToContentScript(message, callback){ chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ chrome.tabs.sendMessage(tabs[0].id, message, function(response){ if(callback) callback(response); }) }) } 複製代碼
開發完成,經過插件頁面的「打包擴展程序」打包生成.crx包。
申請一個Google帳號登陸開發者信息中心,按照要求填完全部的信息就能夠發佈了。