# Chrome Extension
https://www.udemy.com/course/chrome-extension-development-course-for-everyone/
線上課程的筆記和心得整理
## 包含4個部分
1. Manifest file
該應用程式的資訊,用於顯示於chrome商店和使用者
2. background scripts
執行background屬性內的js,通常是監聽使用者的一些行為並執行特定函式,例如新增書籤時
3. content scripts
執行content屬性內的js,同時也會有matches屬性規定在哪個網址內才執行
4. UI elements
跟使用者互動的元素,例如套件的顯示logo、滑鼠移過去hover的字樣、或點下顯示一個小的html(可以包含一些資訊或按鈕)
# Manifest file
## 創建一個chrome extension
node安裝套件
`npm install type @types/chrome`
建立manifest.json
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2
}
```
這邊只填寫必要的屬性,想知道更多manifest的屬性可以參考:https://developer.chrome.com/extensions/manifest
chrome網址輸入
`chrome://extensions/`
右上角開啟`開發人員模式`
點擊`載入未封裝項目`並選擇該專案資料夾

# background scripts
manifest.json新增background屬性
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false // 如果要隨時接收chrome或WebAPI等情況才會永遠執行
}
}
```
新增background.js
```javascript=
// extension載入完畢時執行
chrome.runtime.onInstalled.addListener(()=>{
console.log('installed')
})
```
都儲存後直接到`chrome://extensions/`點擊右下角的重新載入就好了

然後點擊背景頁面查看執行成果

接著繼續新增
```javascript=
// extension載入完畢時執行
chrome.runtime.onInstalled.addListener(()=>{
console.log('installed')
})
// 新增頁籤時執行
chrome.bookmarks.onCreated.addListener(()=>{
alert('Bookmark saved')
})
```
然後同樣儲存後重新載入

當有錯誤時可以點擊錯誤查看問題

這個錯誤是因為bookmarks必須要有權限才行,所以回到manifest.json新增permissions屬性
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false // 如果要隨時接收chrome或WebAPI等情況才會永遠執行
},
"permissions": [
"bookmarks"
]
}
```
清除原本的錯誤訊息並重新載入後,如果沒有再跳出錯誤的話就代表成功了
當你嘗試加入書籤時就會跳出alert的視窗

# content scripts
manifest.json新增content_scripts屬性
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false // 如果要隨時接收chrome或WebAPI等情況才會永遠執行
},
"permissions": [
"bookmarks"
],
"content_scripts": [
{
"js": ["content.js"],
// 在那些網址會載入content.js
"matches": ["https://*.youtube.com/*"],
// 除了哪些網址之外
"exclude_matches": ["https://*.youtube.com/watch*"]
}
]
}
```
新增content.js
```javascript=
window.onload = () => {
document.querySelector('#end').prepend('Dark mode')
}
```
儲存並重新載入之後連到youtube首頁

會發現左邊多了一個Dark mode的字樣,
而隨便點進去一個影片後重新整理(注意因為沒有監控url的變化,所以一定要重新整理),就會發現上面的Dark mode字樣不見了
將Dark mode文字變成按鈕
```javascript=
// content.js
window.onload = () => {
const button = document.createElement('button')
button.id = "darkModeButton"
button.textContent = "DO IT DARK"
document.querySelector('#end').prepend(button)
button.addEventListener('click', () => enableDarkMode())
}
function enableDarkMode() {
document.querySelector('ytd-app').style.background = 'black'
}
```
儲存並重新載入後

按下button背景就會變成黑色了
另外可以在manifest.json新增run_at屬性,決定何時執行
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"bookmarks"
],
"content_scripts": [
{
"js": ["content.js"],
// 在那些網址會載入content.js
"matches": ["https://*.youtube.com/*"],
// 除了哪些網址之外
"exclude_matches": ["https://*.youtube.com/watch*"],
"run_at": "document_end"
}
]
}
```
# UI elements
新增icons, browser_action屬性
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"bookmarks"
],
"content_scripts": [
{
"js": ["content.js"],
"matches": ["https://*.youtube.com/*"],
"exclude_matches": ["https://*.youtube.com/watch*"],
"run_at": "document_end"
}
],
"icons": {
"16": "youtube_icon.png",
"48": "youtube_icon.png",
"128": "youtube_icon.png"
},
"browser_action": {
"default_title": "Created by lovebuizel, enjoy!",
"default_popup": "popup.html"
}
}
```
新增popup.html
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<img src="./youtube_icon.png" alt="">
<p>Hello</p>
注意a連結一定要target="_blank"才有效
<a href="https://google.com" target="_blank">
<button>Visit homepage</button>
</a>
</body>
</html>
```
點擊extension時會顯示`popup.html`

滑鼠移過去時

youtube icon來源:https://www.youtube.com/intl/zh-TW/yt/about/brand-resources/#logos-icons-colors
# 使用storage儲存資料
permissions增加storage權限
```json=
{
"name": "my first extension",
"version": "1.0.0",
"description": "description",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"bookmarks",
"storage"
],
"content_scripts": [
{
"js": ["content.js"],
"matches": ["https://*.youtube.com/*"],
"exclude_matches": ["https://*.youtube.com/watch*"],
"run_at": "document_end"
}
],
"icons": {
"16": "youtube_icon.png",
"48": "youtube_icon.png",
"128": "youtube_icon.png"
},
"browser_action": {
"default_title": "Created by lovebuizel, enjoy!",
"default_popup": "popup.html"
}
}
```
chrome提供的storage api有兩種
* local storage
就是一般web所熟知的local storage,限定同個裝置
* sync storage
chrome如果有登入google帳號並且啟動同步功能的話,裝置不同也能存取之前的資料
以下範例使用`local storage`
新增checkbox讓使用者下次造訪時自動使用上次儲存的紀錄
```javascript=
// content.js
window.onload = () => {
const button = document.createElement('button')
button.id = "darkModeButton"
button.textContent = "DO IT DARK"
const input = document.createElement('input')
input.type = 'checkbox'
input.id = 'darkSetting'
document.querySelector('#end').prepend(button, input, 'Auto apply?')
button.addEventListener('click', () => enableDarkMode())
input.addEventListener('click', () => storeSetting())
checkSetting()
}
function checkSetting() {
chrome.storage.local.get(['enabled'], (result) => {
const isEnabled = result.enabled
console.log(isEnabled)
document.querySelector('#darkSetting').checked = isEnabled
if(isEnabled) {
enableDarkMode()
}
})
}
function storeSetting() {
const isEnabled = document.querySelector('#darkSetting').checked
const setting = { enabled: isEnabled }
// 使用sync storage的話則用chrome.storage.sync
chrome.storage.local.set(setting, () => {
console.log('stored', setting)
})
}
function enableDarkMode() {
document.querySelector('ytd-app').style.background = 'black'
}
```
# 上傳到chrome線上應用程式商店
https://chrome.google.com/webstore/category/extensions?hl=zh-TW
將node_modules資料夾、package.json、package-lock.json以外的檔案壓縮成zip檔(一定要zip檔才行,而不是rar等其他壓縮檔)上傳
發佈前必須繳一次性的註冊費(5塊美金),繳過一次後就不用再繳了,最多可以發佈20個商品。