---
# System prepended metadata

title: Chrome Extension

---

# 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/`

右上角開啟`開發人員模式`
點擊`載入未封裝項目`並選擇該專案資料夾
![](https://i.imgur.com/ARcce5m.jpg)

# 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/`點擊右下角的重新載入就好了
![](https://i.imgur.com/5z06Km5.jpg)
然後點擊背景頁面查看執行成果
![](https://i.imgur.com/SlCt8YW.jpg)


接著繼續新增
```javascript=
// extension載入完畢時執行
chrome.runtime.onInstalled.addListener(()=>{
  console.log('installed')
})

// 新增頁籤時執行
chrome.bookmarks.onCreated.addListener(()=>{
  alert('Bookmark saved')
})
```
然後同樣儲存後重新載入
![](https://i.imgur.com/NV8ZOvt.jpg)
當有錯誤時可以點擊錯誤查看問題
![](https://i.imgur.com/kvQ97WT.jpg)
這個錯誤是因為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的視窗
![](https://i.imgur.com/YQWBl1X.jpg)

# 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首頁
![](https://i.imgur.com/WatyhAQ.jpg)
會發現左邊多了一個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'
}
```
儲存並重新載入後
![](https://i.imgur.com/ZFfyTyl.jpg)
按下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`
![](https://i.imgur.com/aImJ7fE.jpg)

滑鼠移過去時
![](https://i.imgur.com/NHSeAU3.png)

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個商品。

















