LoginSignup
37
47

More than 3 years have passed since last update.

webpackとBabelの基本を理解する(5) ―Sass編―

Last updated at Posted at 2019-03-03

独学の内容をまとめたものです。誤りがございましたら、ご連絡いただけると幸いです。

リンク

  1. webpackとBabelの基本を理解する(1) ―webpack編―
  2. webpackとBabelの基本を理解する(2) ―Babel編―
  3. webpackとBabelの基本を理解する(3) ―webpackとBabel編―
  4. webpackとBabelの基本を理解する(4) ―React編―
  5. webpackとBabelの基本を理解する(5) ―Sass編―(本記事)

概要

この記事の概要

  • 目的
    • フロントエンドの環境構築に利用されるツールへの理解を深める
  • 本記事のゴール
    • SassのコードをコンパイルしてHTMLに埋め込む方法を知る
    • バンドルされたCSSファイルを別ファイルに切り出す方法を知る
  • 対象者
    • WEBフロント担当者
    • HTML,CSS,JavaScript(es2015含む)の基本的な構文を理解している人
    • npmの利用方法を理解している人
  • 環境・バージョン
    • Windows10
    • Node.js(推奨版) 10.15.01
    • npm 6.4.1
    • webpack 4.29.6
    • webpack-cli 3.2.3
    • css-loader 2.1.0
    • node-sass 4.11.0
    • sass-loader 7.1.0
    • style-loader 0.23.1
    • mini-css-extract-plugin 0.4.5

Sass

Sassそのものの説明は割愛させていただきます。。。

Sass
Sass | Wikipedia

Dreamweaverでは標準でSassコードのコンパイル機能があるそうですね。VSCodeも便利なアドオンがあるようで。

インストール

webpackでJavaScriptとJSON以外のファイルを扱いたい場合は、対応するloaderが必要でしたね。以下4つのパッケージをインストールします。

  • style-loader: CSSを<style>タグでHTMLに埋め込む
  • css-loader: CSSファイルをJS用モジュールに変換する
  • sass-loader: SassファイルをCSSに変換するツールを呼び出す
  • node-sass: Sassをコンパイルするためのツール
$ npm install style-loader css-loader sass-loader node-sass --save-dev

CSS・Sassをバンドルする

webpack.config.jsの記述

webpack.config.jsに、CSS・Sassファイルをバンドルする際のルールを追記します。

module.exports = {
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /node_modules/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { url: false }
          },
          'sass-loader'
        ]
      }
    ]
  }
};

対象ファイルは、.css, .sass, .scssです。sass-loader → css-loader → style-loader の順に実行されます。

options.url
CSS内に画像への参照url(image.png)が含まれる場合、それもバンドルの対象となります。画像ファイルをbase64でエンコードするとかでは無い限り、必要な無いので、options.urlfalseを指定して対象外とします。(初期値はtrue)

JSファイルとの接続

webpackは、起点となるJSファイルからimport文を頼りに芋づる式にファイルをつなげていくツール、でしたね。ということは、起点ファイルとCSSファイルの接点を作る必要があります。entryに指定しているJSファイルに、CSSファイルのインポート文を記述します。

import 'file.scss';

CSS In JavaScript
sass-loaderでCSSに変換され、css-loaderでJSモジュールに変換されたデータは、style-loaderによって、<style>タグでHTMLのhead内に埋め込まれます。
正確には、バンドル後のJSファイルにstyle情報とstyleタグを埋め込む処理が追加されます。その為、JSファイルを読み込んだだけでCSSファイルの情報も反映されるようになります。

css-loader | webpack
style-loader | webpack
sass-loader | webpack

CSSファイルを分離する

Plugins

開発中はstyleタグで埋め込みでも良いが、最終的にはCSSファイルは別にしたいという場合も多いと思います。その場合は専用のプラグインを利用します。
これまでに登場したLoaderは、特定のタイプのデータをJSのモジュールに変換する役目を果たします。それ以外のことをやりたい場合は、プラグイン(Plugins)を使って実現します。

