VueJS+TypeScript の開発環境を作ってみた
Vue.js を始めました。TypeScript の開発環境を作ってみました。
はじめに
これは自力でやる場合のサンプル程度に考えてください。
もうしばらくすると、公式の webpack テンプレートが TypeScript に対応しそうです。
Ref. TypeScript template? · Issue #263 · vuejs/vue-cli
セットアップ
まっさらから自分で必要なライブラリ−やツールを入れていくこともできますが、Vue ではプロジェクトテンプレートが用意されているので、それを使うことにします。
プロジェクトテンプレート: vuejs-templates
これからは PWA が良いのかもしれませんが、まだ調べきれていないので、今回は定番の webpack を選びました。
webpack のページの手順に従ってインストールします:
$ npm install -g vue-cli $ vue init webpack <my-project>
この後、いろいろと質問されますが、わからなければ、とりあえずデフォルトで良いと思います。
生成されたら、依存パッケージをインストールして、実行してみます:
$ cd <my-project> $ npm install $ npm run dev
なお、この状態では、TypeScript は使えません。
一応、Productionビルドも試してみます:
$ npm run build
NodeJSの簡易HTTPサーバーで動かしてみます(入ってなければ npm i -g http-server
):
$ cd dist $ http-server -o
ユニットテストも実行してみます:
$ npm run unit
(テストが失敗すると、npmパッケージのエラーになるんですね。ちょっと焦りました。)
TypeScript のセットアップ
事前に以下の公式ページに目を通しておいた方が良いと思います:
まずは、TypeScript 本体と、Webpackでコンパイルするために ts-loader をインストールします:
$ npm i -D typescript ts-loader
tsconfig.json
を生成します:
$ ./node_modules/.bin/tsc --init
公式ページの「TypeScript のサポート - Vue.js」に従い、tsconfig.json
の設定を変更します:
// tsconfig.json { "compilerOptions": { "allowSyntheticDefaultImports": true, "lib": [ "dom", "es5", "es2015.promise" ], "module": "es2015", "moduleResolution": "node" ..... } }
この状態だとまだエラーが出るので修正します。
TypeScript 用の修正
*.ts
ファイルが認識されるようにする
各 *.js
ファイルの拡張子を ts
に変更したら、それが Webpack で認識されるように build/webpack.base.conf.js
の設定を変更します:
@@ -9,7 +9,7 @@ function resolve (dir) { module.exports = { entry: { - app: './src/main.js' + app: './src/main.ts' }, output: { path: config.build.assetsRoot, @@ -19,7 +19,7 @@ module.exports = { : config.dev.assetsPublicPath }, resolve: { - extensions: ['.js', '.vue', '.json'], + extensions: ['.ts', '.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src') @@ -47,6 +47,15 @@ module.exports = { include: [resolve('src'), resolve('test')] }, { + test: /\.ts$/, + loader: 'ts-loader', + include: [resolve('src'), resolve('test')], + options: { + /* Make ts-loader recognize TypeScript code in *.vue files */ + appendTsSuffixTo: [/\.vue$/] + } + }, + { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: {
ts-loader の appendTsSuffixTo
の設定は、単一ファイルコンポーネント内の TypeScript コードが認識されるようにするために必要となります。
@
の解決
一部の import に @
が使用されています:
import Hello from '@/components/Hello'
これは Webpack のエイリアスの機能で、./src
を指すように定義されています:
alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src') }
TypeScript は Webpack の定義を認識しないため、tsconfig.json
でも定義する必要があります:
@@ -37,8 +37,10 @@ /* Module Resolution Options */ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "@": ["src"] + }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */
なお、paths
を指定する場合、baseUrl
の指定も必要になります。
Ref. Webpack resolve.alias does not work with typescript? - Stack Overflow
*.vue
ファイルの import の解決
同じく、import Hello from '@/components/Hello'
の箇所ですが、これは Hello.vue
ファイルをインポートしようとしていますが、TypeScript ではファイルが認識できないため、エラーになります。
これを解決するためには、src/types/index.d.ts
などに型定義を置く必要があります:
declare module "*.vue" { import Vue from 'vue' export default typeof Vue }
これで、import Hello from '@/components/Hello.vue'
とやると、インポートできるようになります。
追記(2017-08-12)
src/types
にある型定義ファイルを認識させるには、tsconfig.json
に設定が必要でした:
"paths": { ..... "*": [ "src/types/*" ] },
追記終了
Ref.
- Cannot import from a typescript Vue component into another typescript Vue component · Issue #5298 · vuejs/vue
- sfc.d.ts
型指定
各 *.ts
ファイルで型の不一致が出るので修正します:
src/main.ts
:
@@ -1,7 +1,7 @@ // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. -import Vue from 'vue' -import App from './App' +import Vue, { ComponentOptions } from 'vue' +import App from './App.vue' import router from './router' Vue.config.productionTip = false @@ -12,4 +12,4 @@ new Vue({ router, template: '<App/>', components: { App } -}) +} as ComponentOptions<Vue>)
src/router/index.ts
:
@@ -1,6 +1,6 @@ import Vue from 'vue' -import Router from 'vue-router' -import Hello from '@/components/Hello' +import Router, { RouteConfig } from 'vue-router' +import Hello from '@/components/Hello.vue' Vue.use(Router) @@ -10,6 +10,6 @@ export default new Router({ path: '/', name: 'Hello', component: Hello - } + } as RouteConfig ] })
*.vue
ファイルの修正
*.vue
ファイルについては、とりあえず、<script lang="ts">
に変更して問題なく動きました。
TSLint の追加(2017-08-07 追記)
$ npm i -D tslint tslint-loader $ ./node_modules/.bin/tslint --init
webpack.base.conf.js
に設定を追加、変更:
..... module: { rules: [ { test: /\.js$/, loader: 'eslint-loader', ..... }, { test: /\.ts$/, loader: 'tslint-loader', enforce: 'pre', include: [resolve('src'), resolve('test')], exclude: /(node_modules)/ }, .....
vue-loader.conf.js
の設定を変更:
module.exports = { loaders: Object.assign({}, // 追加 utils.cssLoaders({ sourceMap: isProduction ? config.build.productionSourceMap : config.dev.cssSourceMap, extract: isProduction }), { ts: 'ts-loader!tslint-loader' } // 追加 ), transformToRequire: { video: 'src', source: 'src', img: 'src', image: 'xlink:href' } }
*.vue
を vue-loader
側で処理させるようにします。
あと、VS Code で TSLint を有効にするには、TSLintプラグイン をインストールする必要があります。
なお、Vue で TSLint をかけると、うまく修正できない箇所が出てくるので、ルールを変更する必要があります:
"rules": { "no-consecutive-blank-lines": [ false ], "no-unused-expression": [ true, "allow-new" ], "ordered-imports": [ false ] },
no-consecutive-blank-lines
: *.vue
は <script>
の内部以外の部分が空行として残るようなので、オフにしました。
no-unused-expression
: new Vue(...)
のような記述を許容するためです。
ordered-imports
: これは好みですが、imports
をアルファベット順にしないと怒られるのでオフにしました。
Ref. Support linting vue/html file · Issue #2099 · palantir/tslint
VS Code
最後に VS Code の設定です。
プロジェクトを VS Code で開き、*.vue
ファイルを表示するとシンタックスハイライトが効かず、拡張機能の Vetur が推奨されるのでインストールします。
Vetur は vuejs で提供されているので公式プラグインなんですね。
再度 *.vue
ファイルを開くと、シンタックスハイライトが効くようになりました。
あとがき
まとまった情報がなく結構悩みましたが、わかってみれば、それほど面倒ではないですね。
TypeScript や Webpack の設定について少し理解が深まったので、その点ではためになりました。
おまけ1:npmパッケージのアップデート(失敗)
npmパッケージが古くなっている場合があるのでアップデートを試みてみました。
バージョン確認:
$ npm outdated
メジャーバージョンが上がっていると npm update
で更新できないので、npm-check-updates というツールを使います:
$ npm install -g npm-check-updates $ ncu -u $ npm update
依存関係でワーニングが出る場合があります:
npm WARN eslint-config-standard@10.2.1 requires a peer of eslint-plugin-import@>=2.2.0 but none was installed. npm WARN eslint-config-standard@10.2.1 requires a peer of eslint-plugin-node@>=4.2.2 but none was installed. npm WARN karma-sinon-chai@1.3.1 requires a peer of chai@^3.5.0 but none was installed. npm WARN karma-sinon-chai@1.3.1 requires a peer of sinon@^2.1.0 but none was installed. npm WARN sinon-chai@2.12.0 requires a peer of sinon@^1.4.0 || ^2.1.0 but none was installed.
この場合は、node_modules
ディレクトリーにある該当パッケージを削除して、再度 npm install
してみます。
それでも、ワーニングが出る場合は、個別にインストールしてみます(package.json
には入れません):
$ npm install chai@3.5.0
.....
├─┬ chai@3.5.0
│ └── type-detect@1.0.0
└── UNMET PEER DEPENDENCY sinon@3.0.0
バージョン指定が不整合を起こしていますね。(chai@3.5.0 では sinon@3.0.0 が必要なのに、karma-sinon-chai@1.3.1 では sinon@^2.1.0 が必要。)
仕方がないので node_modules
を一旦すべて削除してやり直してみます。
$ rm -r node_modules $ npm install
やはり、依存関係のワーニングが出ますね。諦めて戻しました。
公式テンプレートでアップデートされるのを待つしかないようですね。