Chapter 09

モダンJSとNext Steps

ES Modules, npm, Vite などモダンな JavaScript 開発の基礎を学び、React コースへの橋渡しをします。

25 min

このチャプターで学ぶこと

  • ES Modules (import/export) を理解できる
  • npm の基本的な使い方を覚える
  • Vite でプロジェクトをセットアップできる
  • このコースで学んだ JS 知識が React でどう活きるか理解できる

これはこのコースの最終チャプターです。

前のチャプターで、バニラ JS での状態管理の限界を体験しました。React を使えばそれらの問題を解決できると説明しましたが、React を使うには、もう少し準備が必要です。

このチャプターでは、React(そしてモダンな JavaScript 開発全般)に欠かせない3つのツールと概念を学びます。

これらを学んだら、いよいよ React コースへ進みます。


9.1 ES Modules

なぜモジュールが必要か

これまでのチャプターでは、すべてのコードを 1 つの JS ファイルに書いてきました。しかし、アプリが大きくなるにつれて、1 ファイルに何百・何千行ものコードを詰め込むのは維持困難になります。

PHP での解決策を思い出してください。

// PHP では namespace と use でファイルを分割する
namespace App\Services;

use App\Models\User;
use App\Helpers\FormatHelper;

class UserService {
  public function getFormattedName(User $user): string {
    return FormatHelper::capitalize($user->name);
  }
}

JavaScript にも、同じようなファイル分割の仕組みがあります。それが ES Modules(ESM)です。

Named Export(名前付きエクスポート)

// utils.js - 便利な関数をまとめたファイル

// 関数をエクスポートする(外から使えるようにする)
export function formatPrice(price) {
  // 数値を日本円形式にフォーマットする
  return `¥${price.toLocaleString()}`;
}

export function formatDate(dateStr) {
  // 日付文字列を読みやすい形式に変換する
  const date = new Date(dateStr);
  return date.toLocaleDateString("ja-JP", {
    year: "numeric",
    month: "long",
    day: "numeric"
  });
}

// 定数もエクスポートできる
export const TAX_RATE = 0.1;
export const SHIPPING_FREE_THRESHOLD = 3000;
// main.js - utils.js の関数を使うファイル

// 必要なものだけを { } で指定してインポートする
import { formatPrice, TAX_RATE } from "./utils.js";

// これで utils.js の関数が使える
const price = 1500;
const taxIncluded = price * (1 + TAX_RATE);

console.log(formatPrice(taxIncluded)); // "¥1,650"

// 使わないものはインポートしなくてよい
// formatDate は今回使わないので import しない(バンドルサイズの最適化)
// 別名でインポートすることもできる(名前が衝突する場合など)
import { formatPrice as fp, TAX_RATE as rate } from "./utils.js";

console.log(fp(1000)); // "¥1,000"
// まとめてインポートすることもできる
import * as Utils from "./utils.js";

console.log(Utils.formatPrice(1000)); // "¥1,000"
console.log(Utils.TAX_RATE);         // 0.1

Default Export(デフォルトエクスポート)

1ファイルにつき1つだけ設定できる「メイン」のエクスポートです。

// User.js - User クラスを管理するファイル

export default class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
    this.createdAt = new Date();
  }

  getDisplayName() {
    return `${this.name} <${this.email}>`;
  }

  isAdmin() {
    return this.email.endsWith("@admin.example.com");
  }
}
// main.js

// default export は { } なしでインポートする
// 名前は自由につけられる(慣習として元のファイル名と合わせることが多い)
import User from "./User.js";

const user = new User("田中太郎", "tanaka@example.com");
console.log(user.getDisplayName()); // "田中太郎 <tanaka@example.com>"
// 同じファイルに Named Export と Default Export を混在させることもできる
// api.js

