かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

今更の Vite で React + TypeScript のプロジェクト作ってみた

ちょっぱやだと噂の Vite で React のプロジェクトを作ってみた素振りのメモ

Vite とゎ?

昨今の巨大な JavaScript のプロジェクトはモジュールバンドルのパフォーマンスがボトルネックになってきていた。
従来のものは更新頻度の少ないファイルも全てバンドルして提供していた。
開発サーバーでは10倍から100倍高速である esbuild を用いて依存関係を事前にバンドルし、 native ESM を利用することで頻繁に更新されるファイルを Dynamic import するようにして高速化させている。
Production ではコード分割やCSSの処理が成熟している Rollup を使ってビルドを行う。ESM はまだブラウザのサポートが弱く、バンドルされてない ESM は非効率なためビルドを行う設計にしている。将来的に esbuild の機能が安定してきたら Rollup から esbuild を用いたビルドに変更する可能性がある。

Why Vite をざっくりまとめるとこんな感じでしょうか (解釈間違いあれば教えて下さい)
パフォーマンスの良いバンドラーとブラウザの ESM の仕組みを利用して従来のものより効率的かつ高速に動作するようにしたよ!って印象です。

Vite で React のプロジェクトを作成する

$ npm create vite@latest <project name>
# package json に記載されるパッケージ名
? Package name: ›
# react を選択
? Select a framework: › - Use arrow-keys. Return to submit.
    vanilla
    vue
❯   react
    preact
    lit
    svelte
# TypeScript を使う場合は react-ts を選択
? Select a variant: › - Use arrow-keys. Return to submit.
    react
❯   react-ts

Scaffolding project in /Users/kikiki/<project name>

Done. Now run:

  npm install
  npm run dev

Scaffolding project Done. のメッセージが表示され新規ディレクトリが作成されていればOK

/project name
  |- .gitignore
  |- index.html … エントリーポイント
  |- /src … react のソースコード
  |- tsconfig.json
  |- tsconfig.node.json
  |- vite.config.ts … vite の設定ファイル
note.

調べた blog に載っていた npm init vite でも同じ手順でプロジェクトが作成できました。 ドキュメントに見当たらなかったので旧バージョンのコマンドなのかもしれませんが、深くは調べていません。

プロジェクトディレクトリを作成せず、現在の場所に展開したい場合

<project name> を指定せず . にすれば OK
<project name> が空の時は Package name と同じ名称のディレクトリが作成される

$ npm create vite@lastest .
# ディレクトリ内にファイルが存在する場合は既存のファイルを削除しても構わないか聴かれる
# .git など vite が作成しないファイルやディレクトリも消されてしまうっぽので注意が必要
? Current directory is not empty. Remove existing files and continue? › (y/N)

React project の起動

メッセージにあったようにプロジェクトディレクトリに移動して、npm install して npm run dev すればOK

$ npm install
$ npm run dev
 vite v2.9.9 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

  ready in 1954ms.

localhost:3000/ にアクセスして React のロゴがクルクル回る初期画面が表示されていればOK
npx create react-app と異なり自動でブラウザでアプリを開いてくれないので自分で localhost:3000/ にアクセスする必要がある
dev モードでは HMR (Hot Module Replacement) = コードが変更されると自動的に再描画する 仕組みが入っているので、このまますぐに開発ができそうです!

その他のコマンド
  • プロジェクトのビルド … npm run build
  • ビルドした状態のプレビュー … npm run preview

vite は dev の時は esbuild でビルド時は Rollup なのでビルド時のプレヴューができるようになってるっぽいです

起動時に自動でブラウザを開くようにする

create-react-app と同じように実行時に自動的にブラウザが開くようにする
vite.config.ts に下記オプションを追加すればOK

vite.config.ts

export default defineConfig({
+  server: {
+    open: true,
+  },
  plugins: [react(), tsconfigPaths()],
});

既に localhost がブラウザで開いている場合は別のタブが開くのではなく既に開いているタブが再起動するので無駄にタブが増えず良きです

cf. Configuring Vite | Vite

static なファイルの置き場所

vite では JS ファイルで import した画像などは自動的にハッシュ化されたパスに変換してくれるようです
JS でバンドルするまでもない static な CSS や画像は /public というフォルダを作成しそこに配置すればOK
⚠ デフォルトでは public フォルダがが存在しないので作成する必要がある
/public ディレクトリ内に置かれたファイルは JS から / でアクセスが可能になります

/project name
  |- index.html
  |- /public
  |    |- /images
  |        |- sample.jpg 
  |- /src

/src/App.tsx

function App() {
  return (
    {/* /public/images/sample.jpg は /images/sample.jpg でアクセスできる */}
    <img src="/images/sample.jpg" alt="" />
  )
}

cf. Configuring Vite | Vite

所管

CLI が用意されているので、簡単に React + vite なプロジェクトを作成することができました!
体感として webpack な create-react-app より npm run dev した際の起動時間が超速って感じです。追って ESLint + Prettier などの設定もしてみたいと思います。
おわり ₍ ᐢ. ̫ .ᐢ ₎


続き


[参考]

これは Vita 。

JavaScript ファイル名 (文字列) から拡張子を取得したい

