Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
142
Help us understand the problem. What is going on with this article?
@hatakoya

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

More than 3 years have 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でコンポーネントが作れた。ウレシス!

142
Help us understand the problem. What is going on with this article?
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
hatakoya
TypeScriptが好きすぎる TypeScript/React/Kotlin

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
142
Help us understand the problem. What is going on with this article?