摘要:咦,為什么不做一個插件用來管理呢每次同時打開過多的選項卡時,被擠壓的標(biāo)題總是讓我分不清哪個是哪個,查看起來十分不便。即數(shù)據(jù)中的屬性,因此關(guān)閉選項卡的功能實現(xiàn)起來也沒有問題。
1. 前言
繼上周第一次開發(fā)Chrome插件github-star-trend之后,我就一直尋思有什么現(xiàn)實問題可以用插件來解決呢?正當(dāng)我在瀏覽器中搜索尋找靈感時,打開的眾多tab選項卡令我靈光一閃。
咦,為什么不做一個插件用來管理tab呢?每次同時打開過多的tab選項卡時,被擠壓的標(biāo)題總是讓我分不清哪個是哪個,查看起來十分不便。于是乎,經(jīng)過一個周末下午的折騰,我倒騰出這么個東西(gif圖可能有點大,請耐心等待...):
2. 準(zhǔn)備工作按照慣例,正式進(jìn)入主題之前讓我們來先了解點預(yù)備知識。默默打開Chrome插件的官方文檔,直奔我們的Tabs??梢钥吹剿鼮槲覀兲峁┝撕芏喾椒?,而且竟然還有executeScript,這個可以說權(quán)限非常大了,不過跟我們這次的需求沒啥關(guān)系。。。
2.1 query由于我們的需求是管理tab選項卡,所以首先肯定得獲取所有的tab信息。掃了一遍Methods,最相關(guān)的就是方法query:
Gets all tabs that have the specified properties, or all tabs if no properties are specified.
正如官方介紹,該方法可以根據(jù)指定條件返回相應(yīng)的tabs;且當(dāng)不指定屬性時,可以獲得所有的tabs。這恰好滿足我們的需求,按照API指示,我在callback中嘗試打印出了拿到的tabs對象:
chrome.tabs.query({}, tabs => console.log(tabs));
[ { "active": true, "audible": false, "autoDiscardable": true, "discarded": false, "favIconUrl": "https://static.clewm.net/static/images/favicon.ico", "height": 916, "highlighted": true, "id": 25, "incognito": false, "index": 0, "mutedInfo": {"muted":false}, "pinned": true, "selected": true, "status": "complete", "title": "草料文本二維碼生成器", "url": "https://cli.im/text?bb032d49e2b5fec215701da8be6326bb", "width": 1629, "windowId": 23 }, ... { "active": true, "audible": false, "autoDiscardable": true, "discarded": false, "favIconUrl": "https://www.google.com/images/icons/product/chrome-32.png", "height": 948, "highlighted": true, "id": 417, "incognito": false, "index": 0, "mutedInfo": {"muted": false}, "pinned": false, "selected": true, "status": "complete", "title": "chrome.tabs - Google Chrome", "url": "https://developers.chrome.com/extensions/tabs#method-query", "width": 1629, "windowId": 812 } ]
仔細(xì)觀察不難發(fā)現(xiàn),兩個tab的windowId不同。這是由于我在本地同時打開了兩個Chrome窗口,而這兩個tab恰好在兩個不同的窗口內(nèi),所以正好符合預(yù)期。
另外id,index, highlighted,favIconUrl,title等字段信息在后文中也起到非常重要的作用,相關(guān)的釋義都可以在這里查看。
在構(gòu)思Chrome插件UI時,為了突出當(dāng)前窗口中的當(dāng)前tab,我們就必須從上述數(shù)據(jù)中找出這個tab。由于每個窗口中都有一個tab是highlighted的,所以我們無法直接確定哪個tab是當(dāng)前窗口的。不過,我們可以這樣:
chrome.tabs.query( {active: true, currentWindow: true}, tabs => console.log(tabs[0]) );
根據(jù)文檔,通過指定active和currentWindow這兩個屬性為true,我們就能順利拿到當(dāng)前窗口的當(dāng)前tab。然后再根據(jù)tab的windowId和highlighted進(jìn)行匹配,我們就能從tabs數(shù)組中定位出哪個才是真正的當(dāng)前tab了。
2.2 highlight根據(jù)上面所述,我們已經(jīng)可以拿到所有的tabs信息以及確定出哪個tab是當(dāng)前窗口的當(dāng)前tab,所以我們可以根據(jù)這些數(shù)據(jù)構(gòu)建出一個列表。而接下來要做的就是,當(dāng)用戶點擊其中某一項時,瀏覽器就能切換到所對應(yīng)的tab選項卡。帶著這個需求,再次翻閱文檔找到了highlight:
Highlights the given tabs and focuses on the first of group. Will appear to do nothing if the specified tab is currently active.
chrome.tabs.highlight({windowId, tabs});
根據(jù)該API的指示,它需要的是windowId和tab的index,而這些信息都在每個tab實體中可以拿到。不過這里有一個坑需要注意:那就是如果在當(dāng)前窗口切換到另一個窗口的tab時,雖然另一個窗口的tab得以切換,但是Chrome窗口仍聚焦于當(dāng)前窗口。所以需要用以下的方法,令另外的那個窗口得到聚焦:
chrome.windows.update(windowId, {focused: true});2.3 remove
為了增強(qiáng)插件的實用性,我們可以在tabs列表中加入刪除指定tab選項卡的功能。而在翻閱文檔之后,可以確定remove可以實現(xiàn)我們的需求。
Closes one or more tabs.
chrome.tabs.remove(tabId);
tabId即tab數(shù)據(jù)中的id屬性,因此關(guān)閉選項卡的功能實現(xiàn)起來也沒有問題。
3. 開工不同于插件github-star-trend,這次復(fù)雜度更高,涉及到更多的交互操作。為此,我們引入react,antd和webpack,不過整體開發(fā)起來還是比較容易的,更多的可能還是在于Chrome插件提供的API熟練度。
3.1 manifest.json{ "permissions": [ "tabs" ], "content_security_policy": "script-src "self" "unsafe-eval"; object-src "self"", "browser_action": { "default_icon": { "16": "./icons/logo_16.png", "32": "./icons/logo_32.png", "48": "./icons/logo_48.png" }, "default_title": "Tab Killer", "default_popup": "./popup.html" } }
由于這次開發(fā)的插件跟tabs相關(guān),所以我們需要在permissions字段中申請tabs權(quán)限。
由于webpack在dev模式下打包會用到eval,Chrome瀏覽器出于安全策略會報錯,因此需要設(shè)置content_security_policy使其忽略(如果是prod模式打的包,就不需要設(shè)置)。
本次插件的交互是點擊按鈕彈出一個浮層,所以需要設(shè)置browser_action屬性,而其default_popup字段正是我們接下來要開發(fā)的頁面。
3.2 App.js該文件是我們的核心文件之一,主要負(fù)責(zé)tabs數(shù)據(jù)的獲取和處理等維護(hù)工作。
根據(jù)API文檔所示,獲取tabs數(shù)據(jù)是一個異步操作,我們在其回調(diào)函數(shù)中才能拿到。這也意味著我們的應(yīng)用一開始應(yīng)該是處于一個LOADING的狀態(tài),拿到數(shù)據(jù)之后成為OK狀態(tài),另外再考慮到異常情況(例如無數(shù)據(jù)或出錯),我們可以將其定義為EXCEPTION狀態(tài)。
class App extends React.PureComponent { state = { tabsData: [], status: STATUS.LOADING } componentDidMount() { this.getTabsData(); } getTabsData() { Promise.all([ this.getAllTabs(), this.getCurrentTab(), Helper.waitFor(300), ]).then(([allTabs, currentTab]) => { const tabsData = Helper.convertTabsData(allTabs, currentTab); if(tabsData.length > 0) { this.setState({tabsData, status: STATUS.OK}); } else { this.setState({tabsData: [], status: STATUS.EXCEPTION}); } }).catch(err => { this.setState({tabsData: [], status: STATUS.EXCEPTION}); console.log("get tabs data failed, the error is:", err.message); }); } getAllTabs = () => new Promise(resolve => chrome.tabs.query({}, tabs => resolve(tabs))) getCurrentTab = () => new Promise(resolve => chrome.tabs.query({active: true, currentWindow: true}, tabs => resolve(tabs[0]))) render() { const {status, tabsData} = this.state; return (); } } const Helper = { waitFor(timeout) { return new Promise(resolve => { setTimeout(resolve, timeout); }); }, convertTabsData() {} }
思路很簡單,就是在didMount的時候獲取tabs數(shù)據(jù),不過我們在這里用到Promise.all來控制異步操作。
由于獲取tabs數(shù)據(jù)這一操作是異步的,不同電腦,不同狀態(tài),不同tab數(shù)量時該操作的耗時都可能不同,所以為了更好的用戶體驗,我們可以在一開始用antd的Spin組件來充當(dāng)占位符。需要注意的是,如果獲取tabs數(shù)據(jù)非???,Loading動畫會有一閃而過的感覺,并不十分友好。因此我們用個300ms的promise搭配Promise.all使用,可以保證至少300ms的Loading動畫。
接下來就是拿到tabs數(shù)據(jù)之后的convert工作。
Chrome提供的API獲取到的數(shù)據(jù)是一個扁平的數(shù)組,不同窗口內(nèi)的tab也被混在同一個數(shù)組內(nèi)。我們更希望能按窗口進(jìn)行分組,這樣在瀏覽和查找時對用戶更直觀,操作更方便,用戶體驗更好。所以我們需要對tabsData進(jìn)行一次轉(zhuǎn)換:
convertTabsData(allTabs = [], currentTab = {}) { // 過濾非法數(shù)據(jù) if(!(allTabs.length > 0 && currentTab.windowId !== undefined)) { return []; } // 按windowId進(jìn)行分組歸類 const hash = Object.create(null); for(const tab of allTabs) { if(!hash[tab.windowId]) { hash[tab.windowId] = []; } hash[tab.windowId].push(tab); } // 將obj轉(zhuǎn)成array const data = []; Object.keys(hash).forEach(key => data.push({ tabs: hash[key], windowId: Number(key), isCurWindow: Number(key) === currentTab.windowId })); // 進(jìn)行排序,將當(dāng)前窗口的順序往上提,保證更好的體驗 data.sort((winA, winB) => { if(winA.isCurWindow) { return -1; } else if(winB.isCurWindow) { return 1; } else { return 0; } }); return data; }3.3 TabList.js
根據(jù)App.js中的設(shè)計,我們可以先搭起代碼的骨架:
export class TabsList extends React.PureComponent { renderLoading() { return (); } renderOK() { // TODO... } renderException() { return ( ); } render() { const {status} = this.props; switch(status) { case STATUS.LOADING: return this.renderLoading(); case STATUS.OK: return this.renderOK(); case STATUS.EXCEPTION: default: return this.renderException(); } } }
接下來就是renderOK的實現(xiàn),由于沒有固定的設(shè)計稿,我們可以盡情發(fā)揮自己的想象。這里借助antd粗略地實現(xiàn)了一版交互(加入了切換tab、搜索和刪除等操作),具體代碼考慮到篇幅就不貼了,感興趣的可以進(jìn)這里查看。
4. 完結(jié)整個插件的制作過程,到這兒就已經(jīng)完了。如果你有更好的idea或設(shè)計,可以提PR哦~通過這次學(xué)習(xí),熟悉了對Tabs的操作,同時對Chrome插件的制作流程也算是有了更進(jìn)一步的感悟。
5. 參考Chrome extensions dev guide
Chrome extensions browserAction
Chrome extensions tabs
本文所有代碼托管在這兒,覺得還行的可以star一下,也可以關(guān)注我的Blog。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104955.html
摘要:代碼代碼戳這里插件預(yù)備知識首先給出一本參考的中文書籍,在練習(xí)的過程中有幫到忙。你還可以重寫別的頁面,比如書簽管理頁面等,可以參考文檔中文翻譯過來應(yīng)該叫內(nèi)容腳本,它可以運(yùn)行在你指定的頁面之中,可以拿到指定頁面的一些信息。 前言 這是一篇關(guān)于Chrome擴(kuò)展插件入門、Vue.js入門的小練習(xí),功能是:在當(dāng)前瀏覽的頁面點擊擴(kuò)展圖標(biāo),并點擊保存之后,該頁面就會存在你的新標(biāo)簽頁中。其實就是一個可...
摘要:預(yù)加載自定義事件第三方擴(kuò)展插件涉及的,除了,其它所有手機(jī)瀏覽器及瀏覽器均無法使用,目前主要包括語音輸入事件相關(guān)注意瀏覽器沒有事件事件相關(guān)的,手機(jī)端瀏覽器均可使用端模擬手機(jī)瀏覽器也可以正常使用。 最近項目中需要使用MUI做一個視頻播放的小功能。我就花時間研究了一下MUI。 MUI是一個使用JavaScript開發(fā)Android和IOS應(yīng)用的前端框架。這篇文章將以知識樹的形式對MUI的使用...
摘要:在這篇文章中,我將列出我最喜歡的快捷鍵,這些快捷鍵讓我更快的編寫代碼,也讓編碼變得更有趣,以下是個快捷鍵,分享給你。打開鍵盤快捷鍵或,搜索。在中,啟動性能是很重要的。逐個選擇文本可以通過快捷鍵右箭頭右箭頭和左箭頭左箭頭逐個選擇文本。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 注意:自己嘗試的時候,Mac(17, p...
摘要:以下示例將阻止所有對的請求。從存儲請求和阻止請求的對象中刪除當(dāng)前選項卡的屬性。收聽消息告知后臺進(jìn)程阻止的列表已被用戶更新。兩者都提供類似的功能和事件處理程序。 前言 當(dāng)我們?yōu)g覽網(wǎng)站時,都會發(fā)送許多請求來獲取網(wǎng)頁內(nèi)容。這些請求中有些是重要的,而有些是我們不需要,因為它們可能是廣告或建議等。在本文中,將創(chuàng)建一個有助于阻止和取消阻止所選URL的Chrome擴(kuò)展插件,讓你選擇你打開的網(wǎng)址及該打...
摘要:特指度度量的是選擇器識別元素的精確性。為中的各個變量賦予相應(yīng)的數(shù)值,就能得到特指度。為類選擇器屬性選擇器和偽類的數(shù)量。該文件包含選項卡組的樣式。易于混淆的屬性,應(yīng)用注釋予以說明。屬性按照字母順序排列。屬性值為時,省略單位。 1、什么是優(yōu)秀的架構(gòu) (1)優(yōu)秀的架構(gòu)是可預(yù)測的(2)優(yōu)秀的架構(gòu)是可擴(kuò)展的(3)優(yōu)秀的架構(gòu)可提升代碼復(fù)用性(4)優(yōu)秀的架構(gòu)可擴(kuò)展(5)優(yōu)秀的架構(gòu)可維護(hù)什么時候可以重...
閱讀 2881·2023-04-26 01:00
閱讀 796·2021-10-11 10:59
閱讀 3011·2019-08-30 11:18
閱讀 2708·2019-08-29 11:18
閱讀 1044·2019-08-28 18:28
閱讀 3042·2019-08-26 18:36
閱讀 2162·2019-08-23 18:16
閱讀 1093·2019-08-23 15:56