不少人介绍过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...
祝你们上班赖得开心。 别老躲厕所玩手机了,不健康!赖皮插件用起来!