Development

【React-Redux】webpackでCSSを読み込んで反映編 [初心者入門5日目]

React × Reduxの連載記事の5回目です。

前回はMaterial-uiの実装を行い、デザインの変更を行いました。

今回もデザイン関連の設定になりますが、cssファイルを作成して、webpackで読み込んで反映させる方法を書きたいと思います。

複数のcssファイルが作成されたときのことを考慮して、bundle時に1つのcssにまとめて出力するようにしてみたいと思います。

この記事でわかること
  • webpackでのcssの読み込み方法
  • cssを結合してbundleする方法
  • cssを圧縮して最小化させる方法

この記事のターゲットとなる環境

現在の環境状況や前提条件を書いておきます。

前提条件
  • Mac OS Catalina ver 10.15.3
  • 4日目の記事内容まで理解している
  • 環境ターゲット(2020-03-12現在最新)
    • React16.13
    • Redux7.2
    • webpack4.42
    • babel7.8.7
    • eslint6.8
    • Material-UI4.9.5

これまでのReact × Reduxの連載記事はこちらからどうぞ

【2020年版】React × Redux 初心者入門 React × Redux の初心者向け連載記事です。 ネットで調べると古いバージョンの記事が溢れているので、できるだけ最新環境...

必要なnodeモジュールをインストールする

cssを反映させるために、以下のモジュールをインストールします。

  • css-loader
  • mini-css-extract-plugin
  • optimize-css-assets-webpack-plugin
  • terser-webpack-plugin

css-loader

cssをjavascriptで読み込めるようにするローダーです。

以下のコマンドでインストールしてください。

$ npm install --save-dev css-loader

mini-css-extract-plugin

mini-css-extract-pluginはcssを1つのファイルにまとめて出力するためのモジュールです。

以下のコマンドでインストールしてください。

$ npm install --save-dev mini-css-extract-plugin 

webpack v3まではextract-text-webpack-pluginというモジュールを使っていましたがv4からはmini-css-extract-pluginになりました。

optimize-css-assets-webpack-plugin

OptimizeCSSAssetsPluginはCSSを最小化させるためのプラグインです。

$ npm install --save-dev optimize-css-assets-webpack-plugin

terser-webpack-plugin

TerserPluginはJSファイルを圧縮して最小化させるためのプラグインです。

後述するwebpack.config.jsで、cssのminimizerを記述するのですが、同時にjsのminimizerもさせる必要があるため、今回一緒にインストールしておきます。

$ npm install --save-dev terser-webpack-plugin

これで必要なモジュールのインストールは完了です。

bundle.js内に<style>タグとして読み込ませてしまい、cssファイルを別に出力しないようにするのが主流かもしれませんが、今回はあえてcssファイルを別で出力するようにしたいと思います。

webpackのcss周りについてはいろんなpluginがあり、各プラグインでもオプションが多く存在して様々な機能があるので、一度ちゃんと調べてみることをオススメします。 

cssファイルを作成する

src/css/main.cssファイルを作成します。

ブラウザの規定でbodyに余分なmarginがあったりしたので、そのあたりを削除してみます。

また、スタイルとしてdiv用のセンタリングスタイルとしてcenterTableというスタイルを作ってみましょう。

html {
    font-size: 13px;
    line-height: 20px;
    color: #5b5b5b;
    min-height: 100%;
    position: relative;
}

body {
    margin: 0px;
    min-height: 100%;
    background-color: #FAFAFA
}

.centerTable {
    margin: 0 auto 0 auto;
    display: table;
}

cssファイルを反映させる

src/index.jsxの修正

index.jsxでcssファイルをインポートしておきます。

// cssインポート
import './css/main.css';

全体像はこちら

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

// cssインポート
import './css/main.css';
import stores from '../stores';
import App from '../container/AppContainer';

const render = () => {
  ReactDOM.render(
    <Provider store={stores}>
      <div>
        <App />
      </div>
    </Provider>,
    document.getElementById('root'),
  );
};

render();

components/CounterComponent.jsxの修正

Counter.jsxのカウント値が表示されるところと、ボタンのところをセンタリングさせたいと思います。

対象箇所をdivでくくり、作成したcssのcenterTableスタイルを当てます。

components/CounterComponent.jsx

import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import AddIcon from '@material-ui/icons/AddCircle';
import RemoveIcon from '@material-ui/icons/RemoveCircle';

const useStyles = makeStyles((theme) => ({
  button: {
    margin: theme.spacing(1),
  },
}));

const CounterComponent = (props) => {
  const { counter, counterActions } = props;
  const classes = useStyles();

  return (
    <div style={{ width: '100%' }}>
      <div className="centerTable">
        <h2>
          count={counter.value}
        </h2>
        <Button
          className={classes.button}
          variant="contained"
          color="primary"
          startIcon={<AddIcon />}
          onClick={() => counterActions.increment()}
        >
          増加
        </Button>
        <Button
          className={classes.button}
          variant="contained"
          color="secondary"
          startIcon={<RemoveIcon />}
          onClick={() => counterActions.decrement()}
        >
          減少
        </Button>
      </div>
    </div>
  );
};

