はじめに

Vue.js を使って Chrome 拡張機能を開発する方法について簡単に整理した。

前回の記事 では、Kocal/vue-web-extension を使った Chrome 拡張機能の開発方法について調べた。が、「Kocal/vue-web-extension では Popup Page や Options Page で Vue.js を使うもの」、つまり「メインページ(メインコンテンツ?)側では使えないもの」という結論に至った。(※自分の中では、の話)

冷静に考えて「メインページ側に使えないわけがない」と思って、どのようにすれば実現できるかについて調査した。

実際に拡張機能を作ってリリースした話 : Vue.js を使ってChrome 拡張機能を作った話(はてなブックマークのコメントを Qiita 記事内に表示) - Qiita

TL;DR

  • vue.config.js を用意して vue-cli-service でビルドすれば動作することを確認
  • manifest.json では chunk-vendors.js を先に読み込む
  • minify とか css preprocessor とかはまだ調べきれてない

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 第1段階
    2. 第2段階
    3. 第3段階
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G95

$ node -v
v12.7.0

$ npm -v
6.10.3

$ npm v vue
vue@2.6.10 | MIT | deps: none | versions: 250

$ npm v @vue/cli-service
@vue/cli-service@3.11.0 | MIT | deps: 56 | versions: 91

$ npm v vue-template-compiler
vue-template-compiler@2.6.10 | MIT | deps: 2 | versions: 104

詳細

どのように調査していったかを時系列に整理しているので、過程は不要という方はリポジトリを見てね。

リポジトリ: 17number/chrome-extension-with-vue-for-main-content

※Vue.js や JS(Node.js)についてプロフェッショナルなわけではないので、用語がおかしいかもだがご容赦を。

第1段階

第1段階として「そもそもメインページ側でも使えるよね?」を確認することとした。NPM は使わずに vue.jsCDN からダウンロードして、src/js/vue.js に配置。

1
$ curl https://cdn.jsdelivr.net/npm/vue/dist/vue.js -o src/js/vue.js

Vue.js で利用する HTML 要素(<div id="vue-app"></div>)を insertAdjacentHTML で追加し、データバインディングを試してみる。

src/manifest.json を作成、Permission とかは適当なので注意。動作対象ページは Google のページとした。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"manifest_version": 2,
"name": "extension with vue",
"version": "0.0.1",
"content_scripts": [
{
"matches": [
"https://www.google.co.jp/*"
],
"js": [
"js/vue.js",
"js/main.js"
]
}
],
"permissions": [
"activeTab",
"https://www.google.co.jp/*"
]
}

src/js/main.js を作成。「input 要素と name を紐付けて、div の中で表示させるだけ」の HTML を form 要素のところに追加する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function () {
'use strict';
document.querySelector('form').insertAdjacentHTML(
"beforeEnd",
`
<div id="vue-app">
<div>
Hello {{ name }} from vue
</div>
<input type="text" v-model="name">
</div>
`
);
let vm = new Vue({
el: "#vue-app",
data: {
name: "world"
}
});
})();

これで chrome://extensions にアクセスし、デベロッパーモードで拡張機能(src)を読み込ませる。

問題なく動作することを確認できた。

第2段階

最低限やりたいこと(メインページ側で Vue.js を使う)が実現できるのがわかった。しかし、

  • JS でクォート内に HTML をダラダラと書きたくないし、
  • コンポーネント分割とかもしたいし、
  • 普通に NPM packages を使いたい

ので、第2段階として調査した。

まずは npm init -y で初期化。

1
$ npm init -y

vue, @vue/cli-service, vue-template-compiler をインストール。

1
2
$ npm i vue
$ npm i -D @vue/cli-service vue-template-compiler

↑で vue をインストールしたので、src/js/vue.js は削除。

1
$ rm src/js/vue.js

src/js/main.js の処理を別ファイルに移植・分割していく。

まずは src/js/components/Hello.vue。表示や入力、入力内容の反映などはここに記述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="hello">
<div>
Hello {{ name }} from vue
</div>
<input type="text" v-model="name">
</div>
</template>

<script>
export default {
name: "Hello",
data() {
return {
name: "ワールド"
};
}
};
</script>

次に src/js/App.vuesrc/js/main.js から 呼び出され、src/js/components/Hello.vue を呼び出す。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div id="myapp">
<Hello />
</div>
</template>

<script>
import Hello from "./components/Hello.vue";

export default {
name: "app",
components: {
Hello
}
};
</script>

最後に、エントリポイントとなる src/js/main.js。インポート処理を追加して、vue に関する処理を変更。

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from "vue";
import App from "./App.vue";

(function () {
'use strict';
document.querySelector('form').insertAdjacentHTML(
"beforeEnd",
`<div id="vue-app"></div>`
);
let vm = new Vue({
render: h => h(App)
}).$mount("#vue-app");
})();

ソースコードは完成したので、その他の設定を行っていく。

ビルドは vue-cli-service で行うが、デフォルト設定だとファイル名にハッシュ値が付いたり、ソースマップが生成されたりする。どちらも不要なので vue.config.js を生成してコンフィグを追加。

1
2
3
4
module.exports = {
filenameHashing: false,
productionSourceMap: false,
};

ビルドファイルは dist/js/app.jsdist/js/chunk-vendors.js に出力されるので、src/manifest.json で読み込むファイルを修正。

1
2
3
4
5
6
      "js": [
- "js/vue.js",
- "js/main.js"
+ "js/chunk-vendors.js",
+ "js/app.js"
]

package.json を編集してビルド用のスクリプトを追加する。

  • npm run build: vue-cli-service でのビルドを実行
  • npm run release: ビルド、manifest.json のコピー、dist/index.html の削除を実行

※スクリプト名は各自でしっくりくるものに変更して欲しい。自分でもモヤモヤしている。

1
2
3
4
5
   "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "build": "vue-cli-service build src/js/main.js",
+ "release": "npm run build && cp src/manifest.json dist/ && rm dist/index.html"
},

スクリプトを作成したら、npm run release でビルドしてみる。成功すれば dist 配下にファイルが生成されている。

1
2
3
4
5
6
7
8
$ npm run release
...

$ ls -R dist
js/ manifest.json

dist/js:
app.js chunk-vendors.js

chrome://extensions にアクセスし、第1段階で作った古い拡張機能を削除して、dist の方を読み込ませて動作確認。画像は省略するが、同じように動作すれば OK。

第3段階

あとは Minify 関連について調べる必要がある。Chrome Store で公開しないオレオレ拡張機能なら良いと思うけど、vue-cli-service のデフォルト設定での Minify だとポリシー違反になるかもしれない。

参考: Google Chrome 拡張機能で利用可能な JavaScript の圧縮・難読化ツール

他にも

  • SCSS 使いたいとか
  • Pug 使いたいとか
  • TypeScript 使いたいとか
  • vue-cli から上手くやりたいとか

色々とあると思うので、気が向いたら調べて書くと思う。

まとめ

  • vue.config.js を用意して vue-cli-service でビルドすれば動作することを確認
  • manifest.json では chunk-vendors.js を先に読み込む
  • minify とか css preprocessor とかはまだ調べきれてない

その他・メモ

参考文献

関連記事