Plugins | webpack

MiniCssExtractPluginのインストール

「MiniCssExtractPlugin」を使って分離手術を実施したいと思います。

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

MiniCssExtractPlugin | webpack

webpack.config.jsの設定

webpack.config.jsの記述例はこちら。

# ディレクトリ構成
sample/
  ├ src/
  │   ├ scss/
  │   │   ├ base.scss
  │   │   └ index.scss
  │   └ index.js 
  ├ dist/
  └ webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'js/sample.js'  // /dist/js/sample.jsに出力
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /node_modules/,
        use: [
          MiniCssExtractPlugin.loader, // style-loaderの代わり
          {
            loader: 'css-loader',
            options: { url: false }
          },
          'sass-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/sample.css',  // /dist/css/sample.cssに出力
    })
  ]
};

冒頭でmini-css-extract-pluginをインポートしています。webpack.config.jsはNode.jsのファイルなのでrequire文です。
style-loaderの代わりにMiniCssExtractPlugin.loaderを利用するようにしています。

plugins
loaderで捕捉したCSSモジュールを別ファイルに切り出すため、インポートしたプラグインのインスタンスをpluginsに設定します。
コンストラクタに出力したいファイル名をfilenameで渡します。出力先フォルダはoutput.pathで指定しているフォルダです。ここでは、/distの配下に/js/cssでそれぞれフォルダが分かれて出力されるようにしています。

SCSSファイルをコンパイルして出力する

フォルダとファイルの準備

上記のディレクトリ構成で、JSファイルとSCSSファイルを準備します。

JSの起点ファイルには、Sass側の起点ファイルとの接点を設けます。

/**
 * /src/index.js
 */
import './scss/index.scss';

適当にサスサスしたコードを用意します。CSSファイルで納品なんて場合は、出力後の見え方も意識しておいた方が良いかもしれませんね。Sass側のimportは、webpackではなくnode-sassが対応してくれます。

/**
 * /scss/base.scss
 */
html {
  font-size: 62.5%;
}
body {
  font-family: Roboto, 'メイリオ', Arial, sans-serif;
  font-size: 1.4rem;
  line-height: 1.3;
  color: #333;
  background-color: #fdf7eb;
}
a{
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  &:link, &:hover, &:visited, &:active {
    text-decoration: none;
    color: inherit;
  }
}

@mixin mediaQuery {
  @media screen and(max-width: 359px){
    @content;
  }
}

/**
 * /scss/index.scss
 */
@import 'base';

%spreadsheet {
  background: url(/img/sprite.png) no-repeat left top;
  background-size: 200px auto;
  overflow: hidden;
  text-indent: -9999px;
}

%BlockContainer {
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

%BlockSize {
  width: 50%;
  height: 60%;
  @include mediaQuery {
    width: 70%;
    height: 80%;
  }
}

.areaA {
  @extend %BlockContainer;
  background-color: #b8d684;
  &__card {
    @extend %BlockSize;
    background-color: #527516;
  }
}

.areaB {
  @extend %BlockContainer;
  background-color: #c8b7cc;
  &__card {
    @extend %BlockSize;
    background-color: #661675;
  }
}

.icon {
  @extend %spreadsheet;
  width: 2rem;
  height: 2rem;

  $grid:
    '0 0',
    '0 -60px',
    '0 -120px',
    '-60px 0',
    '-60px -60px',
    '-60px -120px'
  ;
  @each $value in $grid {
    $i: index($grid, $value);
    &--#{$i} {
      background-position: #{$value};
    }
  }
}

.iconB {
  @extend %spreadsheet;
  width: 6rem;
  height: 4rem;
  background-position: 0 -80px ;
}

実行結果

webpackを実行した結果はこちら。二つのファイルが結合され、CSSコードに変換されていますね。webpackをwatchモードにした場合、Sassファイルも監視対象に含まれます。

@charset "UTF-8";
html {
  font-size: 62.5%; }

