banner
AgedCoffee

AgedCoffee

元構築ツールvite初探

前書き#

viteは全く新しいフロントエンド構築ツールであり、構築ツールの前にという言葉を加えることで、このメタバースの波の中でmetaが追加され、何らかの神秘的な力を持つように感じられます。

React は 18 でサーバーサイドストリーミングReact サーバーコンポーネントの機能を導入したため、Vue フレームワークの作者である尤雨溪も Vite の作者であり、React はすでに Meta Framework と呼ばれています。

もちろん、Vite を元構築ツールと呼ぶことには名実ともに意味があります。

  • meta という言葉はギリシャ語に由来し、超越の意味を持ち、vite の登場は先行プロジェクトがすべてwebpackに基づいて構築されている現状を超越するという意味を持っています。
  • 中国語の神経元などの言葉において、元は最小機能単位の意味もあり、vite は webpack の構築方案に比べて、最大の特徴の一つは開発段階でソースコード全体をバンドルとしてブラウザに実行させる必要がなく、esmに基づいて現代のブラウザに直接処理させることができる点です。esmはその最小の機能単位として理解できます。

本題に戻ると、プロジェクトを vite 駆動に改造するアイデアが生まれたのは、プロジェクトのコード量とファイル数が増える中で、webpack の dev 環境での毎回のコールドスタートとホットリロードの速度が明らかに遅くなったためです。また、以前から vite のスムーズで迅速な開発体験を聞いていたため、試してみたくなりました。

次に、既存のwebpackベースのreact+antdプロジェクトをvite駆動に改造するプロセスについて主に説明します。

package.json の大幅なスリム化#

エントリ index.html ファイルの調整#

index.htmlはプロジェクトの最外層にあり、publicフォルダ内にはありません

webpack

<!DOCTYPE html>
<html>
  <head>
    <link rel="icon" href="%PUBLIC_URL%/logo.ico" />
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root" style="height: 100%"></div>
  </body>
</html>

vite

<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="icon" href="/src/assets/logo.ico" />
  </head>
  <body>
    <div id="root"></div>
    <!-- vite特有のesmエントリファイル -->
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

react および typescript サポート#

公式プラグイン@vitejs/plugin-reactは完全な react サポートを提供します。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://cn.vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

vite はesbuildを内蔵しており、ts ファイルのコンパイル処理を行います。プロジェクトは独自の tsconfig ファイルを提供するだけで済みます。

less および module-css サポート#

vite は less および module-css のサポートを内蔵しており、less のプリプロセッサ依存関係をインストールするだけで済みます。antd の less は less の関数機能を使用しているため、preprocessorOptions で javascriptEnabled 設定を有効にする必要があります。

export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
      },
    },
  },
})

プロジェクトの基本 server およびプロキシ設定#

export default defineConfig({
  server: {
    port: 3000,
    host: 'localhost',
    open: true,
    https: true,
    hmr: {
      host: 'localhost',
    },
    proxy: {
      '/api': "https://tartget-server",
      '/special/api': {
        target: "https://special-tartget-server",
        changeOrigin: true,
        rewrite: path => path.replace(/^\/special/, ''),
      },
    },
  },
});

絶対パスの alias 設定#

  • プロジェクト内では一般的に絶対パスでモジュールをインポートし、@を使ってルートフォルダの src フォルダのパスを置き換えます。
  • 一部の依存関係の node_modules 自体に @符号が含まれているため、そのようなインポートはマッチすべきではありません。
  • 一部の古い less のインポートは~を使用して絶対インポートの方法を示しているため、そのようなインポートは特別な処理が必要です。
export default defineConfig({
  resolve: {
    alias: [
      {
        find: /^@\//,
        replacement: `${path.resolve(__dirname, 'src')}${path.sep}`,
      },
      { find: /^~/, replacement: '' },
    ],
  },
})

静的ファイルのインポート#

一般的な静的リソースのインポートには vite が多くの方法をサポートしています。移行プロセスでは、変数に基づいて静的リソースの参照処理ロジックを組み合わせる必要がありました。webpack 環境では require メソッドを使用できないため、vite の esm ネイティブ機能import.meta.urlを使用する必要があります。