export default async function fetchUser(id) {
  // デフォルトエクスポート: このファイルのメインの機能
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// 名前付きエクスポート: 補助的な機能
export function buildApiUrl(path) {
  return `https://api.example.com${path}`;
}

export const DEFAULT_TIMEOUT = 5000;
📝 React コンポーネントは Default Export が基本

React のコンポーネントは、基本的に「1 ファイル 1 コンポーネント」で、default export します。

// Button.jsx
export default function Button({ children, onClick }) {
  return <button onClick={onClick}>{children}</button>;
}

// App.jsx
import Button from "./Button.jsx";  // default import

これは React コースで毎日書くパターンです。今のうちに import/export の感覚をつかんでおきましょう。

HTML での ES Modules の使い方

ブラウザで ES Modules を使うには、<script> タグに type="module" を追加します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>ES Modules デモ</title>
</head>
<body>
  <div id="output"></div>

  <!-- type="module" をつけることで ES Modules が使える -->
  <script type="module">
    // インラインでも import できる
    import { formatPrice, formatDate } from "./utils.js";

    const output = document.getElementById("output");
    output.textContent = formatPrice(12800);
  </script>

  <!-- または外部ファイルを読み込む -->
  <!-- <script type="module" src="main.js"></script> -->
</body>
</html>
⚠️ モジュールはファイルサーバー経由で開く必要がある

type="module" を使うと、ブラウザはセキュリティ上の理由から file:// プロトコルでの実行を拒否します。

file:///Users/xxx/index.html のようにファイルを直接ブラウザで開くと、CORS エラーが発生します。

開発時は必ず http://localhost:xxxx 形式のローカルサーバー経由で開いてください。VS Code の「Live Server」拡張機能や、後述の Vite を使うと便利です。


9.2 npm の基本

npm とは

npm(Node Package Manager) は、JavaScript のパッケージ(ライブラリ)を管理するツールです。世界中の開発者が作ったライブラリをインストールして使えます。

PHP の Composer とほぼ同じ役割です。

PHP Composer との比較

Composernpm
composer.jsonpackage.json
composer.lockpackage-lock.json
vendor/ ディレクトリnode_modules/ ディレクトリ
composer installnpm install
composer require パッケージ名npm install パッケージ名
composer require --devnpm install --save-dev
Packagistnpmjs.com
💡 実は npm はもう使っているかもしれない

Laravel を使ったことがある方は、npm をすでに使っているはずです。

# Laravel プロジェクトで見たことがありませんか?
npm install
npm run dev   # Vite の開発サーバーを起動
npm run build # 本番用ビルド

実は Laravel 9 以降、フロントエンドのビルドに Vite と npm が使われています。コマンドを打ったことがある方は、すでに npm ユーザーです。

プロジェクトの初期化

# 新しいフォルダを作って npm プロジェクトを初期化する
mkdir my-js-project
cd my-js-project

# package.json を自動生成する(-y で質問をすべてデフォルト回答)
npm init -y

package.json が生成されます。

{
  "name": "my-js-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

パッケージのインストール

# dayjs という日付操作ライブラリをインストールする
npm install dayjs

package.json が更新され、node_modules/ ディレクトリが作成されます。

{
  "name": "my-js-project",
  "version": "1.0.0",
  "dependencies": {
    "dayjs": "^1.11.10"  // 追加された!
  }
}

インストールしたパッケージを使ってみましょう。

// main.js

// npm でインストールしたパッケージを import する(パスではなく名前で指定)
import dayjs from "dayjs";

const now = dayjs();
console.log(now.format("YYYY年MM月DD日")); // "2024年03月15日"

const oneWeekLater = now.add(1, "week");
console.log(oneWeekLater.format("YYYY-MM-DD")); // "2024-03-22"

// 日本語化する
import "dayjs/locale/ja";
dayjs.locale("ja");

console.log(now.format("MM月DD日 (dddd)")); // "03月15日 (金曜日)"

開発依存パッケージ

本番環境には含めない(開発時だけ使う)パッケージには --save-dev フラグをつけます。

# ESLint(コード品質チェックツール)を開発依存でインストール
npm install --save-dev eslint

# Prettier(コードフォーマッター)も開発依存
npm install --save-dev prettier
{
  "dependencies": {
    "dayjs": "^1.11.10"     // 本番でも使う
  },
  "devDependencies": {
    "eslint": "^8.57.0",    // 開発時のみ
    "prettier": "^3.2.5"    // 開発時のみ
  }
}

scripts の定義

package.jsonscripts セクションに、よく使うコマンドを定義できます。

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "eslint src/**/*.js",
    "format": "prettier --write src/"
  }
}
# npm run スクリプト名 で実行できる
npm run dev    # 開発サーバー起動
npm run build  # 本番ビルド
npm run lint   # リント実行
💡 git に node_modules は含めない