CounterComponent.propTypes = {
  counter: PropTypes.shape({
    value: PropTypes.number,
  }).isRequired,
  counterActions: PropTypes.shape({
    increment: PropTypes.func,
    decrement: PropTypes.func,
  }).isRequired,
};

export default CounterComponent;

webpack.configの修正

cssを読み込めるようにloadersの追加とpluginの追加を行います。

必要なモジュールを読み込みます

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

MiniCssExtractPluginの追加

読み込んだcssを出力する設定の記述を追記します。

plugins: [
    // MiniCssExtractPluginのインスタンスを追記
    // ここで設定するfilenameは出力するファイル名
    new MiniCssExtractPlugin({
      filename: 'css/bundle.css',
    }),
  ],

css-loaderの追加

cssファイルのload処理を追記します。

module: {
    rules: [
        {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },

optimization.minimizerの追加

cssファイルの出力時に、圧縮して最小化する設定を追記します。

 optimization: {
    minimizer: [new TerserPlugin({}), new OptimizeCSSAssetsPlugin({})],
  },

cssの圧縮はOptimizeCSSAssetsPluginで行いますが、optimization.minimizerを記述するとjsの圧縮も有効になってしまうので、TerserPluginも記述しています。

これで、ソース上で参照しているcssが読み込まれ、bundle時にはcss/bundle.cssというファイルに纏められて出力されるようになります。

HtmlWebpackPluginも使用しているので、作成されるindex.htmlには出力されたcss/bundle.cssへのLinkタグが動的に挿入されます。

webpack.configの全体像

全体的にはこんな感じになりました。

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  context: __dirname,
  entry: [
    './src/index.jsx',
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.jsx$/,
        enforce: 'pre',
        exclude: /node_modules/,
        loader: 'eslint-loader',
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: ['babel-loader'],
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      inject: 'body',
    }),
    new webpack.LoaderOptionsPlugin({
      options: {
        eslint: {
          configFile: './.eslintrc',
        },
      },
    }),
    // MiniCssExtractPluginのインスタンスを追記
    // ここで設定するfilenameは出力するファイル名
    new MiniCssExtractPlugin({
      filename: 'css/bundle.css',
    }),
  ],
  optimization: {
    minimizer: [new TerserPlugin({}), new OptimizeCSSAssetsPlugin({})],
  },
};

実行してみる

npm startで実行してみましょう。

以前の状態

今回の状態

無事センタリングされていたらcssの反映は完了です。

buildしてみる

npmスクリプトのbuildタスクを実行してみると、生成ファイルがdistディレクトリに出力されるようになっています。

以下のコマンドをターミナルから出力してみてください。

$ npm run build

distディレクトリが生成されて中に以下のファイルが作成されたと思います。

index.htmlの内容を見てみると、出力されたbundle.cssのリンクが動的に作成されているのがわかると思います。

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>React-Redux Boilerplate</title>
<link href="/css/bundle.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/bundle.js"></script></body>
</html>

webpackでCSSを読み込んで反映編

今回はcssをwebpackで読み込んで、最小化出力の方法を書いてみました。

cssファイルを別出しにしましたし、cssメタ言語のsassやlessは使用しませんでした。

もし使用する場合でもwebpackのloaderで問題なく対応できると思います。

webpack関連のプラグインはバージョンが変わるごとに変更になったり、様々な種類があるので、ちょっと複雑な感じがしちゃいますが、基本的には公式サイトに書いてあったりするので、正しいバージョンとモジュールの組み合わせさえ注意すれば問題なく実装できると思います。

次回予告

次回予告というかやろうと思ってること

  • Redux Toolkitから作成するDucksパターンで実装する完了
  • ESLintでAirbnbのスタイルガイドを実装する完了
  • Materialデザインを実装する完了
  • カスタムCSSを実装する完了
  • ページルーティングを実装する
  • 開発環境の効率化をするためにHotReloadとソースマップを実装する
  • REST通信を実装する
  • ログイン制御のフローを実装する
  • デプロイ方法を検討、実装する

次の記事はこちら

【React-Redux】react-routerでルーティングを実装編 [初心者入門6日目] React×Reduxの連載記事の6回目です。 今回はページングの処理を実装していきたいと思います。 前回の状態からサンプ...

勉強するならプログラミングスクールがベスト

今からプログラミングを勉強してプロのエンジニアを目指すなら、プログラミングスクールで勉強するのがベストです!

未経験からプロのエンジニアを育てるオンラインブートキャンプ

最短4週間で未経験からプロを育てるオンラインスクールなので、自宅からプログラミングやアプリ開発を学ぶことができます。


オススメ本

[itemlink post_id=”534″ klabel=Kindleで探す alabel=Amazonで探す rlabel=楽天市場で探す ylabel=Yahoo!ショッピングで探す]

[itemlink post_id=”541″ klabel=Kindleで探す alabel=Amazonで探す rlabel=楽天市場で探す ylabel=Yahoo!ショッピングで探す]

[itemlink post_id=”540″ klabel=Kindleで探す alabel=Amazonで探す rlabel=楽天市場で探す ylabel=Yahoo!ショッピングで探す]

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です