フロントエンドについて学びはじめた頃なのでTypeScriptはv1.4、Vue.jsはv0.11とかそんな時代。当時、相当苦労した思い出がある。
今では、Vue.jsも公式で型定義を提供している。TypeScriptのサポートも手厚くなり、型のある環境での開発が楽になっているらしいが……。
- gulp書きたくない
- webpack触りたくない
- *-loaderの設定したくない
- その他細かな設定をしたくない
だから、とにかく楽したい!!!
かといって、いわゆるget startedのような最小構成にはしたくない。
ということで、vue-cliのwebpackテンプレートをベースに、とにかく楽してVue.jsプロジェクトでTypeScriptを使えるようにする。
環境は以下のとおり。
- Mac OSX
- npm v5.3.0
- vue-cli v2.8.2
- vue.js v2.4.2
- typescript v2.4.2
vue2.5以降でvue-class-componentのデコレータを使わない方法は、以下の記事を参照ください。
vue-cliを導入する
vue-cliとは、コマンド1つでScaffolding(雛形生成)してくれるcliツールだ。
まずはvue-cliをglobalにインストールする。
$ npm i -g vue-cli
詳細は、以下の記事を参照してほしい。
テンプレートを使ってプロジェクトディレクトリを作成する
vue-cliは、公式でいくつかのテンプレートを公開している。
今回は一番メジャーなwebpackのテンプレートをベースにする。
$ vue init webpack vue-ts-project
# ビルドに必要なライブラリをインストールする
$ cd vue-ts-project
$ npm install
これでScaffoldingは終了!
このままnpm run buildをすればローカルwebサーバが起動してVue.jsのサンプルページを見ることができる。
Commit: vue-cli init webpackで雛形をつくる · BcRikko/vuejs-typescript@a64449d · GitHub
TypeScriptでかけるように設定する
webpackのテンプレートをベースに、必要なモノだけを追加してTypeScriptを使えるようにする。
typescript, ts-loader, vue-class-componentをインストールする
$ npm i -D typescript ts-loader vue-class-component
typescriptとts-loaderはTypeScriptをビルドする用。
vue-class-componentはクラススタイルのVueコンポーネントにする用。(使い方は後述する)
Commit: typescript, ts-loader, vue-class-componentをインストールする · BcRikko/vuejs-typescript@9fe88b0 · GitHub
tsconfig.jsonを追加し、webpackでビルドできるように修正する
次に、TypeScriptをビルドするための設定をする。まずはtsconfig.jsonから。
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"module": "es2015",
"moduleResolution": "node",
"noImplicitAny": true,
"target": "es6",
"lib": ["dom", "es6", "es2015.promise"]
},
"include": [
"./src/**/*.ts"
]
}
target
はes6(es2015)にしておく。allowSyntheticDefaultImports: true
で、import Vue from 'vue'
という記述ができるようになる。Vue.jsでは、型宣言をESモジュール形式で提供する予定なので、
require(...)
ではなくimport ... from ...
が推奨されている。Commit: tsconfig.jsonを追加する · BcRikko/vuejs-typescript@2c0f83e · GitHub
次にwebpackでTypeScriptをビルドできるようにするため、
build/webpack.base.conf.js
の一部を変更する。// build/webpack.base.conf.js
module.exports = {
entry: {
// エントリポイントの拡張子を.js→.tsに変更
app: './src/main.ts'
},
output: {
path: config.build.assetsRoot
// ----- 略 -----
},
resolve: {
// extensionsに'.ts'を追加
extensions: ['.js', '.vue', '.json', '.ts'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
// ----- 略 -----
},
// ↓追加
{
test: /\.ts$/,
loader: 'ts-loader',
include: [resolve('src'), resolve('test')],
options: {
appendTsSuffixTo: [/\.vue$/]
}
},
// ↑追加
{
test: /\.js$/,
loader: 'babel-loader',
Commit: webpackでtsを読み込めるように修正する · BcRikko/vuejs-typescript@1de599e · GitHub
TypeScriptで書くために拡張子などを変更する
ここまででTypeScriptをビルドする環境はできた。次は、TypeScriptで書けるように拡張子などを変更する。
コマンドじゃなくても良いので、srcディレクトリ配下の.jsファイルを.tsに変更する。
$ mv ./src/main.js ./src/main.ts
$ mv ./src/router/index.js ./src/router/index.ts
Vueファイルでの使用言語を指定する。
src/App.vue
とsrc/components/Hello.vue
のscriptタグにlang="ts"
を追加する。<template>
<!-- 略 -->
</template>
<script lang="ts">
export default {
// 略
}
</script>
Commit: tsで書けるように拡張子等を変更する · BcRikko/vuejs-typescript@0394620 · GitHub
しかし、このままではvueファイル をimportするときにCannot find module "./App"のようなエラーが出てしまうので、vueファイルの型定義(
src/sfc.d.ts
)を作成する。// src/sfc.d.ts
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}
Commit: vueファイルを読み込めるように定義を追加する · BcRikko/vuejs-typescript@ebf8165 · GitHub
続いて、import部分を修正する。
// src/main.ts
import Vue from 'vue'
// 拡張子をつける ./App → ./App.vue
import App from './App.vue'
import router from './router'
// 略
// src/router/index.ts
import Vue from 'vue'
import Router from 'vue-router'
// 拡張子をつける @/components/Hello → @/components/Hello.vue
import Hello from '@/components/Hello.vue'
Vue.use(Router)
// 略
Commit: vueファイルが`Cannot find module`になるので読み込めるように拡張子を指定する · BcRikko/vuejs-typescript@a84903b · GitHub
vue-class-componentを使ってTypeScriptで開発する
準備は整ったので、あとはvue-class-componetを使って開発していくだけだ。ためしにサンプルに手を加える。
まずは
src/App.vue
から。<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({
name: 'app'
})
export default class App extends Vue {
}
</script>
次に、
src/components/Hello.vue
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({
name: 'hello'
})
export default class Hello extends Vue {
msg = 'Welcome to Your Vue.js App'
}
</script>
最後にビルドして、ちゃんと表示されるか確認する。
$ npm run dev
Commit: vue-class-componentでクラススタイルのVueコンポーネントに変更する · BcRikko/vuejs-typescript@03d1f6a · GitHub
おまけ: もうちょっとvue-class-componentを触ってみる
src/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view propsMessage="vue-router"></router-view>
<hello
propsMessage="hello tag"
clickEvent="clicked"
@clicked="showChildMessage">
</hello>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import Hello from './components/Hello.vue'
@Component({
name: 'app',
components: { Hello }
})
export default class App extends Vue {
showChildMessage (val: string) {
alert(val + ' from child')
}
}
</script>
src/components/Hello.vue
<template>
<div class="hello">
<h1>{{ propsMessage }}</h1>
<input type="text" v-model="input">
<h2>{{ input }}</h2>
<button @click="onClick">onClick</button>
<ul>
<li v-for="item in filteredList" v-bind:key="item">{{ item }}</li>
</ul>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({
name: 'hello',
props: {
propsMessage: String,
clickEvent: String
}
})
export default class Hello extends Vue {
// local state
msg = 'Welcome to Your Vue.js App'
input = 'a'
list:string[] = ['apple', 'apricot', 'avocado', 'banana', 'bilberry', 'blackberry', 'blackcurrant', 'blueberry', 'boysenberry']
// props
propsMessage: string
clickEvent: string
// mounted
mounted () {
console.log('mounted')
}
// computed
get filteredList () {
return this.list.filter(a => a.indexOf(this.input) > -1)
}
// methods
onClick () {
window.alert('clicked ' + this.propsMessage)
this.$emit(this.clickEvent, this.input)
}
}
</script>
Commit: コンポーネントをいろいろいじってみる · BcRikko/vuejs-typescript@0d69480 · GitHubpropsは、@Componentとメンバ変数に定義。
dataは、クラスのプロパティとして定義。
computedは、getterとして定義。
methodsは、メンバメソッドとして定義。
その他のオプション(mouted, createdなどはそのままの名前で定義。
だいたいこんな感じで開発できる。
VuexのstoreもTypeScriptで書きたいので、後日記事を書こうと思う。
残る闇部分はいくつかある
- 型定義の闇
- eslint → tslintへの移行
- vueファイル内のtslintが正常に動作しない
- npm run buildするとUglifyでエラーになる
- などなど…
いずれ解決したらブログに書こうと思う。
以上
written by @bc_rikko
0 件のコメント :
コメントを投稿