node_modules/ は非常に大量のファイルを含むため、git で管理しません。

.gitignore ファイルに必ず以下を追加してください。

node_modules/
dist/
.env

チームメンバーが git clone したあと npm install を実行すれば、package.jsonpackage-lock.json から同じ環境を再現できます。これは Composer の composer install と全く同じ考え方です。


9.3 Vite でプロジェクトをセットアップ

Vite とは

Vite(ヴィート) は、フランス語で「速い」を意味する、モダンなフロントエンドビルドツールです。

何ができるのかを簡単に説明します。

💡 Vite はすでに Laravel で使われている

Laravel 9.19 以降、フロントエンドのビルドツールとして Laravel Mix の代わりに Vite が採用されました。

vite.config.jsnpm run dev で Vite を起動する経験がある方は、その同じツールを React 開発でも使います。

React プロジェクトの作成

# Vite を使って React プロジェクトを作成する
# (npm create は npm init と同じで、プロジェクト生成ツールを実行する)
npm create vite@latest my-react-app

# 選択肢が表示される
# ✔ Select a framework: › React
# ✔ Select a variant: › TypeScript  (または JavaScript)
# 作成したプロジェクトに移動
cd my-react-app

# 依存パッケージをインストール
npm install

# 開発サーバーを起動
npm run dev

ターミナルに http://localhost:5173/ と表示されたら成功です。ブラウザで開くと、React のサンプルアプリが表示されます。

生成されるファイル構造

my-react-app/
├── public/              # 静的ファイル(画像など)
│   └── vite.svg
├── src/                 # ソースコード
│   ├── assets/          # CSS, 画像など
│   ├── App.tsx          # メインのコンポーネント(.tsx は TypeScript + JSX)
│   ├── App.css          # App のスタイル
│   ├── main.tsx         # エントリーポイント
│   └── index.css        # グローバルスタイル
├── index.html           # HTML テンプレート
├── package.json         # 依存関係の定義
├── tsconfig.json        # TypeScript の設定
└── vite.config.ts       # Vite の設定

HMR(Hot Module Replacement)の体験

npm run dev

開発サーバーが起動した状態で、src/App.tsx(または src/App.jsx)を編集してみましょう。保存した瞬間に、ブラウザが自動で更新されます。ページ全体のリロードではなく、変更したコンポーネントだけが更新されます。

これが HMR です。ブラウザの「更新ボタン」を押す必要がなくなり、開発体験が劇的に向上します。

本番ビルド

# 本番用ファイルを生成する
npm run build

dist/ ディレクトリに最適化されたファイルが生成されます。

dist/
├── index.html
└── assets/
    ├── index-AbCdEfGh.js    # バンドル・ミニファイされた JS
    └── index-XyZwVuTs.css   # バンドル・ミニファイされた CSS

このファイルをサーバーにデプロイするだけで、本番環境で動作します。


9.4 このコースの振り返り

このコースで学んだ JavaScript の知識が、React でどのように活きるかを整理しましょう。