// shortに基づいて国旗リソースの画像パスを動的に生成
const getFlag = (short: string) => {
  return new URL(`./flags/${short.toLowerCase()}.png`, import.meta.url).href
}

環境変数の設定#

dotenv 関連の依存関係をインポートし、起動コマンドに dotenv を追加します。

{
  "scripts": {
    "dev": "dotenv -e .env.dev vite",
    "build": "tsc && dotenv -e .env.dev vite build",
    "serve": "vite preview"
  },
  "devDependencies": {
    "dotenv": "^8.2.0",
    "dotenv-cli": "^4.0.0",
    "dotenv-expand": "^5.1.0"
  }
}

vite では、約束されたプレフィックス規則VITE_を遵守する必要があります。

VITE_VAR=SOME_VALUE

src 内に静的検証用の ts ファイルenv.d.tsを定義します。

interface ImportMetaEnv extends Readonly<Record<string, string>> {
  readonly VITE_VAR: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

manual-chunk バンドル最適化ロジック#

プロジェクト内で React-Router ルートを使用して遅延読み込みを行うことで、コードを自動的に分割し、一般的には大部分のシナリオを解決できます。コード分割が依然として合理的でない場合は、設定に手動で chunk の生成ロジックを追加できます。

import path from 'path'
import { dependencies } from './package.json'

function renderChunks(deps) {
  let chunks = {}
  Object.keys(deps).forEach((key) => {
    if (key.includes('@types')) return
    if (['react', 'react-router-dom', 'react-dom'].includes(key)) return
    chunks[key] = [key]
  })
  return chunks
}

export default defineConfig({
  // buildの下のロジックは生産バンドル時にのみ実行されます
  build: {
    sourcemap: false,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-router-dom', 'react-dom'],
          ...renderChunks(dependencies),
        },
      },
    },
  },
})

奇妙な落とし穴#

  • moment 国際化パッケージは異なる方法でインポートする必要があります。
// webpack
import 'moment/locale/zh-cn'
// vite
import 'moment/dist/locale/zh-cn'
  • 一部の依存パッケージは webpack の生産環境では問題ないが、vite ではエラーが発生します。react-response パッケージは生産環境で global が存在しないというエラーが発生するため、script タグにフォールバックロジックを追加する必要があります。
<script>
  if (global === undefined) {
    var global = window
  }
</script>

完全な vite 設定#

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { dependencies } from './package.json';

function renderChunks(deps) {
  let chunks = {};
  Object.keys(deps).forEach(key => {
    if (key.includes('@types')) return;
    if (['react', 'react-router-dom', 'react-dom'].includes(key)) return;
    chunks[key] = [key];
  });
  return chunks;
}

export default defineConfig({
  plugins: [
    react(),
  ],
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
      },
    },
  },
  server: {
    port: 3000,
    host: 'localhost',
    open: true,
    https: true,
    hmr: {
      host: 'localhost',
    },
    proxy: {
      '/api': "https://tartget-server",
      '/special/api': {
        target: "https://special-tartget-server",
        changeOrigin: true,
        rewrite: path => path.replace(/^\/special/, ''),
      },
    },
  },
  resolve: {
    alias: [
      {
        find: /^@\//,
        replacement: `${path.resolve(__dirname, 'src')}${path.sep}`,
      },
      { find: /^~/, replacement: '' },
    ],
  },
  build: {
    sourcemap: false,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-router-dom', 'react-dom'],
          ...renderChunks(dependencies),
        },
      },
    },
  },
});

最後に#

  1. 移行後のプロジェクトは最新の chrome で問題なく動作しますが、一部の古いブラウザのサポートはまだ検証が必要です。
    (vite は古いブラウザのサポートプラグインを提供しています)

  2. viteは開発環境と生産環境での構築方法に一定の差異があるため、最終的なオンラインデプロイに一定の困難をもたらす可能性があります。

  3. また、移行したプロジェクトにおいて antd のスタイルのインポート方法は、less のソースコードを全量インポートし、自分の less テーマ変数を上書きしました。ブラウザがlessファイルの実行をサポートしていないため、viteはlessファイルを完全にコンパイルする必要があり、プロジェクトを起動するにはlessファイルが多い場合、コンパイル速度が遅くなります。その後、less の按需インポートや less テーマ変数のプラグイン注入を設定できます。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。