コンテンツにスキップ
ブログに戻る

2018年2月5日(月)

Next.js 5: ユニバーサル Webpack、CSS インポート、プラグイン、ゾーン

投稿者

この度、Next.js 5.0 を世界に発表できることを大変嬉しく思います。本日から npm で利用可能です。アップグレードするには、以下を実行してください。

ターミナル
npm i next@latest react@latest react-dom@latest

Next.js のバージョンアップに加え、`react` と `react-dom` のピア依存関係もアップグレードしています。

Next.js は、ユニバーサルでサーバーレンダリング(または静的にプリレンダリング)された React.js アプリケーションのためのツールキットです。どのような規模のアプリケーションであっても、`next` を実行するだけで開発を開始できます。(詳細はこちら)。

新しいリリースごとに、後方互換性を維持し、簡単なアップグレードパスを提供し、API 変更は必要最低限に抑えることをお約束します。Next.js 5.0 も例外ではありません。

しかし、内部的には、強力な新しいユースケースと拡張性を実現するために、Next.js は抜本的な変革を遂げました。まず、Next.js がサーバーコードとクライアントコードの両方でユニバーサルな Webpack パイプラインを共有するようにしました。

ユニバーサル Webpack と Next プラグイン

Next.js は、Webpack、Babel、Uglify のような既存の強力なツールを活用し、エンドユーザーには驚くほどシンプルなインターフェースで提供します。開発は `next`、本番用準備は `next build`、提供は `next start`、または静的ファイルへのプリレンダリングは `next export` です。

初期の決定事項の一つとして、これらのツールの設定方法について非常に強力な拡張ポイントを提供することにしました。使いやすさだけでなく、ツールキットを好きなように拡張できる柔軟性も求めていました。

例えば、`next.config.js` に `webpack` プロパティを設定することで、Next.js の Webpack 設定を拡張できます。

Webpack は本番環境と開発環境で異なる動作をするため、当時、デフォルトの Webpack 設定を装飾する関数としました。

next.config.js
module.exports = {
  webpack(config, { dev }) {
    // modify it!
    return config;
  },
};

オプションの `next.config.js` ファイルの例

しかし、Webpack はクライアント(ブラウザ)バンドルでのみ実行されるため、サーバーレンダリングのためにこの素晴らしいツールチェーンを使用する可能性を逃していました。

コードベースを広範囲にリファクタリングし、Webpack をユニバーサルに動作させる ことを発表できることを嬉しく思います。

あなたの視点からは、変更されるのは、上記のデコレータ関数に `isServer` プロパティが追加されるだけです。しかし、新しいセマンティクスは、Webpack ローダーの広範なエコシステムが利用可能になることを意味します。

CSS、LESS、SASS、SCSS、CSS Modules

最も要望の多かった機能の1つは、CSS ファイルをインポートし、Webpack ローダーを活用できる機能です。

import './index.css';
 
export default function Index() {
  return (
    <div>
      <p>I love CSS!</p>
    </div>
  );
}

ユニバーサル Webpack により、CSS インポートが可能なページ(`pages/index.js`)の例

これを機能させるには、必要なローダーをピア依存関係として導入します。

ターミナル
npm i --save css-loader style-loader postcss-loader

Next.js では、必要なローダーを自由に選択し、必要に応じてバージョンアップすることができます。

そして、`next.config.js` でローダーを設定します。

next.config.js
module.exports = {
  webpack(config, options) {
    const { dev, isServer } = options;
    const extractCSSPlugin = new ExtractTextPlugin({
      filename: 'static/style.css',
      disable: dev,
    });
    config.module.rules.push({
      test: /\\.css$/,
      use: cssLoaderConfig(extractCSSPlugin, {
        cssModules,
        dev,
        isServer,
      }),
    });
    return config;
  },
};

生の Webpack 設定を拡張することで、大きな柔軟性と制御が可能になります。

コンポーネントローカルスタイリングソリューション(組み込みの `styled-jsx` babel プラグインなど)の使用を一般的に推奨しますが、CSS ローダーは既存の CSS コードベースを簡単に再利用でき、古いコードベースを Next.js に移行するのを大幅に簡素化するなど、多くの重要な利点があると考えています。

デフォルトですべての機能やローダーを有効にするのではなく、設定を自動的に拡張するNext.js プラグインを紹介します。上記のように手動で設定を拡張してローダーを設定する代わりに、次のようにできます。

const withCss = require('next-css');
module.exports = withCss({
  /* extra optional config */
});

`.css` ファイルのインポートを有効にするには、`next-css` を導入するだけです。

Next.JS でのCSS ローダーの使用方法に関する詳細、または既に作成済みのパッケージを参照してください。

ローダーパッケージ
CSSnext-css
LESSnext-less
SASSnext-sass

私たちの目標は、コミュニティが実用的でシンプルな拡張機能のエコシステムを開発・成長させることを支援することです。そのために、next-plugins モノレポを Next.js コミュニティが管理できるように公開します。すべての PR を歓迎します!

