LoginSignup
5
0

More than 5 years have passed since last update.

Rails 5: webpacker 3: vue 2: typescript: Vue コンポーネントを .vue ファイルにまとめずにクラスベースにする

Last updated at Posted at 2018-05-24

あれこれ試行錯誤したので記録を残しておく。

やりたいこと

次の2つを同時に実現するには、少し工夫が要る。

.vue ファイルにはまとめたくない

.vuetype="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.ymlextensions セクションに .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

実ファイルのパスを指定しデータを得る。
renderstaticRenderFns をそれぞれ渡す。

const myComponent: VueTemplateCompilerLoader = require('./my_component.html')

@Component<MyComponent>({
  render: myComponent.render,
  staticRenderFns: myComponent.staticRenderFns
})

export default class MyComponent extends Vue {
  ...
}

以上でコンパイルが通るはずだ。

See Also

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0