不少人介紹過Chrome插件,但必需要說,插件開發就是擺弄一個小玩具,第一要素是實用,其次是好玩。 單純羅列各類功能是很是無趣的。 因此把一篇舊文拿出來與你們分享。
人,活着就是爲了賴皮。css
做爲一個合格的開發人員,把30%的時間用來賴皮(上班偷懶)是值得推薦的。html
由於,若是你工做時間沒法賴皮,並不能說明你工做認真,只能說明你的工做自動化程度不夠。node
賴皮狗,通常會在上班時間瀏覽:SGamer論壇、虎撲論壇、鬥魚、BiliBili這一類的網站。git
但在瀏覽過程當中會遇到如下痛點:github
因此,咱們須要:web
90%的上班族都在使用Chrome瀏覽器賴皮,因此咱們選擇採用Chrome插件來實現功能。chrome
Chrome插件沒什麼大不了的,依然仍是採用HTMLCSSJS的組合。json
在這裏,我將手把手帶你從零開始製做插件。windows
就像node.js的package.json同樣,每個插件必須有一個manifest.json,做爲最初配置文件。api
咱們建立一個新的項目,在根目錄下建立manifest.json,填入如下代碼
==mainfest.json==
{ "name": "上班一鍵賴皮工具", "version": "0.1", "description": "windows:按Alt+S開啓、關閉賴皮網站\nmac:按Control+S開啓、關閉賴皮網站", "manifest_version": 2 }
解釋一下:
接下來請右鍵保存虎撲logo到根目錄,名字仍是如apple-touch-icon.png
就行吧。
也能夠點擊
https://b1.hoopchina.com.cn/c... 保存圖片
修改mainfest.json,設置四個尺寸的icon都變成apple-touch-icon.png,以及插件欄也顯示apple-touch-icon.png。
==mainfest.json==
{ "name": "上班一鍵賴皮工具", "version": "0.1", "description": "windows:按Alt+S開啓、關閉賴皮網站 \nmac:按Control+S開啓、關閉賴皮網站", "icons": { "16": "apple-touch-icon.png", "32": "apple-touch-icon.png", "48": "apple-touch-icon.png", "128": "apple-touch-icon.png" }, "browser_action": { "default_icon": "apple-touch-icon.png", "default_popup": "popup.html" }, "commands": { "toggle-tags": { "suggested_key": { "default": "Alt+S", "mac": "MacCtrl+S" }, "description": "Toggle Tags" } }, "manifest_version": 2 }
解釋一下:
icons
: 配置了顯示在不一樣地方的圖標browser_action
: 即右上角插件,browser_action > default_icon
即右上角插件圖標commands
:通常用於快捷鍵命令。 commands > toggle-tags > suggested_key
之下,設置了快捷鍵,只要按下快捷鍵,即會向Chrome就會向後臺發佈一個command
,值爲toggle-tags
。在windows環境下,咱們將快捷鍵設置成Alt+S
,在mac環境下,咱們將快捷鍵設置成Control+S
,配置文件中寫做MacCtrl+S
如今咱們有了命令,就須要後臺腳本接收,在mainfest.json中添加後臺腳本:
==mainfest.json==
... "background": { "scripts": [ "background.js" ] } ...
並在根目錄下建立background.js.
==background.js==
chrome.commands.onCommand.addListener(function(command) { alert(command) console.log(command) })
如今咱們的目錄結構以下:
├── manifest.json └── background.js └── sgamers.png
點擊Chorme右上角的三個點按鈕...
> More Tools
> Extensions
在右上角把Developer mode打開
再找到頂部的LOAD UNPACKED
,把項目的根目錄導入進去
項目導入後會出現一個新的卡片,是這個效果:
這時,你若是在Windows中按下Alt+S
就會彈出消息,消息爲toggle-tags
,正好是咱們在mainfest.json中定義好的。
同時咱們能夠點擊上圖藍色鍵頭所指示的background page
,打開一個調試工具,能夠看到toggle-tags
的輸出。
咱們在以後本地編輯插件後,能夠按灰色鍵頭所指的刷新,新功能就能當即刷新加載了!
有了這些工做,意味着你能夠進入下一步了!
一鍵打開/關閉賴皮網站,實現原理其實就是chrome的標籤頁功能。
標籤頁功能訪問須要在manifest.json中添加權限
==mainfest.json==
... "permissions": ["tabs"] ...
接下來,咱們寫一下background.js實現經過快捷鍵(windows的Alt+S,或mac的Ctrl+S)建立新的主頁:
==background.js==
// 輸入你想要的網站主頁 const MainPageUrl = 'http://https://bbs.hupu.com/all-gambia' chrome.commands.onCommand.addListener(function (command) { if (command === 'toggle-tags') { chrome.tabs.create({"url": MainPageUrl, "selected": true}); } })
其實實現很簡單,就是調用chrome.tabs.create接口,就建立了一個新的標籤頁。
刷新一下插件,再試一試快捷鍵功能————是否是已經能控制瀏覽器彈出標籤 頁了!
稍顯複雜的地方是標籤頁isOpen狀態的處理,
下圖主要關注isOpen狀態的變化,以及tabCache的值變化。
具體後臺邏輯以下,能夠跟據備註、對照流程圖進行理解:
//初始化isOpen和tabCache狀態 let isOpen = false let tabCache = [] //新標籤打開的主頁 const mainPageUrl = 'https://bbs.hupu.com/all-gambia' //四個賴皮網站的正則匹配表達式 const myPattern = 'sgamer\.com/|douyu\.com|hupu\.com|bilibili\.com' //當前頁面的Url let currentPageUrl = '' /** * 開始步驟: 判斷isOpen狀態 * 情形一:isOpen爲true,則移除頁面 * 情形二:isOpen爲false,則重載頁面 */ chrome.commands.onCommand.addListener(function (command) { if (command === 'toggle-tags') { if (isOpen) { //情形一:isOpen爲true removePages(myPattern) //情形二:isOpen爲false } else { reloadPages(myPattern, mainPageUrl) } } }) /** * 情形1:移除頁面 * 一、清空tabCache緩存 * 二、關閉全部域名內標籤 * 三、將關閉的標籤存入tabCache緩存數組 * 四、將isOpen狀態改成false */ function removePages(patternStr) { tabCache = [] chrome.tabs.query({active: true}, function (tab) { currentPageUrl = tab[0].url }) let pattern = new RegExp(patternStr) walkEveryTab(function (tab) { if (pattern.test(tab.url)) { chrome.tabs.remove(tab.id,function(){ tabCache.push(tab.url) }) } },function(){ isOpen = false }) } /** * 情形2:重載頁面 * 判斷有沒有緩存: * 情形2-1無緩存:開啓新標籤或定位到域名內的標籤 * 情形2-2有緩存:打開所有緩存內的頁面 */ function reloadPages(patternStr, mainPageUrl) { if (tabCache.length === 0) { focusOrCreateTab(patternStr, mainPageUrl) } else { openAllCachedTab(tabCache) } } /** * 情形2-1:開啓新標籤或定位到域名內的標籤 * 一、遍歷所有標籤,記錄符合域名的標籤的url,以及最後一個標籤頁 * 二、若是沒有符合域名的標籤,則建立主頁,並將isOpen狀態改成true * 三、若是有符合域名的標籤: * 一、獲取當前的頁面url * 二、若是當前頁面url不符合域名,則定位到這個標籤頁,將isOpen狀態改成true * 三、若是當前頁面url符合域名,則關閉全部標籤頁(按情形1處理),將isOpen狀態改成false */ function focusOrCreateTab(patternStr, url) { let pattern = new RegExp(patternStr) let theTabs = [] let theLastTab = null walkEveryTab(function (tab) { if (pattern.test(tab.url)) { theTabs.push(tab.url) theLastTab = tab } }, function () { if (theTabs.length > 0) { chrome.tabs.query({active: true}, function (tab) { let currentUrl = tab[0].url if (theTabs.indexOf(currentUrl) > -1) { removePages(patternStr) isOpen = false } else { chrome.tabs.update(theLastTab.id, {"selected": true}); isOpen = true } }) } else { chrome.tabs.create({"url": url, "selected": true}); isOpen = true } } ) } /** * 情形2-2: * 一、把tabCache全部標籤頁從新打開 * 二、將isOpen狀態改成true */ function openAllCachedTab(tabCache) { let focusTab = null tabCache.forEach(function (url, index) { chrome.tabs.create({'url': url}, function (tab) { if (tab.url === currentPageUrl) { focusTab = tab.id } if (index === tabCache.length-1 - 1) { if (focusTab) { chrome.tabs.update(focusTab, {"selected": true},function(){ }); } } }) }) isOpen = true } /** * * @param callback * @param lastCallback * 包裝一下遍歷所有標籤的函數,建立兩個回調。 * 一個回調是每一次遍歷的過程當中就執行一遍。 * 一個回調是所有遍歷完後執行一遍。 */ function walkEveryTab(callback, lastCallback) { chrome.windows.getAll({"populate": true}, function (windows) { for (let i in windows) { let tabs = windows[i].tabs; for (let j in tabs) { let tab = tabs[j]; callback(tab) } } if(lastCallback) lastCallback() }) }
咱們須要在Chrome的開發者中心發佈插件,進入 Developer Dashboard
好了,一個簡單易用的上班賴皮插件作好了!在調試模式下,你能夠用ctrl+s來快捷尋找、打開、關閉、從新打開賴皮頁面。隨時隨地、全方位賴皮,從容面對老闆查崗。
如今我但願個人插件均可以隨時配置站點:
那麼就須要用到chrome.storage了。
你須要打開storage權限:
manifest.json內添加
... "permissions": [ "tabs","storage" ], ...
而後使用
chrome.storage.local.set({ 'value1':theValue1, 'value2',theValue2 })
這種形式來存放storage。
這後使用
chrome.storage.local.get(['value1'],(res)=>{ const theValue1 = res.value1 })
這樣獲得存放的value值。
background.js
咱們把mainPageUrl
和myPattern
改爲從storage中獲取。
const INIT_SITES_LIST = ['bilibili.com','douyu.com','sgamer.com','hupu.com'] const INIT_MAIN_PAGE = 'https://bbs.hupu.com/all-gambia' // 在安裝時即設置好storage chrome.runtime.onInstalled.addListener(function() { chrome.storage.local.set({ sites: INIT_SITES_LIST, mainPage:INIT_MAIN_PAGE }) }); //初始化isOpen和tabCache狀態 let isOpen = false let tabCache = [] let currentPageUrl = '' /** * 開始步驟: 判斷isOpen狀態 * 情形一:isOpen爲true,則移除頁面 * 情形二:isOpen爲false,則重載頁面 */ chrome.commands.onCommand.addListener(function (command) { if (command === 'toggle-tags') { chrome.storage.local.get(['sites','mainPage'],function(res){ let sites = res.sites let mainPageUrl = res.mainPage let myPattern = sites.map(item=>item.replace('.','\\.')).join('|') console.log(myPattern) if (isOpen) { //情形一:isOpen爲true removePages(myPattern) //情形二:isOpen爲false } else { reloadPages(myPattern, mainPageUrl) } }) } }) // ======================== 下面的部分不須要改動,看到這裏就夠了) /** * 情形1:移除頁面 * 一、清空tabCache緩存 * 二、關閉全部域名內標籤 * 三、將關閉的標籤存入tabCache緩存數組 * 四、將isOpen狀態改成false */ function removePages(patternStr) { tabCache = [] chrome.tabs.query({active: true}, function (tab) { currentPageUrl = tab[0].url }) let pattern = new RegExp(patternStr) walkEveryTab(function (tab) { if (pattern.test(tab.url)) { chrome.tabs.remove(tab.id,function(){ tabCache.push(tab.url) }) } },function(){ isOpen = false }) } /** * 情形2:重載頁面 * 判斷有沒有緩存: * 情形2-1無緩存:開啓新標籤或定位到域名內的標籤 * 情形2-2有緩存:打開所有緩存內的頁面 */ function reloadPages(patternStr, mainPageUrl) { if (tabCache.length === 0) { focusOrCreateTab(patternStr, mainPageUrl) } else { openAllCachedTab(tabCache) } } /** * 情形2-1:開啓新標籤或定位到域名內的標籤 * 一、遍歷所有標籤,記錄符合域名的標籤的url,以及最後一個標籤頁 * 二、若是沒有符合域名的標籤,則建立主頁,並將isOpen狀態改成true * 三、若是有符合域名的標籤: * 一、獲取當前的頁面url * 二、若是當前頁面url不符合域名,則定位到這個標籤頁,將isOpen狀態改成true * 三、若是當前頁面url符合域名,則關閉全部標籤頁(按情形1處理),將isOpen狀態改成false */ function focusOrCreateTab(patternStr, url) { let pattern = new RegExp(patternStr) let theTabs = [] let theLastTab = null walkEveryTab(function (tab) { if (pattern.test(tab.url)) { theTabs.push(tab.url) theLastTab = tab } }, function () { if (theTabs.length > 0) { chrome.tabs.query({active: true}, function (tab) { let currentUrl = tab[0].url if (theTabs.indexOf(currentUrl) > -1) { removePages(patternStr) isOpen = false } else { chrome.tabs.update(theLastTab.id, {"selected": true}); isOpen = true } }) } else { chrome.tabs.create({"url": url, "selected": true}); isOpen = true } } ) } /** * 情形2-2: * 一、把tabCache全部標籤頁從新打開 * 二、將isOpen狀態改成true */ function openAllCachedTab(tabCache) { let focusTab = null tabCache.forEach(function (url, index) { chrome.tabs.create({'url': url}, function (tab) { if (tab.url === currentPageUrl) { focusTab = tab.id } if (index === tabCache.length-1 - 1) { if (focusTab) { chrome.tabs.update(focusTab, {"selected": true},function(){ }); } } }) }) isOpen = true } /** * * @param callback * @param lastCallback * 包裝一下遍歷所有標籤的函數,建立兩個回調。 * 一個回調是每一次遍歷的過程當中就執行一遍。 * 一個回調是所有遍歷完後執行一遍。 */ function walkEveryTab(callback, lastCallback) { chrome.windows.getAll({"populate": true}, function (windows) { for (let i in windows) { let tabs = windows[i].tabs; for (let j in tabs) { let tab = tabs[j]; callback(tab) } } if(lastCallback) lastCallback() }) }
那麼咱們能夠寫一個popup頁面,若是點擊圖標就會顯示,如圖:
下面咱們完善一下popup.html和popup.css和pupup.js頁面
全部js文件均可以直接調用chrome.storage.local.get
只須要特別注意一下js文件的chrome.storage
調用部分
其它的拷貝便可,咱們不是來學頁面佈局的
popup.html
<html> <head> <title>經常使用網站配置頁面</title> <link rel="stylesheet" href="popup.css"> </head> <body> <div class="container"> <h2 class="lapi-title">經常使用賴皮站點域名</h2> <ul class="lapi-content"> </ul> <p> <label><input type="text" id="add" class="add"></label> <button class="button add-button ">+</button> </p> <p></p> <p></p> <p></p> <h2>個人賴皮主頁</h2> <div id="change-content"> <span class="main-page-inactive" id="main-page-inactive"></span><button class="button change-button " id="change">✎</button> </div> <p class="lapi-tip">按<span class="lapi-key">Alt+S</span>快速開啓/關閉賴皮站點</p> </div> <script src="zepto.min.js"></script> <script src="popup.js"></script> </body> </html>
popup.css
* { margin: 0; padding: 0; color:#6a6f77; } input, button, select, textarea { outline: none; -webkit-appearance: none; border-radius: 0; border: none; } input:focus{ list-style: none; box-shadow: none; } ol, ul { list-style: none; } li{ margin: 5px 0; } .container { width: 200px; padding: 10px; } .container h2{ margin: 10px; text-align: center; display: block; } .lapi-content li{ transition: opacity 1s; } .site{ cursor: pointer; color: #00b0ff; } .add, .main-page{ box-sizing: border-box; text-align:center; font-size:14px; /*height:27px;*/ border-radius:3px; border:1px solid #c8cccf; color:#6a6f77; outline:0; padding:0 10px; text-decoration:none; width: 170px; } #main-page{ font-size: 12px; text-align: left; width: 166px; margin: 0; padding: 2px; } .add{ height: 27px; } .main-page{ width: 170px; outline: none; resize: none; } .main-page-inactive{ width: 160px; line-break: auto; word-break: break-word; overflow: hidden; display: inline-block; cursor: pointer; color: #00b0ff; margin: 3px; } .button{ font-size: 16px; /*border: 1px solid #c8cccf;*/ color: #c8cccf; /*border: none;*/ padding: 0 4px 1px 3px; border-radius: 3px; } .close-button{ transition: all 1s; } .button:hover{ background: #E27575; color: #FFF; } .add-button{ transition:all 1s; font-size: 20px; padding: 0 6px 1px 5px; } .change-button{ position: absolute; transition:all 1s; font-size: 20px; padding: 0 6px 1px 5px; } .change-button:hover{ background: #f9a825; color: #FFF; } #change-check{ color: #f9a825; } #change-check:hover{ color: #fff; } .add-button:hover{ background: #B8DDFF; color: #FFF; } .submit{ transition: all 1s; margin: 10px; padding: 5px 10px; font-size: 16px; border-radius: 4px; background: #B8DDFF; border: 1px solid #B8DDFF; color: #FFF; } .submit:hover{ border: 1px solid #B8DDFF; background: #fff; color: #B8DDFF; } .fade{ opacity: 0; } .add-wrong,.add-wrong:focus{ border: #e91e63 1px solid; box-shadow:0 0 5px rgba(233,30,99,.3); } .lapi-tip{ margin-top: 20px; border-top: 1px solid #c8cccf; padding-top: 8px; color: #c8cccf; text-align: center; } .lapi-key{ color: #B8DDFF; }
重點關注:chrome.storage
部分
popup.js
let sites = [] let mainPage = '' const isMac = /Macintosh/.test(navigator.userAgent) let $lapiKey = $('.lapi-key') isMac? $lapiKey.text('Control+S'):$lapiKey.text('Alt+S') // 從storage中取出site和mainPage字段,並設置在頁面上。 chrome.storage.local.get(['sites','mainPage'], function (res) { if (res.sites) { sites = res.sites mainPage = res.mainPage sites.forEach(function (item) { let appendEl = '<li><span class="site">' + item + '</span>\n' + '<button class="button close-button">×</button>\n' + '</li>' $('ul.lapi-content').append(appendEl) }) } $('#main-page').val(mainPage) $('#main-page-inactive').html(mainPage) }) $('#save').on('click', function () { alert() }) $('#change-content').delegate('#main-page-inactive','click',function(){ let mainPageUrl = $(this).html() if(/^http:\/\/|^https:\/\//.test(mainPageUrl)){ chrome.tabs.create({"url": mainPageUrl, "selected": true}) }else{ chrome.tabs.create({"url": 'http://'+mainPageUrl, "selected": true}) } }) let addEl = $('#add') addEl.focus() let lapiCon = $('ul.lapi-content') lapiCon.delegate('.close-button', 'click', function () { let $this = $(this) let siteValue = $this.siblings().html() sites = sites.filter(function (item) { return item !== siteValue }) chrome.storage.local.set({sites: sites}) $this.parent().addClass('fade') setTimeout(function () { $this.parent().remove() }, 800) }) $('.add-button').on('click',addEvent) addEl.bind('keypress',function(event){ if(event.keyCode === 13) addEvent() }) function addEvent(){ if(!validate(addEl.val())){ addEl.addClass('add-wrong') }else{ let appendEl = '<li><span class="site">' + addEl.val() + '</span>\n' + '<button class="button close-button">×</button>\n' + '</li>' $('ul.lapi-content').append(appendEl) sites.push(addEl.val()) chrome.storage.local.set({sites:sites}) addEl.removeClass('add-wrong') addEl.focus().val('') } } function validate(value){ value = value.trim() if(value.length ===0){ return false } return /^([\w_-]+\.)*[\w_-]+$/.test(value) } lapiCon.delegate('.site','click',function(){ let siteUrl = $(this).html() chrome.tabs.create({"url": 'http://'+siteUrl, "selected": true}) }) $('#change-content').delegate('#change','click',function(){ changeMainPage($(this)) }).delegate('#change-check','click',function(){ changeCheck($('#change-check')) }).delegate('#main-page','blur',function(){ changeCheck($('#change-check')) }) function changeMainPage($this){ $this.siblings().remove() $this.parent().prepend('<label><textarea id="main-page" class="main-page"></textarea></label>') $this.parent().append('<button class="button change-button " id="change-check">✓</button>') $('#main-page').val(mainPage).focus() $this.remove() } function changeCheck($this){ let mainPageVal = $('#main-page').val() $this.siblings().remove() $this.parent().prepend('<span class="main-page-inactive" id="main-page-inactive"></span>') $('#main-page-inactive').text(mainPageVal) chrome.storage.local.set({mainPage:mainPageVal}) $this.parent().append('<button class="button change-button " id="change">✎</button>') }
好了,一個優雅的賴皮插件就作好了,你們能夠查看
https://github.com/wanthering...
祝你們上班賴得開心。 別老躲廁所玩手機了,不健康!賴皮插件用起來!