序章

Next.js (React)および Nuxt.js (Vue)は、アプリビューをサーバーにレンダリングするプロセスを合理化するのに役立ちます。

開発に対応する方法でReactコンポーネントにCSSをレンダリングするためのソリューションが引き続き必要です。 また、スタイルを使用できるように、サーバーでCSSをレンダリングするためのソリューションも必要になります。

この記事では、CSSのレンダリングの課題について説明し、Next.jsプロジェクトのサーバーでstyled-componentsstyled-jsxを使用します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、ノードv16.2.0、npm v7.14.0、react v17.0.2、react-dom v17.0.2、next v10.2.3、およびstyled-compnentsv5.3.0。

Reactのスタイルパターンを理解する

ReactでCSSを作成する一般的な方法はいくつかありますが、すべて機能します。 状況に応じて、次のいずれかの方法でReactアプリにスタイルを適用します。

グローバルスタイル

グローバルスタイルは、ローカルまたはコンテンツ配信ネットワーク(CDN)でホストされるスタイルシートを含めるパターンです。

次に例を示します。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />

これは、コンポーネントの再利用を促進しないため、あまり好ましくないスタイリングパターンです。 また、スタイルの構成を奨励しません。

これらのCSSルールは、特定の要素またはコンポーネントに直接スコープされていません。 コンポーネントがインポート、ネスト、または拡張によって予期されていなかった方法で相互作用する場合、競合が発生する可能性が高くなります。

ただし、フォントの組み込みやCSSのリセットやデフォルトなど、スタイルをグローバルに含めることができる場合があります。

インラインスタイル

インラインスタイルは、React styleプロパティを使用して、DOM要素またはReactコンポーネントに直接適用されます。 実装はHTMLインラインstyle属性によく似ていますが、JavaScript element.styleAPIを使用します。

次に例を示します。

const titleStyle = {
  fontSize: '4rem';
  lineHeight: '1.6';
  color: '#222';
}

<h1 style={titleStyle} {...props}>{props.children}<h1>

このパターンは、スタイルとコンポーネントの構成、および再利用を促進します。

ただし、インラインスタイルでは、疑似クラスとターゲット疑似要素を使用してフォーカス状態のホバーを処理する方法は提供されません。 大規模で複雑なプロジェクトの場合、より堅牢なソリューションが必要になる場合があります。

コンポーネントスタイル

コンポーネントスタイルは、再利用を促進し、スタイルとコンポーネントをより適切に構成するのに役立ちます。 これを実現するには、ユーティリティまたはライブラリが必要です。 スタイルローダー、スタイルコンポーネントグラマーなど。 コンポーネントスタイルをサポートするためのツールの例です。

styled-componentsは、CSS-in-JSを実装するための一般的なソリューションです。 これらはインラインコンポーネントスタイルですが、複雑な(疑似)選択、ネストなどを実行するためのより強力な機能を備えています。

ステップ1-プロジェクトの設定

開始するには、ターミナルを開き、プロジェクト用の新しいフォルダを作成します。

  1. mkdir css-ssr-next-example

次に、新しいプロジェクトディレクトリに移動します。

  1. cd css-ssr-next-example

次に、次のコマンドを実行して初期化します。

  1. npm init -y

これにより、依存関係の追跡に使用するpackage.jsonファイルが作成されます。

次に、Next.js、React、およびReactDOMをインストールします。

  1. npm install next@10.2.3 react@17.0.2 react-dom17.0.2

この時点で、Next.js、React、およびReactDOMを使用した新しいプロジェクトが作成されます。

注:公開以来、Next.jsプロジェクトを作成するためのより最新のアプローチでは、create-next-appを利用できます。

次に、package.jsonを更新して、devスクリプトでNext.jsアプリを起動します。

package.json
{
  "name": "css-ssr-next-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "next"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^10.2.3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }
}

次に、pagesディレクトリを作成します。

  1. mkdir pages

このディレクトリに、次の内容のindex.jsファイルを追加します。

pages / index.js
import React from 'react';

const Index = () => <h1>Hi, new Next.js project</h1>;

export default Index;

次に、devスクリプトを実行して、サーバーを起動します。

  1. npm run dev

Webブラウザでlocalhost:3000を開くと、次のことがわかります。

Output
Hi, new Next.js project

まず、NextのHeadコンポーネントを使用して、normalize.cssを使用してスタイルを正規化します。

pages / index.js
import React from 'react';
import Head from 'next/head';

const Index = () =>
  <div>
    <Head>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />
      <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet" />
    </Head>
    <h1>Hi, new Next.js project</h1>
  </div>;

export default Index;

次に、Next.jsプロジェクトのルートにstaticフォルダーを作成します。

  1. mkdir static

このディレクトリに、次のCSSコンテンツを含むbase.cssファイルを追加します。

static / base.css
body {
  font-family: 'Raleway', sans-serif;
  color: #222;
}

base.cssファイルをインデックスページにインポートします。

pages / index.js
// ...

    <Head>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />
      <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet" />
      <link rel="stylesheet" href="/static/base.css" />
    </Head>

// ...

ブラウザでは、フォントがデフォルトのフォントからRalewayに表示されます。

