181
142

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js #4Advent Calendar 2017

Day 24

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

Last updated at Posted at 2017-12-23

#こんな人向け
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でコンポーネントが作れた。ウレシス!

181
142
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
181
142

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?