TypeScript サポート

JavaScript エコシステムで最も急成長しているテクノロジーの1つは TypeScript です。それほどまでに、Babel 7 で公式にサポートされるようになり、Next.js でも `.babelrc` をカスタマイズするだけで自然にサポートされます。

しかし、新しいユニバーサル Webpack サポートのおかげで、本日すぐに完全な TypeScript サポートを得ることができます!

Webpack 設定を次のように拡張できます。

next.config.js
module.exports = {
  webpack(config, options) {
    const { dir, defaultLoaders } = options;
    config.resolve.extensions.push('.ts', '.tsx');
    config.module.rules.push({
      test: /\\.+(ts|tsx)$/,
      include: [dir],
      exclude: /node_modules/,
      use: [
        defaultLoaders.babel,
        { loader: 'ts-loader', options: { transpileOnly: true } },
      ],
    });
    return config;
  },
};

`ts-loader` を有効にするだけで済みます。

CSS ローダーやプリプロセッサと同様に、TypeScript も最も要望の多かった機能の1つです。他のローダーと同じくらい簡単にプロジェクトに組み込めるように、`next.config.js` ファイルに含めることができる `next-typescript` プラグインを用意しました。

next.config.js
const withTs = require('next-typescript');
module.exports = withTs({
  /* additional config*/
});

プラグインは簡単に合成できます。それらは単なる関数です。

React Altlibs & モジュールオーバーローディングのサポート向上

