一、背景
相信看到這篇文章的人應該都用過Chrome外掛吧,最近剛好有個這方面的需求,我就把Chrome外掛的相關知識學習了一下,發現其實Chrome外掛的開發和大前端Web開發的底子是一樣的,無非就是runtime只限於Chrome瀏覽器,並且可以呼叫Chrome提供的一些chrome.*
API來實現一些基於Chrome瀏覽器的小功能。這裡非要類比的話,我理解chrome.*
API就像我們開發Hybird應用一樣,需要有一個bridge層來提供底層原生的能力給js。我是做Android開發出生的,這只是我的個人理解,可能對大Web技術的理解還是不夠。
其實Chrome上的外掛,從UI上主要分成兩類:一類是瀏覽器按鈕(BrowserAction),另一類是頁面按鈕(PageAction)。兩者的開發大同小異,我這裡今天主要介紹的主角不是Chrome外掛開發,而是如何使用React.js來開發Chrome外掛,本文先簡單介紹下Chrome外掛的開發和ReactJS,最後介紹如何採用Facebook官方推薦的creat-react-app腳手架來開發Chrome外掛。
二、Chrome外掛開發基礎知識
下面是我看的幾篇教程,簡單看一下應該就可以算Chrome外掛速成了:
簡單來說,一個最基本Chrome外掛應用需要有一個manifest.json清單檔案,這個檔案一般長這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "manifest_version": 2, "name": "One-click Kittens", "description": "This extension demonstrates a browser action with kittens.", "version": "1.0", "permissions": [ "https://secure.flickr.com/" ], "browser_action": { "default_icon": "icon.png", "default_popup": "popup.html" } } |
這個檔案裡描述了外掛應用的一些屬性,如名稱、版本、需要的許可權、介面的對應的html檔名等等。額!!乍一看怎麼和AndroidManifest.xml的功能這麼像啊?是的大兄弟!!恭喜你對技術的理解已經融會貫通了!
根據manifest.json檔案可以看到,一個Chrome外掛最少得有:manifest.json檔案,icon.png圖示和popup.html檔案。當然檔名可以隨便改,只要和manifest.json裡宣告的一致就行。
這裡就不浪費時間具體說怎麼開發外掛了,各路前端大牛比我強100倍。但我只強調一點,那就是popup.html中引用的js檔案只能是外部引入,不能在popup.html檔案裡面寫js程式碼。所以一般我們還有見到popup.js檔案。另外如果你想知道自己使用的外掛有什麼祕密,完全可以去Chrome瀏覽器的安裝目錄下面把它們給扒出來。。
三、React JS基礎知識
React.js不需要多說了吧,從React這個詞在技術界誕生起,就是一顆明星,連我這種死抱著Native技術的人都不得不去學習它。。
簡單扯兩句React JS的話題(React Native下次再說),作為一個Android App/SDK開發,我沒有開發過太多傳統意義上的Web頁面,但是經過我學習了大概一週多的時間,我發現React JS開發Web頁面的思路其實和客戶端很像,不去用jQuery/Zepto啊操作DOM,而是關注資料本身,以資料驅動去改變介面。重構寫好了靜態html後,哪塊地方需要變化,你就把哪裡變成一個變數放到元件的State/Props裡面(至於元件怎麼切分,哪個資料放State,哪個放Prop不是今天要討論的話題),然後就只用關注資料的變化,然後setState一下介面就可以重新整理了。理解了這一點,就會發現其實開發Web頁面很簡單。比起操作DOM,一些模板引擎之類的東西,我認為React這個思想非常容易接受,寫起來也很舒服,完全沒有那種混亂的感覺,而且現在ReactJS生態圈非常大,諸如Redux這類的庫使得ReactJS越發的犀利,很多公司早就用得飛起了。
扯得有點遠了,ReactJS開發我推薦大家就看Facebook官方的示例就夠了。英文不好的朋友可以看看阮一峰老師的部落格,或者看看這篇入門教程也是闊以的。
四、應該用哪個腳手架?
當然是Facebook官方推薦的creat-react-app。開啟終端,依次輸入:
1 2 3 |
npm install -g create-react-app create-react-app my-app cd my-app/ |
然後就在my-app
下面看到這些檔案了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
my-app/ README.md node_modules/ package.json .gitignore public/ favicon.ico index.html manifest.json src/ App.css App.js App.test.js index.css index.js logo.svg registerServiceWorker.js |
到此為止,是一個標準的ReactJS編寫WebApp的步驟,在終端輸入npm start
,就可以在瀏覽器中訪問本地的localServer了。
1.怎麼讓這個專案支援Chrome外掛開發呢?
前面介紹了,Chrome外掛最重要的檔案就是manifest.json清單檔案。我們先看下腳手架給我們預設生成的manifest.json長啥樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "short_name": "React App", "name": "Create React App Sample", "icons": [ { "src": "favicon.ico", "sizes": "192x192", "type": "image/png" } ], "start_url": "./index.html", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } |
對於一個普通的WebApp來說,manifest.json檔案在快取、離線模式以及最新的PWA場景下會起作用,但是這裡我們是要開發Chrome外掛,那麼把它原來的內容通通刪掉,改成你的Chrome外掛所需要的格式和內容就好了。例如可以改成這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "manifest_version": 2, "name": "MyChromeExt", "description": "My first chrome extension.", "version": "1.0.0", "icons": { "16": "img/icon-16.png", "128": "img/icon-128.png" }, "browser_action": { "default_icon": { "19": "img/icon-19.png", "38": "img/icon-38.png" }, "default_title": "MyChromeExt", "default_popup": "index.html" }, "permissions": [ "tabs" ], "background": { "scripts": ["background.js"] } } |
這裡儘可能對腳手架的東西做最小的改動,把default_popup的檔名改成了index.html
,因為腳手架預設會把js檔案都打包到一個main.js檔案中,並在index.html中插入這個main.js。
我們執行一下npm run build
命令,就會發現生成了一個my-app/build
目錄,這個目錄就是我們可以在chrome://extensions/去載入的外掛目錄,當然也可以用Chrome把這個目錄打包成一個crx外掛。
使用creat-react-app腳手架開發Chrome外掛的基本方法就是這樣了,但是在實際中我們會遇到很多的問題,有時甚至會想要執行npm run eject,然後去完全自定義webpack.config.js
來實現打包。
2.background.js怎麼打包?
我們在開發外掛的時候,非常可能需要用到後臺的background.js,原因如下:
注意:不要在popup頁面的js空間變數中儲存資料。由於popup頁面只在使用者點選圖示時才會開啟,當使用者關閉這個頁面時就會停止,並沒有一個從始至終的例項分配給popup頁面。所以每當使用者開啟popup頁面時,它都是嶄新的,之前儲存在變數中的資料都會消失。如果需要通過popup頁面儲存使用者的資料,可以通過通訊將資料交給後臺頁面(background頁面)處理,或者通過localStorage和chrome.storage將資料儲存在使用者的硬碟上。
所以background.js最後也是要進入到我們的釋出資料夾下面的,這裡建議還是要堅持最低程度地修改腳手架的設定,建議不要npm run eject之後來修改webpack的配置,因為實在是真的有點複雜。
這次修改下package.json
檔案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "name": "my-app", "version": "0.1.0", "private": true, "devDependencies": { "react-scripts": "1.0.7" }, "dependencies": { "react": "^15.6.1", "react-dom": "^15.6.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject", "build-chrome-ext": "react-scripts build && cp src/background.js build/background.js" } } |
可以看到我們新增了一個命令npm run build-chrome-ext
,並把background.js丟到了build目錄下。如果你還有其他的js,我建議在my-app/src
下建立一個my-app/src/chrome
資料夾,專用於存在chrome相關其他js程式碼,然後在build的時候統一丟到build裡面。如果要minify這些js,同樣可以採用&&
方式去新增命令。修改
3.需要注意的細節
由於使用了一些chrome.*
API,我們需要在編譯js的時候將chrome
這個全域性物件宣告一下。
creat-react-app這個腳手架在非eject模式下,沒辦法修改ESlint的配置來新增global物件,只能在用到了 chrome.*
API的程式碼處新增 // eslint-disable-line
註釋來實現保證編譯通過。
如果你已經npm run eject
了,在eject模式下,可以在package.json
檔案裡配置ESLint:
1 2 3 4 5 6 |
"eslintConfig": { "extends": "react-app", "globals": { "chrome": true } } |
五、其他腳手架推薦
除了自己改造Facebook推薦的creat-react-app外,下面兩個腳手架也算是使用者比較多的,專門用於開發Chrome外掛的腳手架。
- https://github.com/jhen0409/react-chrome-extension-boilerplate:預設支援ReactJS,基於webpack。
- https://github.com/yeoman/generator-chrome-extension:沒有預設支援ReactJS,需要自己修改,基於gulp打包。