この時点で、グローバルスタイルのNext.jsプロジェクトができました。

ステップ2–スタイル付きコンポーネントの使用

styled-componentsライブラリを使用してコンポーネントのスタイルを設定しましょう。

まず、styled-componentsをインストールします。

  1. npm install styled-components@5.3.0

次に、componentsディレクトリを作成します。

mkdir components

このディレクトリに、Button.jsファイルを追加し、次のようにファイルを更新します。

components / Button.js
import React from 'react';
import styled from 'styled-components';

const ButtonBase = (props) => <button {...props}>{props.children}</button>
const Button = styled(ButtonBase)`
  /* Rectangle 2: */
  background: #0077E2;
  box-shadow: 0 2px 7px 0 rgba(120,137,149,0.25);
  border-radius: 3px;
  text-transform: uppercase;
  padding: 10px;
  color: #fff;
  border: #0077E2;
`

export default Button;

styledとしてインポートされたstyled-componentsを使用して、Buttonのスタイルを設定しています。 ButtonBaseはボタンのスケルトンを返し、Buttonはスタイルが設定されたButtonBaseのコンポーネントを返します。

インデックスページのButtonコンポーネントをインポートして使用します。

index.js
import React from 'react';
import Head from 'next/head';
import Button from '../components/Button'

const Index = () => <div>
  <Head>
    ....
  </Head>
  <h1>Hi, new Next.js project</h1>
  <Button>Clicker</Button>
</div>;

export default Index;

変更を保存し、ブラウザでアプリケーションを観察します。 テキストの下には、新しいスタイルのボタンがあります。

ただし、ハードリフレッシュ後、ボタンのスタイルは期待どおりになりません。

ボタンのスタイルが欠落しているように見えますが、どういうわけか、基本のスタイルはそのままです。 Web開発ツールを使用して、ページソースを検査します。

コンテンツはサーバーにレンダリングされますが、ボタンに関連するページのどこにもスタイルは表示されません。 一方、適用されたフォントスタイルからわかるように、外部ファイルがサーバーに正常にレンダリングされたことがわかります。

では、コンポーネントスタイルの何が問題になっているのでしょうか。 コンソールを見てください:

Output
Warning: Prop `className` did not match. Server: "sc-gtsrHT kbmjhF" Client: "sc-bdnxRM kbyRfM"

クラス名の不一致を示すエラーが表示される場合があります。 これは、リロードすると、コンテンツが最初にサーバーからフェッチされるためです。

残念ながら、styled-componentsはサーバーにレンダリングされないため、その時点では使用できません。

注:公開以来、styled-componentsサーバー側レンダリングをサポートしています。

Next.jsのwith-styled-componentsの例も参照できます。

解決策を見てみましょう。

ステップ3–スタイル付きJSXの使用

Next.jsに取り組んでいるチームは、この問題を軽減するために、styled-jsxというライブラリを導入しました。 ライブラリは、作成したスタイルが、ブラウザだけでなく、サーバーとブラウザでレンダリングされることを保証します。

すでにNext.jsにバンドルされているので、何もインストールする必要はありません。

Buttonコンポーネントに再度アクセスし、styled-jsxを使用するように変更します。

components / Button.js
import React from 'react';

const Button = props => (
  <button {...props}>
    {props.children}
    <style jsx>{`
      background: #0077e2;
      box-shadow: 0 2px 7px 0 rgba(120, 137, 149, 0.25);
      border-radius: 3px;
      text-transform: uppercase;
      padding: 10px;
      color: #fff;
      border: #0077e2;
    `}</style>
  </button>
);

export default Button;

スタイリングするコンポーネントのルート要素の終了タグの前に、jsx属性を使用してstyle要素を作成できます。 style要素で、テンプレート文字列を含む中括弧を開きます。 文字列は有効なCSSスタイルであり、コンポーネントスタイルとしてサーバーである必要があります。

コンポーネントが1つの要素のみで構成されていない場合は、styled-jsxでセレクターを使用することもできます。

components / TitleAndButton.js
import React from 'react';

const TitleAndButton = props => (<div {...props}>
  <h1 className="title">Hi Title</h1>
  <button>Clicker</button>
  <style jsx>{`
    h1.title {
      color: #222
    }
    button {
      background: #0077e2;
      box-shadow: 0 2px 7px 0 rgba(120, 137, 149, 0.25);
      border-radius: 3px;
      text-transform: uppercase;
      padding: 10px;
      color: #fff;
      border: #0077e2;
    }
  `}</style>
</div>);

export default TitleAndButton;

これで、[インデックス]ページのTitleAndButtonコンポーネントをインポートして使用できます。 両方の要素が期待どおりにスタイル設定されます。

注:Next.jsのwith-styled-jsxの例も参照できます。

styled-jsxは、Next.jsプロジェクトでサーバーにスタイルをレンダリングするための1つのアプローチです。

結論

この記事では、CSSのレンダリングの課題について学び、Next.jsプロジェクトのサーバーでstyled-componentsstyled-jsxを使用しました。

Next.jsについて詳しく知りたい場合は、 Next.js入門テクニカルトークを試すか、Next.jsトピックページで演習とプログラミングプロジェクトを確認してください。 。