To-Do ListのChrome拡張機能をVue.jsで作る

#### Chromeの拡張機能でいい感じのTo-Do Listが欲しいと思い、ちょうどバイト先でもVueを使っていたのでVueを使い作ってみました。
(Vue.jsはバージョン2.6.14です。あと, 他の記事を見たときにbuildがめんどくさいと書いてあったのですがその辺りはpythonで実行しています。buildしたときにファイルを追加しなきゃならないのがめんどくさい)

# 是非ダウンロードして使ってみて下さい!!!

https://chrome.google.com/webstore/detail/to-do-list/chaiaabmmjdihkcjlpmbadcdmefaljib

ソースコード↓

https://github.com/07130918/ChromeExtentions


### 完成イメージ
![Screen Shot 2022-02-27 at 10.25.50.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684396/39edfbe3-34b7-01bd-be40-9ed65883d5e7.jpeg)
![Screen Shot 2022-02-27 at 10.26.45.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684396/3dc0bc3e-dda2-f2c7-7621-407ec65e9e03.jpeg)　　　![Screen Shot 2022-02-27 at 10.26.59.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684396/6db6bff9-f4b0-fa03-8c0a-ca827bef2f6e.jpeg)


## 機能
- リストへの書き込み
- やったことをチェックできる
- リストのリセット
- リストの一括リセット
- ダークモード
- リストのドラッグ


# 手順
1. Chorme拡張機能の申請に必須のファイル(manifest.json)を作成
2. 作りたいものをVue-cliで作成
3. 申請し審査を通す


### 1.Chorme拡張機能の申請に必須のファイル(manifest.json)を作成
申請、開発途中の確認(chrome://extensions)をする際に、`npm run build`で作った`dist`ディレクトリをアップロードするのですがその際に`manifest.json`と必要な画像ファイルが`dist`に入らないのでpythonで`build+必須ファイルのセッティング`を行ってくれるようコードしました。
```python: extention_build.py
# make manifest.json and set icon to dist/icons
import os
import json
import shutil


def main():
    os.system('npm run build')
    write_manifest()
    set_icons()


def write_manifest():
    content = {
        "name": "To Do List",
        "description": "A simple to-do list extension, You can also write your own not-to-do list",
        "manifest_version": 3,
        "version": "1.0.4",
        "permissions": ["activeTab", "storage"],
        "icons": {
            "16": "icons/icon16.png",
            "48": "icons/icon48.png",
            "128": "icons/icon128.png",
        },
        "action": {
            "default_icon": "icons/icon128.png",
            "default_popup": "index.html"
        }
    }

    os.system('touch ./dist/manifest.json')
    with open('./dist/manifest.json', 'w') as f:
        json.dump(content, f, ensure_ascii=False, indent=4, separators=(',', ': '))


def set_icons():
    os.system('mkdir dist/icons')
    for icon in ['icon16.png', 'icon48.png', 'icon128.png']:
        shutil.copy(f'./src/assets/icons/{icon}', './dist/icons/')


if __name__ == '__main__':
    main()
```
manifest.jsonの中身自体は`content`という変数で定義しています。
##### なのでこれを`manifest.json`だと思って下さい。
pythonの環境がある方はバージョンにもよりますがファイル名等を揃えておけばこれで動くと思います。(Python 3.8.8)

`permissions`自体は何種類かあるのですが、使わない機能を`permissions`に入れて申請すると審査落ちするので気をつけて下さい。
また`icons`と`default_icon`は必ず正方形に整形しておく必要があります。
そうしないと適切に描画されなかったりそもそも拡張機能を開けなくなってしまいます。
画像整形には[この辺り](https://www.peko-step.com/tool/resize.html)のサービスが使えると思います。

今回は、`To-Do Listの中身をchromeの方で保管しておいて欲しい`、`そもそのこの拡張機能を表示するのにHTMLのポップアップを許して欲しい`という2つの要求があるので`"permissions": ["activeTab", "storage"]`としています。


### 2. とりあえず作りたいものをVue-cliであらかた作ってみる
このあたりは本当に人によりけりだと思います。
その際に[この記事](https://original-game.com/how-to-make-chrome-extensions/)がかなり参考になりました。
見本も3つあるのでだいたいどんなことをしなければいけないのかのイメージが付きます。

以下自分の場合
```:ディレクトリ構成
.
├── README.md
├── babel.config.js
├── extensiton_build.py
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   │   ├── icons
│   │   │   ├── icon.png
│   │   │   ├── icon128.png
│   │   │   ├── icon16.png
│   │   │   └── icon48.png
│   │   ├── logo.png
│   │   └── scss
│   │       ├── dark-mode.scss
│   │       └── style.scss
│   ├── components
│   │   ├── NotToDoList.vue
│   │   ├── Preferences.vue
│   │   └── ToDoList.vue
│   ├── main.js
│   └── mixinFunctions.js
└── vue.config.js
```

コンポーネントとして`ToDoList.vue`、`NotToDoList.vue`、設定をまとめた`Preferences.vue`作りました。これらを`App.vue`で`<component>`タグを使いそれぞれ描画できるようにしています。

一応、`storage`を使う際にデータの保存と取り出し方は決まっていてなおかつ各コンポーネントで共通しているため`mixin`するようしました。
```javascript:mixinFunctions.js
export default {
    methods: {
        getFromChromeStorage(objName, listLength = null) {
            chrome.storage.local.get(objName, item => {
                if (listLength === null) {
                    this[objName] = item[objName] ? true : false;
                } else {
                    this[objName] = item[objName] ? item[objName] : this.createList(objName, listLength);
                }
            });
        },
        createList(objName, listLength) {
            const generatedList = [];
            const uniqueIds = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta'];
            for (let i = 0; i < listLength; i++) {
                generatedList.push({ id: uniqueIds[i], isChecked: false, content: '' });
            }
            this.setToChromeStorage(objName, generatedList);
            this[objName] = generatedList;
            return this[objName];
        },
        setToChromeStorage(objName, object) {
            chrome.storage.local.set({ [objName]: object }, function() {
                console.log(`${objName} saved`);
            });
        },
    },
}
```
toDoListとnotToDoListはどちらも`{ id: uniqueIds[i], isChecked: false, content: '' }`というオブジェクトの配列にしています。
なぜ、`uniqueIds`を設定しているのかというと[Vue.Draggable](https://github.com/SortableJS/Vue.Draggable)という要素をドラッグできるようにするライブラリを使っていて、ドラッグ後の順番を保証するために必要でした。
(もっと良い方法がある気がします。。。)

storageからの取得は`chrome.storage.local.get`、格納は`chrome.storage.local.set`で行います。
setの際、keyを動的に変更するには`{ [objName]: object }`とするそうです。
こうすることでそれぞれのコンポーネントの`data`を動的に受け入れさせます。

その後も見た目の調整等を行い、完成しました。


### 3. googleに申請する
作りたいものはできたので、申請します。
まず、初回にデベロッパーとして登録するのに5ドルかかります。
その後`npm run build`で`dist`フォルダを作りそれを`.zip`にして拡張機能ダッシュボードの`新しいアイテム`にアップロードし、結果を待ちます。

審査結果はだいたい2日位で返ってきました。