Vite + React + TS + vanilla-extract +α が動作するサンプルリポジトリを作った
概要
以下のGitHubリポジトリに、Vite + React + TS + vanilla-extract +α が動作するサンプルリポジトリを作った。
本記事では動作させるための設定のポイントや、各利用技術、ライブラリの概要について説明する。
リポジトリ名をplaygroundとしているとおり、今後このリポジトリには色々なライブラリ(react-hook-form, SWRなど)の検証内容を追記していく予定だが、ひとまず環境構築で一段落したので記事を書いた。
動作確認時のバージョン
Vite
Viteは、近年のWebフロントエンド開発における生産性低下に対して、native ES modulesの活用といったアプローチでより高速に、快適に開発できることを目指して開発されているツールである。Nuxt.jsやNext.jsで中規模以上のアプリケーションを開発しており、dev serverへの反映が遅いといった不満をいだいたことがある開発者は少なくないだろう。
開発時にはesbuildを使って依存している多くのライブラリを事前にバンドルすることで、ものの数十ミリ秒〜数百ミリ秒で手元の更新内容がブラウザに反映されるHMRの仕組みを整えている。
プロダクションビルドではrollupを採用しており、プロダクションに向けた細かいバンドル設定はrollupを通して行うことができる。esbuildをプロダクションでも使わない理由はここに記載されている。
つまるところViteは、古くから使われているwebpackの課題点に対して、ノーバンドルツールであるesbuildにHot Module Replacementを組み合わせたら開発体験がめっちゃ改善されるのでは、というアプローチと、本番ビルド時はまだesbuildはアーリーだからrollupを使っておこうか、といったアプローチで解消を試みているツールということができる。
CSS Modulesについて
esbuildだけではCSS Modulesに対応していないっぽい(https://github.com/evanw/esbuild/issues/20) が、Viteを通して利用すればCSS Modulesを使って開発ができる。
また、上記ドキュメントにも書いてあるが、CSS Modulesを使っているとき、キャメルケースに変換してほしければ以下のようにvite.config.tsに書けばいい。
css: {
modules: {
localsConvention: 'camelCase',
},
},
プラグイン機構
Viteはプラグイン機構を備えており、アーリーなゆえに足りない機能はプラグインを書くことで補うことができる。
実際、いくつかの機能はプラグインありきで進めなければならない。このへんは今後破壊的な変更が入る可能性もあり少々怖いが、一方で現時点ですでにある程度のエコシステムを形成できており、将来有望なツールということもできる。
React
ViteでReactを書くためには、公式が用意しているcreate-vite-appといった仕組みを使えばいい。
yarn create @vitejs/app HOGE --template react-ts
ただし、執筆時点ではViteはReact17以降で対応した、import React
が不要になったFeatureに対応していない。
したがって、以下のようにvite-react-jsx
プラグインをインストールし、設定する必要がある。
import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import viteReactJsx from 'vite-react-jsx';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [reactRefresh(), viteReactJsx()],
})
rocon
本題とはそれるが、本リポジトリではルーティングライブラリにroconを利用している。
NuxtやNextを利用している際のルーティング型安全には、pathpidaがあるが、通常のReact SPAに対して類似のアプローチとして有力なものはまだ少ない。
roconを使えば、ルーティング関連の設定をTypeScriptと紐付けることができ、リンク切れやパラメータ名の手打ちミスなどを防ぐことができる。
TypeScript
エイリアス
筆者はTSプロジェクトでは基本的にエイリアスをソースのルートディレクトリに対して設定するが、これも少々設定が必要である。
tsconfigでbaseUrl, pathsを設定の上、以下のようにプラグインvite-tsconfig-paths
をインストールしてconfigも設定をする。
+ import tsconfigPaths from 'vite-tsconfig-paths';
- plugins: [reactRefresh(), viteReactJsx()],
+ plugins: [reactRefresh(), viteReactJsx(), tsconfigPaths()],
vanilla-extract
vanilla-extractはZeroランタイムでCSSをTypeScriptベースで書くことができるライブラリで、2021年5月末にメジャーバージョンをリリースしたアーリーなライブラリである。
筆者は以前からZeroランタイムなCSS記述手法としてlinariaを推していたが、linariaはstyled-componentsの競合といった立ち位置だった。こちらはどちらかというとCSS Modulesの対抗馬に感覚としては近い印象である。
.css.ts
といった拡張子で記述すると、コンパイル後に.cssファイルに吐き出されたり、<style></style>
タグ内に吐き出される仕組みである。筆者の手元では、dev modeでは<style></style>
タグ内に吐き出され、プロダクションビルドでは個々のCSSファイルに吐き出されるようだ。
以下に簡単な記述例を示す。
import {style} from "@vanilla-extract/css";
export const styles = {
headerContainer: style({
paddingTop: 4,
paddingBottom: 4,
display: "flex",
alignItems: 'center',
paddingRight: 12,
paddingLeft: 12,
height: 56,
}),
logo: style({
width: 40,
height: 40,
}),
title: style({
fontWeight: "bold",
fontSize: 24,
textTransform: "uppercase",
whiteSpace: "nowrap",
marginLeft: 8,
})
}
import vite from '~/assets/img/icons/vite.svg';
import { styles } from '~/components/layouts/common/Header.css';
export const Header = () => (
<header className={styles.headerContainer}>
<img className={styles.logo} src={vite} alt="Vite" />
<span className={styles.title}>Vite+React+TS Playground</span>
</header>
);
このように、CSS Modules感覚でType SageなCSSを書くことができる。ちなみにCSSをType Sageにする途方も無いであろう作業をこなしているのはCSSTypesというライブラリである。
Viteで動作させるためには、これまたプラグインが必要である。
+ import {vanillaExtractPlugin} from "@vanilla-extract/vite-plugin";
- plugins: [reactRefresh(), viteReactJsx(), tsconfigPaths()],
+ plugins: [reactRefresh(), vanillaExtractPlugin(), viteReactJsx(), tsconfigPaths()],
筆者が試した範囲だと、`vanillaExtractPlugin()`を配列の前方に置かなければ動作しなかった(最後の要素に指定すると失敗した)。他にもViteを色々触っているなかで、Pluginの順番次第で動作結果の変わることがあり、しばらくの間はViteでPluginを必要とするようなライブラリを次々と入れるのは避けたほうがいいかもしれない。
atoms
vanilla-extractにはatomsという機能があり、paddingXといったショートカット記法が使えたり、レスポンシブ対応などがよりスムーズに実装できるらしい。
らしい、というのは以下のパッケージをインストールして試してみたが使い方がよくわからなかったからである。使いこなすと便利な機能だと思うのでまた機を見て試してみたい。
最後まで読んでいただきありがとうございました!記事が参考になったらバッジお願いします!
Discussion