JavaScript コースで学んだことReact での使われ方
アロー関数コンポーネント定義、イベントハンドラ
分割代入Props の受け取り ({ title, onClick })useState の戻り値 const [count, setCount]
スプレッド構文State の更新 { ...state, newProp: value }
.map()JSX でリストをレンダリング posts.map(post => <Post key={post.id} {...post} />)
.filter()条件に基づくリスト絞り込み
テンプレートリテラル動的な className 生成、URL 構築
addEventListenerReact の onClickonChangeonSubmit
fetch + async/awaituseEffect 内でのデータ取得
render() パターンReact の自動再レンダリング(setState で自動実行)
state 変数 + DOM 手動更新useState + 宣言的 UI(DOM 更新は React が自動化)
イベント委任React が内部で自動処理
localStorageカスタムフックとして抽象化
Promise / async-awaituseEffectuseMutation(React Query)
ES Modules(このチャプター)コンポーネントファイルの import/export
クロージャカスタムフック、コンテキスト
💡 JavaScript は React の土台

すべての JavaScript の知識が React で直接活きます。

React は JavaScript の「上に」構築されたライブラリです。React 固有の概念(JSX、useState、useEffect など)を学ぶ前に、JavaScript をしっかり理解していることが、React 習得の最短ルートです。

このコースを完走したあなたは、その土台をすでに持っています。

特に重要な知識

React を書く上で、特に「毎日使う」JavaScript の知識を確認しておきましょう。

// 1. アロー関数(コンポーネントとイベントハンドラで毎日使う)
const add = (a, b) => a + b;
const greet = name => `こんにちは、${name}さん`;

// 2. 分割代入(Props の受け取りで必須)
const { name, age, email } = user;
const [count, setCount] = useState(0); // React での使い方

// 3. スプレッド構文(State 更新で必須)
const newState = { ...oldState, count: oldState.count + 1 };
const newArray = [...items, newItem];

// 4. .map() でのリストレンダリング(JSX で毎日使う)
const listItems = items.map(item => (
  <li key={item.id}>{item.name}</li>  // JSX の書き方(React コースで学ぶ)
));

// 5. 条件式(JSX 内での条件分岐)
const element = isLoggedIn ? <UserProfile /> : <LoginForm />;
const maybeMessage = hasError && <ErrorMessage text={errorText} />;

// 6. async/await(データ取得で毎日使う)
async function loadPosts() {
  const response = await fetch("/api/posts");
  const data = await response.json();
  setPosts(data); // React の setState
}

9.5 React コースへ

準備は整いました。

このコースを通じて、あなたは以下を習得しました。

React コースでは、次のことを学びます。

トピック概要
Chapter 0: 環境構築Vite で React プロジェクトを作成する
Chapter 1: JSXHTML のような構文で UI を書く
Chapter 2: コンポーネントUI を再利用可能なパーツに分割する
Chapter 3: Props親から子へデータを渡す
Chapter 4: State と useState状態管理を React に任せる
Chapter 5: イベント処理ユーザーの操作に反応する
Chapter 6: useEffectデータ取得、副作用の処理
Chapter 7: フォーム入力フォームの作り方
Chapter 8: リストとキー動的なリストの効率的な描画
Chapter 9: カスタムフックロジックの再利用

React コースの Chapter 0(環境構築)に進みましょう: Reactコース

このコースで体験した「状態管理の壁」が、React でどれほどシンプルに解決されるか、ぜひ確かめてください。Chapter 8 のミニ SNS を React で作り直したとき、「なるほど、これが React の力か」と感じるはずです。


✍ 演習: Vite プロジェクトで ES Modules を試す

Vite でプロジェクトを作り、ES Modules の import/export を実際に動かしてみましょう。

手順:

  1. Vite プロジェクトを作成する(フレームワーク: Vanilla、バリアント: JavaScript)
npm create vite@latest my-modules-practice
cd my-modules-practice
npm install
  1. src/utils.js を新規作成して、以下を実装する
// src/utils.js

/**
 * 価格を日本円形式にフォーマットする
 * @param {number} price - 税抜き価格
 * @returns {string} フォーマットされた価格文字列
 */
