Help us understand the problem. What is going on with this article?

TypeScriptではじめるVueコンポーネント(vue-class-component)

More than 1 year has passed since last update.

こんな人向け

Vue.jsを使っているうちに以下のような考えになった人

  • TypeScriptでコンポーネントを書きたい
  • .vueファイルに抵抗がある(フォーマッタ、補完がそのままじゃ効かないし、色々設定するのは疲れる)
  • scoped cssはなくても大丈夫

前提知識

  • Vue.js、TypeScriptがなんとなくわかる

やること

  • コンポーネントをTypeScriptで書く。
  • (ついでに)テンプレート部分はhtmlファイルに分ける。

使うもの

  1. vue
  2. vue-class-component
  3. vue-property-decorator
  4. webpack

vue-class-componentってなに?

TypeScript(.tsファイル)でコンポーネントが書けるようになる。

Componentデコレータをつけて、Vueを継承したクラスとして書く。
例えば、空のコンポーネントはこう。

hello.ts
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class MyComponent extends Vue{

}

書き方

  • data
    クラスの変数として書く

  • 算出プロパティ
    getterとして書く

  • メソッド
    普通にメソッドとして書く

  • ライフサイクルフック
    名前を合わせてメソッドとして書く

詳しくはvue-class-componentを見れば大体わかる感じ。

vue-property-decoratorってなに?

vue-class-componentではpropsなどはComponentデコレータの中に書いていく。

hello.ts
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  props: {
    hoge: String
  }
})
class MyComponent extends Vue {

}

vue-property-decoratorを使うと、デコレータを使って書ける
クラスメンバとして書けるので、this.って打つと補完にでる(※嬉しい)

hello.ts
import Vue from 'vue'
import Component from 'vue-class-component'
import { Prop } from 'vue-property-decorator';

@Component
class MyComponent extends Vue {

  @Prop({ type: String })
  hoge: string

}

$emitも、デコレータを使うとこんなふうに書ける。
クラスメソッドとして書けるので、メソッド名を文字列で書かなくていい(※嬉しい)

hello.ts
import Vue from 'vue'
import Component from 'vue-class-component'
import { Emit } from 'vue-property-decorator';

@Component
class MyComponent extends Vue {

  @Emit()
  change(newValue: string) { }

  selectItem(val: string) {
    this.change(val) // this.$emit('change', val) って書かなくていい
  }
}

他にも使えるデコレータが用意されているので
詳しくはvue-property-decoratorを見れば大体わかる感じ。

とりあえずコンポーネント作ってみよう

実際に作ってみる。
違いはないかもしれないけどVSCodeを使う前提。

環境構築

適当にフォルダ作って、必要なものを入れる

npm init
npm install -S vue
npm install -S vue-class-component
npm install -S vue-property-decorator
npm install -D typescript
npm install -D webpack
npm install -D ts-loader
npm install -D html-loader
npm install -D @types/node

package.jsonの設定

webpackでビルドするための行を追記する。

package.json
{
  // ...省略
  "scripts": {
    "build": "webpack"
  },
  // ...省略
}

tsconfig.jsonをつくって、追記する。

・importしたときに補完が効くようにする
・デコレータを使えるようにする

tsc --init

して、以下のとおり追記する。

tsconfig.json
{
  "compilerOptions": {
    // ...省略
    "baseUrl": ".", // pathsを設定するときはbaseUrlの設定が必要
    "paths": {
      "vue": [
        "/node_modules/vue/types/index"
      ],
      "vue-class-component": [
        "/node_modules/vue-class-component/lib/index"
      ],
      "vue-property-decorator": [
        "/node_modules/vue-property-decorator/lib/vue-property-decorator"
      ]
    },
    "experimentalDecorators": true, // デコレータを使うために必要
    // ...省略
  }
}

webpack.configをつくる

html-loaderの設定をしているのは、後ででてきますがテンプレート部分を別ファイルにするため。

webpack.config.js
module.exports = {
  entry: __dirname + "/main.ts",
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { test: /\.ts$/, loader: 'ts-loader' },
      { test: /\.html$/, loader: 'html-loader?minimize=false' },
    ]
  },
  resolve: {
    extensions: ['.ts'],
    modules: [
      "node_modules"
    ],
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // コンポーネントを使うときはこれを書く必要があるみたい
    }
  }
};

コンポーネントのファイルをつくる

クリックしたら”こんにちは”が"さようなら"に変わる。それだけ。

MyComponent.ts
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  template: require('./MyComponent.html') // html-loaderを使うと外部のhtmlファイルを読み込める
})
export default class MyComponent extends Vue {
  // data
  hoge : string = 'こんにちは'

  // methods
  onClick(){
    this.hoge = 'さようなら'
  }

}
MyComponent.html
<div>
  <p @click="onClick">{{hoge}}</p>
</div>

コンポーネントの利用側をつくる

さっき作ったMyComponentを単に読み込んでいるだけ

main.ts
import Vue from 'vue'
import MyComponent from './MyComponent'

window.onload = () => {
  new Vue({
    el: '#app',
    components: {
      MyComponent
    }
  })
}
main.html
<html>

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<script src="dist/bundle.js"></script>

<body>
  <div id="app">
    <my-component></my-component>
  </div>
</body>

</html>

ビルドして動作確認

npm run build

main.htmlを開いてちゃんと動いたらOK

おまけ

vue-class-componentを使う時に知っておくと嬉しいこと

継承

各コンポーネントに共通処理があるとき、継承を使ってまとめられる。
例えば、mountedのタイミングでコンソールに"mounted!!!"と表示する処理を共通化する場合は以下のように書く。

ComponentBase.ts
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class ComponentBase extends Vue {

  mounted(){
    console.log('mouted!!!')
  }

}
MyComponent.ts
import Vue from 'vue'
import Component from 'vue-class-component'
import ComponentBase from './ComponentBase';

@Component({
  template: require('./MyComponent.html')
})
export default class MyComponent extends ComponentBase {
  // data
  hoge : string = 'こんにちは'

  // methods
  onClick(){
    this.hoge = 'さようなら'
  }

}

まとめ

TypeScriptでコンポーネントが作れた。ウレシス!

hatakoya
TypeScriptが好きすぎる TypeScript/React/Kotlin
https://memo.koya-it.com/
topgate
Google技術を中心に取り扱う技術者集団
https://www.topgate.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした