Chrome 是 web 開發人員必備的瀏覽器沒有之一。
要編寫一個 chrome 瀏覽器擴展也只需要(基本的)html,js ,css 這些 web 開發的基本能力。
下面先看一下一個 chrome 擴展的項目結構吧:
├── images
│ ├── icon-128.png
│ └── icon-16.png
├── manifest.json
├── options.html
├── popup.html
└── scripts
├── background.js
從文件結構來看,其實所謂的 chrome 擴展也就是一個類似 webapp 的東西。
其中最重要的就是 manifest.json
這個配置文件了,其中包含了擴展的一些元數據,
包含名稱,版本,圖標,權限,各類入口文件等信息。
在上面的例子中,options.html
是配置項的界面,popup.html
是擴展彈出頁的界面,而 background.js
相當于擴展的后臺進程,可以獨立執行邏輯或者給前端(popup)提供數據。
一般來說對于擴展或者插件類應用的編寫采取任務驅動的方式是最適合的。下面我們就以一個『send to gitlab issue』的擴展為例,來講解一下 chrome 擴展的開發流程。
這個擴展的功能類似于 pocket 這個稍后閱讀的服務,只不過 pocket 這類應用都只能對保存的網頁做出『已讀』或者『未讀』的區分,而對于一篇技術文章,很難說到底讀懂了多少,理解消化的程度讓我很難把一篇技術文章標記為『已讀』,這就造成了我的 pocket 中未讀數量只增不減,到頭來等于沒有整理。
所以我打算寫一個擴展,可以根據當前網頁鏈接創建一個 gitlab 的 issue ,gitlab 的 issue 相對而言,可以有更大的空間做筆記,也可以很方便的直接把文章中的知識學以致用后和 gitlab 上托管的項目代碼直接關聯,而且 Milestone 配合 label 的分類也比 pocket 單純的 tag 要強不少,更別提還能和別人一起討論了。
那么先分析一下需求,作為一個用戶,當我看到一篇不錯的技術文章,我想把它記錄到 gitlab 上,然后下班后仔細研讀,做筆記,并和同事們討論一下。于是我點擊了 chrome 瀏覽器右上角的 『send to gitlab issue』圖標,彈出了一個小頁面,里面預先填好了文章的標題和 url,我可以稍微修改(或?不做),然后點擊保存,那么這篇文章就被記錄到(預先配置好的)gitlab 服務器上了。
對于這樣一個小項目,可以先從一個最簡的 demo 開始,先讓擴展在瀏覽器上『占有一席之地』,然后點擊后可以彈出一個小的 ui 界面。
別急著創建配置文件和 html 頁面,懶惰是程序員的一種美德,先使用 這個 基于 yeoman 的 chrome 擴展 generator 來生成項目骨架(還支持 es2015 哦)。
然后在 manifest.json
中指定擴展的彈出頁面文件:
"browser_action": {
"default_popup": "popup.html"
}
然后創建 popup.html
文件,編寫 html 內容,一般樣式文件直接在 header 中即可,而 js 文件 必須 從外部引用,不能直接寫在 html 中。
<body>
<p id="flash">
</p>
<input type="text" id="popup_title" placeholder="標題"/>
<textarea type="text" id="popup_url" placeholder="url">
</textarea>
<br />
<button type="button" id="popup_button">發送</button>
<script src="scripts/app.js"></script>
</body>
現在可以打開 chrome 瀏覽器的擴展管理頁面,如圖勾選載入開發中的擴展,選定項目的 app
目錄,就能看到瀏覽器的右上角出現了我們正在開發的擴展了。
一切順利的話,點擊圖標,就會出現我們編寫的 html 頁面了。
好了讓我們進入下一步,獲取當前頁面的標題和 url。很明顯這兩個數據需要通過 chrome 提供的接口才能獲取,這里我本來采用的是在 background.js
中直接去獲取 dom 樹的內容,可是測試后發現,background.js
獲取的總是第一個頁面的內容,當切換 tab 頁后數據也不會改變。結果還是老老實實地看文檔,找到了下面的這段代碼:
chrome.tabs.query({
active: true,
currentWindow: true
}, (tabs) => {
document.getElementById('popup_title').value = tabs[0].title;
document.getElementById('popup_url').value = tabs[0].url;
});
chrome 提供了獲取當前激活的 tab 頁面的接口,于是我們就能獲取到當前 tab 頁的 title 和 url 了, 順便把他們自動填充到頁面的 input 框中。
注意這里需要配置擴展的權限,加入對 tab 的訪問權限才行,在 manifest.json
中加入:
"permissions": ["tabs"]
之后的工作就是把 input 框的內容傳遞給 gitlab 了,關于 gitlab 的接口調用不是本文重點,可以直接參考官方的文檔。gitlab 對外暴露 RESTful 的 http 接口,不需要任何 sdk 也可以很方便地調用。
document.getElementById('popup_button').addEventListener('click', function() {
createGitlabIssue(localStorage.options_project_id,
document.getElementById('popup_title').value,
document.getElementById('popup_url').value)
});
一開始我先把 gitlab 相關配置都硬編碼到了擴展中,跑通流程后當然就不能直接就這樣提交,需要把它們做成配置項。
chrome 擴展可以自定義一個配置頁面,在 manifest.json
中加入:
"options_page": "options.html",
然后創建 options.html
文件,并編寫 html 內容:
<body>
<p id="flash"></p>
gitlab host: <input type="text" id="options_host" placeholder="gitlab host" />
<br />
project id: <input type="text" id="options_project_id" placeholder="project id" />
<br />
private token: <input type="text" id="options_token" placeholder="private token" />
<br />
<button type="button" id="options_save">save</button>
<script src="scripts/options.js"></script>
</body>
然后編寫 options.js
文件,把配置項持久化到 localStorage 中:
var options_host = localStorage.options_host
document.getElementById('options_host').value = options_host;
var options_project_id = localStorage.options_project_id
document.getElementById('options_project_id').value = options_project_id;
var options_token = localStorage.options_token
document.getElementById('options_token').value = options_token;
document.getElementById('options_save').onclick = function() {
localStorage.options_host = document.getElementById('options_host').value;
localStorage.options_project_id = document.getElementById('options_project_id').value;
localStorage.options_token = document.getElementById('options_token').value;
document.getElementById('flash').innerHTML = '保存成功';
}
現在如果右鍵點擊擴展的圖標的話,就能看到『選項』這個菜單是可點擊的:
然后在處理 gitlab 的 api 調用時,動態讀取配置內容即可。
這樣一來,一個簡單的 chrome 擴展就這樣完成了。當然還有很多細節需要打磨。
項目地址:https://github.com/teddy-ma/send-to-gitlab-issue ,歡迎各類 issue 和 pr。