GoogleAppScriptでVue.jsが使えると知ったので使ってみた。
しかし、そのままではVue.jsの単一ファイルコンポーネント(single-file component)は使えず、一つのhtml内にすべて記述する必要がある。
Vue.jsを使っておきながら単一ファイルコンポーネントが使えないとなると少しもったいない。そこで、なんとかgasで単一ファイルコンポーネントが使えるよう模索したのでここに示す。
※ ある程度のgasとvueの知識が必要
プロジェクト構成
gasプロジェクトは以下の構成とする
.
├── main.gs
├── index.html
└── app.vue
htmlを取得できるようにする
ページにアクセスできるようにdoGetメソッドを作る。
今回はデモなので細かい設定は省略しindex.htmlをそのまま返すようにする。
function doGet() {
return htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
}
AppScriptエディタ右上の"デプロイ"から、ウェブアプリとしてデプロイしておく。
ここまでは普通の gas + html
GoogleAppScriptにVue.jsを導入する
index.htmlにvue導入処理を記載する。
cdn版Vue.jsは一行で導入できる!すごい!
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
</div>
</body>
<script>
new Vue({
el: '#app',
data () { return { message: 'Hello GAS + Vue.js' } }
});
</script>
</html>
この状態でデプロイ先リンクを開くと"Hello GAS + Vue.js"と出力される。
ここまでは普通の gas + vue
単一ファイルコンポーネントを読み込む
さて本題の単一ファイルコンポーネントの読み込みだ。
事前にapp.vue.htmlを単一ファイルコンポーネントの形式にしておく。
ここでは引数messageを受け取って表示するだけのコンポーネントとする。
<template>
<h1>
{{message}}
</h1>
</template>
<script>
module.exports = {
props: ['message']
}
</script>
index.htmlから単一ファイルコンポーネントを読み込むためにhttp-vue-loaderを導入する。
http-vue-loaderは単一ファイルコンポーネントのURIを受け取ってコンポーネントを生成するため、これを使って別ファイルに書き出したコンポーネントを読み込む。
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
<script src="https://unpkg.com/http-vue-loader"></script> <!-- 追加 -->
</head>
<body>
<div id="app">
<App :message = "message"></App>
</div>
</body>
<script>
// 追加
const App = httpVueLoader(
'data:text/plain,' +
'<?!= encodeURIComponent(HtmlService.createTemplateFromFile('app.vue').evaluate().getContent()).replace(/[\'']/g,"\\'") ?>'
);
new Vue({
el: '#app',
data () { return { message: 'Hello GAS + Vue.js + single-file component' } },
components:{ // 追加
App
}
});
</script>
</html>
ミソは以下の部分
const App = httpVueLoader(
'data:text/plain' +
'<?!= encodeURIComponent(HtmlService.createTemplateFromFile('app.vue').evaluate().getContent()).replace(/[\'']/g,"\\'") ?>'
);
app.vue.htmlの中身を文字列として取得
HtmlService.createTemplateFromFile('app.vue').evaluate().getContent()
取得した文字列をURIにエンコード
encodeURIComponent(...)
エンコードした文字列の中のシングルクオートをエスケープ
....replace(/[\'']/g,"\\'")
<?!= ~ ?>
の中の文字列はgasのスクリプトとして処理されたのち埋め込まれるため、
const App = httpVueLoader('data:text/plain,{app.vue.htmlの中身}')
のようになり、URIをhttpVueLoaderで読み込んでコンポーネントオブジェクトが生成できる。
読み込んだコンポーネントはvueのコンポーネントとして普通に使うことができる。
この状態でデプロイページにアクセスすると、"Hello GAS + Vue.js + single-file component"と表示されるはずだ。
これで、単一ファイルコンポーネントをgasで扱うことができるようになった。
Gasに変換処理を任せる
const SomeComponent = httpVueLoader(
'data:text/plain' +
'<?!= encodeURIComponent(HtmlService.createTemplateFromFile('some_component.vue').evaluate().getContent()).replace(/[\'']/g,"\\'") ?>'
);
単一ファイルコンポーネントが利用できるようになったのはいいが、コンポーネントを読み込むたびにこんな長文を書いてられないので、Gas側でできるところまでメソッド化する。
function doGet() {
return htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
}
// 追加
/** @type { (name:string) => string } */
function getVueURI(name) {
return "'data:text/plain," + encodeURIComponent(HtmlService.createTemplateFromFile(name).evaluate().getContent()).replace(/[\'']/g,"\\'") + "'"
}
ファイル名からURIを構成して返却するメソッドgetVueURIを作る。やっていることは先ほどと同じ。
それに合わせてindex.htmlを書き換えると以下のようになる。
httpVueLoaderだけはクライアント側で実行しないといけないため、このような構成となった。
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
</head>
<body>
<div id="app">
<App :message = "message"></App>
</div>
</body>
<script>
// 変更
const App = httpVueLoader(<?!=getVueURI('app.vue')?>)
new Vue({
el: '#app',
data () { return { message: 'Hello GAS + Vue.js' } },
components:{
App
}
});
</script>
</html>
おわりに
これでGoogleAppScriptでvueの単一ファイルコンポーネントが使えるようになった。
あとはvuexでもvuetifyでも使って好きに開発できる!
追記:著者はgas/vueともにあまり詳しくはないので、ミスやほかの方法があればコメントにて教えてください。