拡張子を含むファイル名の文字列から、拡張子とか拡張子を除いたファイル名を取得する方法のメモ

拡張子の始まりの位置を特定すれば拡張子とファイル名に分解できる

拡張子とは何かを考える

  • 拡張子は . から始まる
  • 文字列の一番最後に出現する . 以降が拡張子
  • 文字列の一番最後に出現する . 以前がファイル名

文字列に含まれる最後の . の位置を取得して、その前後で分解する

// 文字列中の最後の `.` の位置を取得
const extensionIndex = fileNameStr.lastIndexOf('.');
const hasExtension = extensionIndex > 0 && extensionIndex < fileNameStr.length - 1;
// `.` 以降が拡張子
const extension = hasExtension ? fileNameStr.substr(extensionIndex) | undefined;
// '.' 以前がファイル名
const fileName =  hasExtension ? fileNameStr.substr(0, extensionIndex) : fileNameStr;
  • 文字列中に . が存在しない場合は、拡張子なし・全てファイル名と判断する
  • 最後の . が文字列の先頭にある場合は、拡張子なし・全てファイル名と判断する
  • 最後の . が文字列の最後にある場合は、拡張子なし・全てファイル名と判断する

Sample

See the Pen Untitled by KIKIKI (@kikiki_kiki) on CodePen.

? パラメーターが存在する場合、パラメーターと拡張子とファイル名に分解してみるサンプル

厳格にはダメかもだけど、入力値が絞れるような場合はこんな感じで拡張子とファイル名とが取得できそう


[参考]

TypeScript JEST 引数を取る関数のエラーになる場合のテストにハマったメモ

3億年ぶりに JEST を使って引数を取る関数がエラーになる場合のテストケースを書いていてちょいハマったのでメモ

環境
  • jest ^27.5.0
  • ts-jest ^27.1.3
  • typescript ^4.5.5

JEST toThrow(error) でエラーになる場合のテストができる

toThrow() に取れる

  • 正規表現: エラーメッセージがパターンに マッチする か検証します
  • 文字列:エラーメッセージが文字列を含む か検証します
  • error オブジェクト: エラーメッセージがオブジェクトのmessageプロパティと等しいかを検証します
  • errorクラス: errorオブジェクトがそのクラスのインスタンスであるかを検証します

.toThrowError(error?)toThrow(error)エイリアス

function drinkFlavor(flavor) {
  if (flavor == 'octopus') {
    throw new DisgustingFlavorError('yuck, octopus flavor');
  }
}

// test
test('throws on octopus', () => {
  function drinkOctopus() {
    drinkFlavor('octopus');
  }

  // Test that the error message says "yuck" somewhere: these are equivalent
  expect(drinkOctopus).toThrowError(/yuck/);
  expect(drinkOctopus).toThrowError('yuck');

  // Test the exact error message
  expect(drinkOctopus).toThrowError(/^yuck, octopus flavor$/);
  expect(drinkOctopus).toThrowError(new Error('yuck, octopus flavor'));

  // Test that we get a DisgustingFlavorError
  expect(drinkOctopus).toThrowError(DisgustingFlavorError);
});

cf. .toThrow(error?) | Expect · Jest

テストする関数は expect 内で実行される必要がある

引数を取る関数のエラーになる場合のテストをしようと下記のように書いて正しくエラーを取ることができなかった。

const myFunc = (data: string[]) => {
  if (!data.length) {
    throw new Error('Empty data :(');
  }
};

// test
describe('myFunc', () => {
  test('When argument is an empty array, throw error', () => {
    expect(myFunc([])).toThrow();
  });
});

👇 テストを実行

$ npx jest
 FAIL  ./myFunc.test.ts
  myFunc
    ✕ When argument is an empty array, throw error (1 ms)
  ● myFunc › When argument is an empty array, throw error
    Empty data :(
      1 | const myFunc = (data: string[]) => {
      2 |   if (!data.length) {
    > 3 |     throw new Error('Empty data :(');
        |           ^
      4 |   }
      5 | };

エラーが発生している箇所でテストがエラーになっている?

テストしたい関数が先に実行されてしまっているのが原因

エラーの発生は関数が返す値ではないので、expect(myFunc([])) という書き方だと、先に関数が実行されエラーが発生してしまうので expect でエラーをキャッチすることができずテストが落ちてしまっていた。

エラーが発生することのテストはテストする関数を別途関数でラップする必要がある

expect に関数を渡すと実行されるので、エラーの発生する関数を別の関数で囲ってあげれば良い

// test
describe('myFunc', () => {
  test('When argument is an empty array, throw error', () => {
-   expect(myFunc([])).toThrow();
+   expect(() => myFunc([])).toThrow();
  });
});

👇 テストを実行

$ npx jest
 PASS  ./myFunc.test.ts
  myFunc
    ✓ When argument is an empty array, throw error (9 ms)

₍ ᐢ. ̫ .ᐢ ₎ 👌

よくみたら公式のサンプルもテスト用に関数で囲ってた。
教訓。公式のコードはちゃんと見よう。
おわり。


[参考]

作業しながら空挺ドラゴンズのアニメ見返してて、改めて面白いな〜と思って原作買ったら原作の絵が超好みだった。