Webpacker gemを導入したRailsプロジェクトにtypescript+vue.jsを導入した時に調べたことのメモ記載しておきます。
- TypeScript/vueのインストール/設定
- Vue-loaderの動き
- PnpWebpackPlugin.tsLoaderOptionsについて
- Webpacker Gem/ Loader Packageバージョン
- その他参考資料
TypeScript/vueのインストール/設定
既にプロジェクトにはWebpackerは導入済だったので、WebpackerのREADME通りに
$ bin/rails webpacker:install:typescript $ bin/rails webpacker:install:vue
と実行し、typescriptとvueをインストール/設定しました。その際、出力される ts-loader
の設定ファイルは、
# /config/webpack/loaders/typescripts.js const PnpWebpackPlugin = require('pnp-webpack-plugin') module.exports = { test: /\.tsx?(\.erb)?$/, use: [ { loader: 'ts-loader', options: PnpWebpackPlugin.tsLoaderOptions() } ] }
のようになってましたが、このままではVueファイルを以下のようなSFC(単一ファイルコンポーネント)にした場合にうまくTypeScriptとして認識してくれずに、ブラウザで開くとJSエラーが発生していました。
# app/javascript/app.vue <template> <div id="app"> <p>{{ message }}</p> </div> </template> <script lang="ts"> export default { data: function () { return { message: "Hello Vue!" } } } </script> <style scoped> p { font-size: 2em; text-align: center; } </style>
[エラー内容]
Property or method "message" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property
ts-loaderのドキュメントを参照し、オプションを指定するとうまく動くようになってくれました。
# /config/webpack/loaders/typescripts.js const PnpWebpackPlugin = require('pnp-webpack-plugin') module.exports = { test: /\.tsx?(\.erb)?$/, use: [ { loader: 'ts-loader', options: PnpWebpackPlugin.tsLoaderOptions({ appendTsSuffixTo: [/\.vue$/] }) } ] }
Vue-loaderの動き
vue-loader はファイルを解析し、それぞれの言語ブロックを必要に応じて他の loader を通し、最終的に module.exports が Vue.js のコンポーネントオプションオブジェクトの CommonJS モジュールに変換します。
と記載されていて、 vue-loader
を介して ts-loader
がコンパイルしてくれるらしいことが分かります。
PnpWebpackPlugin.tsLoaderOptionsについて
上記の ts-loader
の設定ファイル( /config/webpack/loaders/typescripts.js
)で、 PnpWebpackPlugin.tsLoaderOptions()
のようにオプション指定されていて、これが何かも気になったので、調べてみました。
Pnpとは?
そもそも、先頭3文字の Pnp
というのは何かというと、Yarn 2.0から導入された node_modules
に変わるパッケージの依存関係を解決するAPIであるPlug'n'Play
の頭文字を取ったもの。
Plug'n'Play
を導入すると、でかい node_modules
の変わりに .pnp.js
という単一のファイルで済むようになり70%ほど速度が早くなるようです(参考)
ただし、Webpackerのissueを見ると、2020/5/16時点ではまだ使える状態にはなさそうなので、恩恵を受けられる日が来るのを待ちましょう。
PnpWebpackPlugin.tsLoaderOptionsはなぜ要るのか?
Plug'n'Play
が導入されると、Typescript
のimport
文やRefarence
ディレクティブが参照する箇所を node_modules
から変えないといけないので、Plug'n'Play
が有効になっている場合のみ、ts-loaderのオプションに
resolveModuleName
resolveTypeReferenceDirective
というオプションを渡して、TypeScriptのデフォルトの実装(import
文やRefarence
ディレクティブ)と動きが変わるようにしてあげる必要があるようです。
PnpWebpackPlugin.tsLoaderOptionsの実装は以下のコードを確認しました。 https://github.com/arcanis/pnp-webpack-plugin/blob/659125cee0625f1a543bc1645e3917ad90857052/index.js#L157
参考までに、PnpWebpackPlugin.tsLoaderOptionsの実装の中の
-
process.versions.pnp
の説明(yarnpkgのドキュメント) resolveModuleName
とresolveTypeReferenceDirective
の説明(ts-loaderのドキュメント)
の参照リンクも貼っておきます。
Webpacker Gem/ Loader Packageバージョン
- Webpacker gem
- 4.2.2
- ts-loader
- 7.0.4
- vue-loader
- 15.9.2