從零開始寫一個採集圖片的chrome插件

零、介紹

這篇文章是介紹一個chrome瀏覽器插件的實現過程,主要包括:javascript

  1. 瀏覽器插件是什麼
  2. 插件目錄
  3. 插件通訊
  4. 插件調試
  5. 項目實現
  6. 插件發佈
  7. 參考文檔

1、瀏覽器插件是什麼

瀏覽器插件,是基於瀏覽器的原有功能,另外增長新功能的工具。它不是獨立的軟件,須要依附於相應的瀏覽器才能發揮做用。目前主流的瀏覽器都容許使用插件,以加強瀏覽器的功能,讓你的瀏覽器的功能更加多樣化。 開發瀏覽器插件,其實就是相似於開發一個web應用,都是由HTML+JS+CSS構成,本文將介紹一個圖片採集功能插件的實現。該插件主要用於採集網頁圖片並存儲到服務端。css

2、插件目錄

本文實現的chrome瀏覽器插件的目錄以下圖所示:html

2.1 manifest.json文件

根目錄下有一個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"
    },
}
複製代碼

2.2 頁面模板

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"]
}
複製代碼

2.3 靜態文件

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
    }]
}
複製代碼

3、插件通訊

3.1 插件上下文通訊

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

複製代碼

3.2 content-script與插件上下文通訊

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做中轉。

4、插件調試

4.1 安裝插件開發包

一、打開插件管理頁面。經過瀏覽器菜單進入插件管理頁面,也能夠直接在地址欄輸入chrome://extensions訪問。

二、勾選開發者模式。勾選後便可以直接加載插件應用文件夾,不然只能安裝.crx格式的壓縮文件,沒法及時同步插件更新。

三、插件更新。開發過程當中代碼有任何改動都須要從新加載插件,點擊插件的更新按鈕便可,以防萬一最好能夠把頁面也刷新一下。

4.2 調試content-script.js

content-script.js是運行在web頁面的腳本,打開web頁面的開發者工具就能夠進行調試了。

4.3 調試background.js

因爲background.js和content-script.js不是運行在同一個上下文中,所以web頁面的調試窗口是看不到background.js的。調試background.js須要打開插件調試窗口。在插件管理頁面點擊你的插件的「查看視圖template/background.html」,就會出現插件調試窗口了,接下來的操做就和普通web頁面調試同樣了。

4.4 調試popup.js

雖然popup.js和background.js是處於同一個上下文中,可是想要看到popup.js,還須要多一步操做「點擊審查彈出內容」才能夠:

5、項目實現

5.1 popup窗口網頁實現

popup窗口網頁提供了用戶操做界面,主要包含了如下功能:

  • 用戶點擊「預覽所有已加載圖片」按鈕,則popup.js會通知content-script.js去讀取web頁面的DOM,篩選出圖片DOM,並取出圖片連接。而後操做DOM在web頁面建立一個對話框,將全部圖片放到對話框裏集中進行預覽&採集操做。
  • 用戶切換「頁面是否展現圖片採集按鈕」選框,選中時popup.js會通知content-script操做web頁面的DOM, 給圖片加上採集按鈕。取消選中時popup.js會通知content-script刪除web頁面裏的採集按鈕。

<!-- 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);
        });
    });
})
複製代碼

5.2 content-script插入腳本實現

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);
            }
        })
    });
}

複製代碼

5.3 background後臺頁面實現

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);
        })
    })
}

複製代碼

6、插件發佈

開發完成,經過插件頁面的「打包擴展程序」打包生成.crx包。

申請一個Google帳號登陸開發者信息中心,按照要求填完全部的信息就能夠發佈了。

相關文章
相關標籤/搜索