export function formatPrice(price) {
  // toLocaleString で 3桁区切りにする
  return `¥${price.toLocaleString("ja-JP")}`;
}

/**
 * 日付文字列を読みやすい形式に変換する
 * @param {string|Date} date - 変換する日付
 * @returns {string} フォーマットされた日付文字列
 */
export function formatDate(date) {
  // Date オブジェクトに変換
  const d = new Date(date);

  // 無効な日付の場合はエラーメッセージを返す
  if (isNaN(d.getTime())) {
    return "無効な日付";
  }

  // 日本語形式でフォーマットする
  return d.toLocaleDateString("ja-JP", {
    year: "numeric",
    month: "long",
    day: "numeric"
  });
}

/**
 * 文字列を指定文字数で切り詰める
 * @param {string} text - 元のテキスト
 * @param {number} maxLength - 最大文字数
 * @returns {string} 切り詰められたテキスト
 */
export function truncateText(text, maxLength) {
  if (text.length <= maxLength) return text;
  return text.slice(0, maxLength) + "...";
}

// 定数もエクスポートする
export const APP_NAME = "練習アプリ";
export const VERSION = "1.0.0";
  1. src/main.js を以下のように書き換える
// src/main.js

// utils.js から必要なものだけをインポートする
import { formatPrice, formatDate, truncateText, APP_NAME } from "./utils.js";

// ---- DOM への表示 ----
const app = document.querySelector("#app");

// 商品リスト(テストデータ)
const products = [
  {
    name: "React 入門書",
    price: 3200,
    releaseDate: "2024-01-15",
    description: "React の基礎から応用まで網羅した、初心者向けの包括的な入門書です。豊富なサンプルコード付き。"
  },
  {
    name: "TypeScript 完全ガイド",
    price: 4500,
    releaseDate: "2023-11-01",
    description: "TypeScript の型システムを徹底解説。実践的なプロジェクトを通じて習得できます。"
  },
  {
    name: "Next.js ハンドブック",
    price: 3800,
    releaseDate: "2024-03-20",
    description: "Next.js 14 の App Router から Server Components まで、最新の機能を網羅的に解説します。"
  }
];

// HTML を生成して表示する
app.innerHTML = `
  <h1>${APP_NAME} - 書籍一覧</h1>
  <ul style="list-style: none; padding: 0;">
    ${products.map(product => `
      <li style="border: 1px solid #ddd; padding: 16px; margin-bottom: 12px; border-radius: 8px;">
        <h3>${product.name}</h3>
        <p>価格: <strong>${formatPrice(product.price)}</strong></p>
        <p>発売日: ${formatDate(product.releaseDate)}</p>
        <p>${truncateText(product.description, 50)}</p>
      </li>
    `).join("")}
  </ul>
`;
  1. 開発サーバーを起動して確認する
npm run dev

確認ポイント:

  • formatPrice() が正しく「¥3,200」形式で表示されるか
  • formatDate() が「2024年1月15日」形式で表示されるか
  • truncateText() が 50 文字で「…」をつけて切り詰めるか

チャレンジ:

  • utils.jscalcTaxIncluded(price, taxRate = 0.1) 関数を追加して、税込み価格を計算する
  • main.js からインポートして、税込み価格も表示する

9.6 まとめ

このチャプターで学んだことを振り返りましょう。

ES Modules

npm

Vite


JavaScript コースを完走したあなたは、React を学ぶ準備が整っています。

前のチャプターで体験した「状態管理の壁」、そしてこのチャプターで学んだ ES Modules と Vite の知識を持って、React コースへ進んでください。

React コースへ進む: Chapter 0 - 環境構築

React では、バニラ JS で 200 行以上かかったミニ SNS が、はるかに少ないコードで、しかもバグなく書けるようになります。あのコメント入力の途中にいいねしても、入力が消えることはありません。ヘッダーの「総いいね数」は、自動でリアルタイムに更新されます。

JavaScript コースで学んだすべての知識が、React コースで花開きます。