body {
  font-family: Roboto, 'メイリオ', Arial, sans-serif;
  font-size: 1.4rem;
  line-height: 1.3;
  color: #333;
  background-color: #fdf7eb; }

a {
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
  a:link, a:hover, a:visited, a:active {
    text-decoration: none;
    color: inherit; }

.iconA, .iconB {
  background: url(/img/sprite.png) no-repeat left top;
  background-size: 200px auto;
  overflow: hidden;
  text-indent: -9999px; }

.areaA, .areaB {
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center; }

.areaA__card, .areaB__card {
  width: 50%;
  height: 60%; }
  @media screen and (max-width: 359px) {
    .areaA__card, .areaB__card {
      width: 70%;
      height: 80%; } }

.areaA {
  background-color: #b8d684; }
  .areaA__card {
    background-color: #527516; }

.areaB {
  background-color: #c8b7cc; }
  .areaB__card {
    background-color: #661675; }

.iconA {
  width: 2rem;
  height: 2rem; }
  .iconA--1 {
    background-position: 0 0; }
  .iconA--2 {
    background-position: 0 -60px; }
  .iconA--3 {
    background-position: 0 -120px; }
  .iconA--4 {
    background-position: -60px 0; }
  .iconA--5 {
    background-position: -60px -60px; }
  .iconA--6 {
    background-position: -60px -120px; }

.iconB {
  width: 6rem;
  height: 4rem;
  background-position: 0 -80px; }

日本語
以前に用意した下記構成では、日本語はエンコードされていました。引用符でくくられた文字列はエスケープする仕様でした。エスケープは、css-loaderが担っているようです。

// 各パッケージのバージョン
{
  "devDependencies": {
    "css-loader": "^1.0.1",
    "mini-css-extract-plugin": "^0.4.5",
    "node-sass": "^4.10.0",
    "sass-loader": "^7.1.0",
    "webpack": "^4.16.3"
  }
}

// コンパイル前
font-family: Roboto, 'メイリオ', Arial, sans-serif;

// コンパイル後
font-family: Roboto, '\u30e1\u30a4\u30ea\u30aa', Arial, sans-serif;

この構成では日本語がそのまま出力されていました。css-loaderがメジャーアップデートしています。仕様が変わったみたいですね。

// 各パッケージのバージョン
{
  "devDependencies": {
    "css-loader": "^2.1.0",
    "mini-css-extract-plugin": "^0.5.0",
    "node-sass": "^4.11.0",
    "sass-loader": "^7.1.0",
    "webpack": "^4.29.1"
  }
}

CSS Loader の文字列エスケープがなんかちょっとおかしい件

ファイルの圧縮

webpackのモードをproductionにしていても、切り出されたCSSファイルは圧縮されません。
以前は、css-loaderのオプションにminimizeというのがありましたが、最新版では無くなりました。webpack5では、CSSの圧縮が標準装備されるらしいです。
webpack4でCSSの圧縮を行うには、プラグインを利用します。これがまた結構ややこしそうなので、また別の機会に…。下記のサイトが詳しいです…。

webpack@4 でCSSを抽出する際は mini-css-extract-plugin を使う
Minimizing For Production

近いうちにちゃんと噛み砕いて自分の言葉でまとめたいです。でもその前にwebpack5の安定版が出るかな。どうでしょう。

2019年10月22日追記

sass-loaderの8月の更新(v7.3.0)にて、productionモードの際に圧縮されるようになりました!
あくまでsass-loaderの方なので、css-loaderだけ利用する場合は引き続きプラグインが必要そうです。

参考情報

Sass(SCSS)でCSSコーディングを効率化・メリットと使い方を知る
webpackでCSSやSASSを使う
Adobe Dreamweaver CCを使って爆速でSASS(SCSS)環境を手に入れる
VSCode(Visual Studio Code)で簡単にSASS/SCSSファイルのコンパイルができる拡張機能「Easy Sass」がお勧め
node-sassでSassファイルをコンパイルする

37
47
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
47