あれこれ試行錯誤したので記録を残しておく。
やりたいこと
次の2つを同時に実現するには、少し工夫が要る。
.vue ファイルにはまとめたくない
.vue
に type="ts"
で埋め込んだ TypeScript のコードは、VS Code (Vetur) 上の補完や tsfmt によるコードの整形が(まだ)貧弱だったり効かなかったりするので、ロジックは .ts
に書きたい。
コンポーネントはクラスベースにしたい
コードの見通しがよくなる(+補完が効く)ので、コンポーネントは vue-class-component を使ってクラスベースで記述したい。
問題
<template>
セクションがないので、代わりのデータを用意し外から指定する必要がある。
方法1.べた書きする
@Component<MyComponent>({
template: '<div>hello</div>'
})
テスト目的ならともかく、論外である。
方法2.インラインテンプレートを使う
元の html
ファイルに埋め込む。
<script type="text/x-template" id="my-component">
...
</script>
@Component<MyComponent>({
template: '#my-component'
})
お手軽でよいのだが、webpacker 外のリソースになるので webpack-dev-server
によるライブリロードが効かないのが少し不便。
方法3.外部の html ファイルを読む
外部の .html
ファイルをうまく読む仕組みが必要となる(css に関しては割愛)。こちらの方法を説明する。
必要なもの
yarn add vue-class-component
yarn add vue-template-compiler-loader
webpacker の設定
config/webpack/custom.js
新しく作成する。名前はなんでもよいが custom.js
としている例が多いので倣う。
テンプレート文字列を外部から指定するには コンパイラを含むビルド版の Vue を読み込む必要があるが、.ts に直接 vue.esm.js
を import しても vue-class-component
が対応していないのでコンパイルが通らない。
import Vue from 'vue/dist/vue.esm.js' // 今回の例では通らない
そこで、custom.js
に次のようなエイリアスを登録する。
module.exports = {
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
}
config/webpack/loaders/html.js
.html
ファイルのローダを新しく作成する。名前はなんでもよいがひとまず html
とした。
module.exports = {
test: /\.html(\.erb)?$/,
use: [{
loader: 'vue-template-compiler-loader'
}]
}
config/webpack/environment.js
適当な場所にそれぞれ追記する。
custom.js
を読み、設定をマージする。
const customConfig = require('./custom')
environment.config.merge(customConfig)
html
ローダを追加する。
const html = require('./loaders/html')
environment.loaders.append('html', html)
メモ
config/webpacker.yml
の extensions
セクションに .html
を加える必要はないようだ。
アプリのコード
キモは require()
の型指定をしておくこと。
import Vue, { VNode } from 'vue'
declare function require(x: string): VueTemplateCompilerLoader
interface VueTemplateCompilerLoader {
render: () => VNode
staticRenderFns: Array<() => VNode>
}
VueTemplateCompilerLoader
型は私が適当に定義したオマケである。もし require()
で別のタイプの何かを読み込むのなら単純に any
で受けることになるだろう。
declare function require(x: string): any
実ファイルのパスを指定しデータを得る。
render
と staticRenderFns
をそれぞれ渡す。
const myComponent: VueTemplateCompilerLoader = require('./my_component.html')
@Component<MyComponent>({
render: myComponent.render,
staticRenderFns: myComponent.staticRenderFns
})
export default class MyComponent extends Vue {
...
}
以上でコンパイルが通るはずだ。