既存のVue.jsプロダクトにTypeScriptを入れる【webpack4.x+babel7+vue.js 2.x】
Laravel4+blade+jQuery+knockout.,jsの、レガシーなフロントエンド構成から、ひとまず、knockout.jsを排除してVue.jsに書き換える作業をしていたのですが、急遽、「TypeScriptも入れたほうが良くね?」ということになったので、作業メモ。
・webpack4.x+babel7+vue.js 2.xの環境については、以下サイトを参考に構築しましたm(_ _)m
【参考サイト】Vuexを使用する基本的なビルド環境を構築する
・class style componentの導入はVue3.0でのTypeScriptの影響を考慮し、見送りました。Vue.extendベースでの開発想定
【参考サイト】Vue.js × TypeScript でclass style componentを廃止した話
npmモジュールのインストール
まずは、TypeScript と、TypeScriptをwebpackで処理するための「ts-loader」を入れます
1 |
npm i -D typescript ts-loader |
webpackがまだ入って無いよーって方は、webpackも入れます。
1 |
npm i -D webpack webpack-cli |
package.jsonに追加されたことを確認します。
■package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "production": "webpack -p", "development": "webpack -d --watch" }, "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.4.4", "@babel/core": "^7.4.5", "@babel/preset-env": "^7.4.5", "babel-loader": "^8.0.6", "css-loader": "^3.0.0", "node-sass": "^4.12.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "ts-loader": "^6.0.4", // 追加 "typescript": "^3.5.3", // 追加 "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.10", "webpack": "^4.35.0", "webpack-cli": "^3.3.5" }, "dependencies": { "@babel/polyfill": "^7.4.4", "axios": "^0.19.0", "vue": "^2.6.10" } } |
webpack.config.jsの修正
webpack.config.jsファイルにTypeScript用の記述を追加していきます。(①〜③)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // entry point entry: { // - 'pages': './resources/pages/main.js',【①js→tsへ】 'pages': './resources/pages/main.ts' }, // 出力するパスは絶対パスで書きます output: { filename: '[name].js', path: path.resolve(__dirname, 'public/js/vue'), publicPath: '/', }, // webpack4はlordersではなくなりました module: { rules: [ { // 拡張子 .ts の場合【②TyeScript用のルールを追加】 test: /\.ts$/, // TypeScript をコンパイルする use: "ts-loader" }, // 拡張子.vueのファイルに対する設定 { test: /\.vue$/, use: [ { loader: "vue-loader" } ] }, // 拡張子.jsのファイルに対する設定 { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', }, ] }, { test: /\.css$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, ] }, { test: /\.scss$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'sass-loader', }, ] } ] }, // デフォルトの設定値だけでは足りないことについて解決します resolve: { // モジュールを読み込むときに検索するディレクトリの設定 modules: [path.join(__dirname, 'resources'), 'node_modules'], // importするときに省略できる拡張子の設定 【③TyeScriptの拡張子(ts)を追加】 extensions: ['.js', '.ts', '.vue', 'css'], alias: { // 例えばmain.js内で `import Vue from 'vue';` と記述したときの`from vue`が表すファイルパスを指定 'vue$': 'vue/dist/vue.esm.js' }, }, // プラグインを列挙 plugins: [ new VueLoaderPlugin() ], } |
main.jsをmain.tsにリネームする
この状態で、①にある、main.jsをmain.tsにリネームします。
エラーと戦う
準備ができたので、一度 npm run dev 等でコンパイルしてみると、多くのエラーが出ると思うので一つづつ倒していきます
1.Cannot find module
1 2 3 |
./app/javascript/packs/hello_vue.ts [tsl] ERROR in /path/to/project/app/javascript/packs/hello_vue.ts(7, 17) TS2307: Cannot find module '../app.vue'. |
これはTypeScriptから .vue ファイルが読めていないということなので、 webpack.config.jsを以下のように編集します。
■webpack.config.js
①option「appendTsSuffixTo」で、vueファイルをtsとして監視するように追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
module: { rules: [ { // 拡張子 .ts の場合 test: /\.ts$/, // TypeScript をコンパイルする use: [ { loader:"ts-loader", options: { appendTsSuffixTo: [/\.vue$/] //①optionsを追加 } } ] }, //・・・略・・// } |
また、Cannot find moduleとなっていた.vueファイルでも、TypeScriptを利用するための宣言が必要なので、全て書き換えます。(ついでに、型推論が使えるようにする。)
■.vueファイル(単一ファイルコンポーネント)
① <script lang="ts"> とすることで、Typescripを利用する宣言をする。
②Vue コンポーネントオプション内部で TypeScript が型を適切に推測できるように export default していたオブジェクトを Vue.extend() で定義します。
1 2 3 4 5 6 7 8 9 10 |
<script lang="ts"> //① Typescripを利用する宣言 import DatePickerComponent from './DatePickerComponent.vue' import Vue from 'vue'; // ③ Vueをインポートする。 export default Vue.extend({ //② 型推論に必要な定義 data:() =>{ fname:'', } }) </script> |
③ Vueがimportされていない場合は、以下のエラーがでるので、importしておく
1 2 3 4 5 6 7 |
ERROR in /usr/xxxx/xxxx/resources/components/DatEpickercompoNent.vue.ts [tsl] ERROR in /usr/xxxx/xxxx/resources/components/DatEpickercompoNent.vue.ts(9,16) TS2686: 'Vue' refers to a UMD global, but the current file is a module. Consider adding an import instead. ERROR in /usr/xxxx/xxxx/resources/components/DatEpickercompoNent.vue.ts [tsl] ERROR in /usr/xxxx/xxxx/resources/components/DatEpickercompoNent.vue.ts(9,20) TS2339: Property 'extend' does not exist on type 'typeof import("/usr/xxxx/xxxx/node_modules/vue/types/index")'. |
2.Laravel用のvenderディレクトリ配下でめちゃエラーでる
1 2 3 4 5 6 7 |
ERROR in /usr/xxxxx/xxxxx/vendor/symfony/translation/Tests/fixtures/resources.ts [tsl] ERROR in /usr/xxxxx/xxxxx/vendor/symfony/translation/Tests/fixtures/resources.ts(3,4) TS2304: Cannot find name 'context'. ERROR in /usr/xxxxx/xxxxx/vendor/symfony/translation/Tests/fixtures/resources.ts [tsl] ERROR in /usr/xxxxx/xxxxx/vendor/symfony/translation/Tests/fixtures/resources.ts(4,6) TS2749: 'name' refers to a value, but is being used as a type here. |
symfonyでエラーが起きてる辛い。なぜそんな関係ないところ見に行ってしまうん・・。
こちらは、tsconfig.jsでvenderディレクトリを対象外にする必要があります。
■tsconfig.js
①excludeを追記する。
1 2 3 4 5 6 7 8 |
{ "compilerOptions": { ... }, "exclude": [ //①対象外フォルダを指定する。 "vendor" ], } |
3.Cannot find nameめちゃでる。
Laravelのbladeなど、テンプレートエンジン内で書いたグローバル変数や、読み込んだjQueryなどの外部ライブラリ周りで、以下のエラーが大量発生します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ERROR in /usr/xxxx/xxxx/resources/pages/main.ts ./resources/pages/main.ts [tsl] ERROR in /usr/xxxx/xxxx/resources/pages/main.ts(285,28) TS2304: Cannot find name 'userId'. ERROR in /usr/xxxx/xxxx/resources/pages/main.ts ./resources/pages/main.ts [tsl] ERROR in /usr/xxxx/xxxx/resources/pages/main.ts(308,28) TS2304: Cannot find name 'laravel_data'. ERROR in /usr/xxxx/xxxx/resources/pages/main.ts ./resources/pages/main.ts [tsl] ERROR in /usr/xxxx/xxxx/resources/pages/main.ts(312,7) TS2581: Cannot find name '$'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery`. |
TypeScriptコンパイラは宣言されていない変数にアクセスする処理をコンパイルエラーにし、グローバル変数に依存する処理を検出することができます。
【参考サイト:レガシーなJavaScriptコードをTypeScriptを使って整備したメモ – ryiwamotoのブログ】
このような、グローバル変数は最終的には削除していくべきですが、今回は、アンビエント宣言を使用し、ソースコード外で初期化される変数を明示的に宣言します。
■エラーがでてる.vueファイルor .tsファイル
①bladeで宣言している変数を、vue側でアンビエント宣言する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import Vue from 'vue' import 'v-autocomplete/dist/v-autocomplete' import '@babel/polyfill' import DatePicker from '../../components/DatePicker' // ①declareで宣言 declare const userId: Number declare const laravel_data: String declare const error_message: String declare const $ :any let app_vue = new Vue({ el: '#vue_app', components: { DatePicker }, methods: { displayResult (message) { $.growl({ //jQueryライブラリも使える icon: 'glyphicon glyphicon-warning-sign', message: message }); } } |
これで、依存しているグローバル変数名とその型を明確にすることができました。
dataやpropsの型を付けていく
これで、一旦、TypeScriptの導入と、コンパイルエラーの解消は完了したので、あとはVueコンポーネントのdataなどに型定義をしていきます!TypeScriptの恩恵受けるぞー。お疲れ様でしたー。
この記事がお役に立てたら、是非シェアをお願いします^^