React のドロップイン代替実装は数多く登場しています。その中でも注目すべきは [preact](https://preact.dokyumento.jp/)、nervjsinfernoなどです。

DOM レンダラーを置き換えることに焦点を当てた他のライブラリもあります。たとえば、react-dom-liteは、ブラウザ互換性に多少のトレードオフをもたらすことで、より小さな React ビルドを目指しています。

ユニバーサル Webpack サポートにより、これらのライブラリをドロップイン置換として組み込むプロセスがさらに容易になりました。他のプラグインと同様に、Next.js と preact を使用するには、次のことを行うだけです。

ターミナル
npm i @zeit/next-preact preact preact-compat

preact プラグインと必要なピア依存関係をインストールします。

const withPreact = require('@zeit/next-preact');
module.exports = withPreact();

preact 用に準備された新しい `next.config.js`

非常にシンプルな `@zeit/next-preact` モジュールをチェックするか、独自のモジュールを作成してください!

本番環境でのオプションの外部ソースマップ

Webpack がクライアントとサーバーの両方のコードで Next.js によって使用されるようになったため、本番ビルドでソースマップを有効にするには、設定にわずかな調整を加えるだけです。

開発中はソースマップが自動的に有効になるため、本番環境では異なる設定を行います。

next.config.js
module.exports = {
  webpack(config, { dev }) {
    if (!dev) {
      config.devtool = 'source-map';
    }
    return config;
  },
};

開発時以外は `devtool` オプションを設定するだけです。

ゾーン

当初から Next.js の目標の1つは、Web のシンプルさを取り戻し、維持することでした。

サーバーレンダリング、シンプルでアグノスティックなデータ取得アプローチ、ファイルシステム構造に基づいた宣言型ページは、この考え方に沿って導入された機能の一部です。

Web サービスや Web サイトの、しばしば見落とされがちな側面は、それらがどれほど自然に合成可能でスケーラブルであるかということです。

例えば、`mydomain.com/settings` と `mydomain.com/` は、独立してデプロイ、独立してスケール、さらには同じソフトウェアの異なるバージョンを実行することもできる、全く異なる2つのアプリである可能性があります。

エンドユーザーにとって統一された体験を「接着」させるために必要なのは、それらを世界に公開するバックエンドルーティングレイヤーやロードバランサーの簡単な設定だけです。Next.js で構築された複数のアプリケーションを合成する機能、そしてそれを通常の `` コンポーネントで接続する機能が、ついに実現しました。この機能をゾーンと呼びます。

例として、Vercel にデプロイされた2つの独立した Next.js アプリケーションを考えてみましょう。

Both of our pages have a seamless experience, but they belong to separate apps
私たちのページはすべてシームレスな体験を提供しますが、それぞれが独立したアプリです。

ドキュメントの「ミニウェブサイト」をリニューアルした際、コミュニティからの貢献を可能な限り容易に受け入れられるようにしたいと考えました。

ドキュメントの「ミニウェブサイト」を独自のレポジトリに分割しました。さらに、プルリクエストが送信され変更が提案されるたびに、それを個別に自動デプロイします。

Every time a change happens inside a PR our bot automatically deploys it
PR 内で変更が発生するたびに、ボットが自動的にデプロイします。

最終的に、これらは2つのゾーンとなり、パスエイリアス機能を使用して親ドメイン `https://vercel.com` に統合されました。それは次のようなものになります。

{
  "rules": [
    { "pathname": "/docs", "dest": "our-docs.vercel.app" },
    { "pathname": "/api", "dest": "our-docs.vercel.app" },
    { "dest": "my-main-website.vercel.app" }
  ]
}

これらの単純なルールにより、マイクロサービスとゾーンを構成できます。

あとは `now alias` コマンドを呼び出すだけです。

ターミナル
now alias -r rules.json my-domain.com

私たちの使命は、デプロイメントを可能な限りユニバーサルでオープンにすることです。ローカル開発を支援するために、最近 `micro-proxy` をオープンソース化しました。これは、上記と同じ設定形式で動作するツールです。

同様に、Nginx、HAProxy、API Gateway のような他のソリューションとゾーンを結合することもできます。

本番ビルド時間の短縮

開発者体験とユーザー体験は密接に関係していると考えています。変更を記述、テスト、デプロイする効率が上がるほど、新機能の追加、バグ修正、全体的なユーザー体験の向上も早くなります。

したがって、システムを構成する最も基本的な要素のパフォーマンスプロファイルを継続的に改善することに注力しています。

Next.js 5.0 では、本番環境にデプロイする前、または Next.js アプリケーションを静的サイトとしてエクスポートする前に実行するコマンドである `next build` を見直す機会がありました。

数千のコンポーネントで構成される React アプリケーションである vercel.com では、Next.js 5.0 によって本番ビルド時間が 23.6% 短縮されるなど、劇的な改善が見られました。

Our main application production build now takes 38 fewer seconds to complete
私たちのメインアプリケーションの本番ビルドは、現在 38 秒短縮されます。

動的インポートのキャッシュ改善

動的 `import()` を使用すると、Webpack は新しいコード分割エントリポイントが存在することを示します。

ビルド時には、対応するモジュールのサブツリーに対応する特定のバンドルを生成することを意味します。

Next.js 5.0 より前の動的バンドルは、次のような URL を使用していました。

/_next/1517592683901/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6.js

現在、動的インポートをサブツリーの内容のコンテンツアドレス可能なハッシュに変換しました。

/_next/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6-b7874680a9e21fb6eb89.js

これは、デプロイメント全体で、ユーザーや顧客が既に利用したコードを不必要に再ダウンロードする必要がなくなることを意味します。

フラグメント

Next.js は、各ページと共にサーバーレンダリングされるトップレベルの `` コンポーネントをビルドします。このコンポーネントをオーバーロードすることで、マークアップを完全に制御でき、多くの高度なユースケースが可能になります。

初期マークアップの一部は、Next.js がクライアントサイドで評価する必要のあるスクリプトのリストです。カスタム `_document` は次のようになります。

pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document';
export default class extends Document {
  render() {
    return (
      <html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

`Document` を使用すると、ページのサーバーレンダリングされた出力全体をカスタマイズできます。

最近まで、スクリプトを `

` でラップする必要がありました。

Next.js 5.0 では、新しい `Fragment` サポートを活用することで、より軽量なページと、余分なマークアップなしでページのスタイリングを完全に制御できるようになりました。

より正確なエラー

Node.js はソースマップをサポートしていないため、サーバー側で発生したエラーには、コンパイルされたコードを指すスタックトレースが伴います。

Next 5 では、サーバー側のソースマップサポートを改善しました。サーバーレンダリング中に発生したエラーは、正しい関数と行番号を指すようになりました。

Errors now show the correct line, file and function name
エラーは、正しい行、ファイル、関数名を表示するようになりました。

結論

ユニバーサル Webpack は Next.js の基盤を強固にし、将来性も高めます。全体として、どのプラグインやローダーが Next.js に適用可能で、どれがそうでないかという人工的な分離はもはやありません。

ゼロコンフィギュレーションの精神で、Next.js の機能を自動的に拡張するレシピのコミュニティリポジトリであるNext Pluginsを導入できることを嬉しく思います。具体的な設定を変更する必要はありません。

これにより、CSS ソリューション、TypeScript のようなコンパイル言語、Nerve のような React の代替手段(Nerveなど)の全範囲を、追加モジュールを導入し、`next.config.js` で明示的に含めるだけでサポートできます。複雑さのないシンプルさ。

ゾーンは、同じリポジトリやサーバーに属していない Next.js アプリケーションを相互接続できます。これは、「チームの拡張性」カテゴリの改善において非常に重要なマイルストーンと考えています。

これにより、Next.js は、複数のチームによって保守される大規模アプリケーションの優れた候補となります。チームは現在、並行して改善をデプロイでき、エラーサーフェスを削減し、イテレーション速度を向上させ、コア技術に加えて、多くの異なるアプローチ(状態管理やデータ取得など)を試すこともできます。

この機能の設計につながった重要な洞察、コード、およびテストを提供してくれた Deep Varma とTrulia エンジニアリングチームに感謝いたします。

いつものように、このリリースは多くのオープンソース貢献者と素晴らしいコミュニティなしには実現できませんでした。