前提条件
- VueCLI v5.0.8
- TypeScript v4.8.1
- Vue v2.7
- unplugin-auto-import v0.11.2
- unplugin-vue-components v0.22.9
import 文省略
.vue
や、 .ts
の import
文を省略できるようにしていきます。
.ts
は、 import
文なしで参照できるようになります。
参照されたモジュールは、ビルド時にオンデマンドで自動インポートされます。
.ts
の import
文省略の方法は、こちらで unplugin-auto-import を利用した説明をしています。
SFC
.vue
は、 <template>
内で記述された場合にオンデマンドで自動インポートされます。
.vue
の自動インポートは、 unplugin-vue-components で行えるようになります。
まずは、 unplugin-vue-components をインストールします。
pnpm add -D unplugin-vue-components
unplugin-vue-components で SFC の自動インポートを有効にするには、 vue.config.js
の中で webpack の設定に以下を追加します。
module.exports = {
configureWebpack: {
plugins: [
require("unplugin-vue-components/webpack")({
dirs: ["src/packages"], // コンポーネントを探索するディレクトリ
deep: true, // サブディレクトリを探索
extensions: ["vue"], // コンポーネントの拡張子
globs: ["src/packages/**/!(temp)/*.vue"], // glob パターンを用いて自動インポートの対象から外すことが可能
dts: "src/components.d.ts", // volar を使っていれば、 TypeScript のサポートを受けれれます
version: 2.7, // Vue.js のバージョン
}),
],
},
};
dts
オプションで指定したファイルに SFC の定義がグローバル空間に追加されます。
tsconfig
の include
で指定した範囲内に出力されるようにします。
VSCode で volar をインストール済みであれば、 <template>
内で、コンポーネントの補完が有効になり便利です。
また、 props
の型サポートも有効になります。
型定義の import 文省略
unplugin-auto-import では、 型エイリアス export type
や インタフェース export interface
は、 import
文を省略できません。
そのため、 export
されている 型定義をグローバル定義にして import 文を省略できるようにします。
import * as packages from "@/packages";
declare global {
// グローバル型定義を追記していく
type SampleType = pacakges.SampleType,
}
index.ts
で export
を集約でていないと、 import
文を都度追記して代入する必要があるので、手間がかかりすぎてしまいます。
サブディレクトリも含めてすべての export
を集約できるように index.ts
を自動生成しておきます。
こうしておくことで、 packages
ディレクトリ内の全ての型定義を参照できるようなっています。
index.ts
を再帰的に自動生成するには、 ctix を使います。
export
するモジュールの命名は、ファイル名をプレフィックスにするなど、グローバルネームスペースで衝突を避ける工夫が必要です。
index.ts
は、バンドラーでは全てのファイルがバンドルされてビルド後のファイル容量が大きくなってしまいます。
しかし、ここでは型定義を集めているだけなのでバンドルサイズは影響を受けません。
ESLint ルール
型定義をグローバル定義すると import
文を省略してもビルドは通るようになりますが、 ESLint を導入済みであれば no-undef エラーが検出されてしまいます。
エラーを防ぐため、 import
文を省略した型定義は、グローバル定義済みであることを ESLint ルールに追加する必要があります。
{
"globals": {
"SampleType": true
}
}
.eslintrc.js
から .eslintrc-global-types.json
のルールを extens
します。
module.exports = {
extends: [
".eslintrc-global-types",
]
}
ESLint ルール .eslintrc-global-types.json
を用意する手間を省くため、グローバル型定義ファイルsrc/global-types.d.ts
から生成させるスクリプトを実装しました。
import { $ } from "zx";
import fire from "js-fire";
import { render } from "template-file";
const root = process.cwd();
const template = `{
"globals": {
{{typesJson}}
}
}`;
const generateEslintRuleGlobalTypes = {
__description__: "📦 global-types.d.ts から ESLint ルールのグローバル型を生成する",
execute: async () => {
const globalTypesFile = await $`cat ${root}/src/global-types.d.ts`;
const globalTypes = globalTypesFile.stdout.split("\n")
.map((line) => line.trim())
.filter((line) => line.length > 0 && line.match(/^type\s/) !== null)
.map((line) => {
//型名を取得
return line.split(" ")[1];
})
.map((line) => {
// ジェネリスク型を除外;
return line.replace(/<.*>/, "").replace(/<.*/, "");
});
const typesJson = globalTypes.map((line) => {
return `"${line}": true`;
}).join(",\n ");
const result = await render(template, { typesJson });
await $`echo ${result} > ${root}/.eslintrc-global-types.json`;
},
};
fire(generateEslintRuleGlobalTypes);
以上のスクリプトを npm scripts から実行できるようにしておきます。
"scripts": {
"gen-eslint-globals": "node tools/generate-eslint-rule-global-types.mjs execute"
}
新たに import
文を省略したい型がある場合は、以下の手順で省略可能にします。
-
src/global-types.d.ts
に型定義を追加。 -
$ pnpm gen-eslint-globals
を実行。 -
.eslintrc-global-types.json
に ESLint ルールが追加されてno-undef
エラー回避。
まとめ
export
しているモジュールは、 import
文を省略しつつコード補完が効いて実装が快適になったので概ね良かったと思います。
ローカルコンポーネントの SFC は使用時に import
文が不要になり、 .ts
ファイルは export
されているモジュールや型定義の import
文を省略することができました。
今回は SFC や、 .ts
ファイルが合計で 9万行程度のプロジェクト内で import 文を省略し、合計で 3,000行のimport
文を省略させることができました。
コードを減らせるということはレビューコストが減ると思います。
また、import
文が省略され自動インポートとなるので、誤って粒度が大きい index.ts
をインポートしてしまいバンドルファイルサイズが増えるリスクも少なくなります。
注意しなければいけないのは、 export
するモジュールがグローバル定義となることです。
プロジェクト内のディレクトリ階層はあまり深くせず、 named export の命名はファイル名をプレフィックスにするなど、コンフリクトしないような命名規則にしておく必要があります。