webpack+spritesmithでページの高速化

フロントエンド系技術 webpack spritesmith

Posted on 2017-01-30


ページの表示を高速化しようとしたら手法はいろいろあるけどフロントエンドエンジニアができることのひとつに画像のスプライト化ってのがある。
たくさんの画像ファイルをサーバーへリクエストして表示ってのをやるとサーバーの負荷になる。 負荷になると応答が遅くなる。こういうことからリクエスト数を減らせば表示の高速化が見込まれたりするので少しでも快適にページを観てもらおうと思えばやったほうがいい。

http://compass-style.org/
で、ソシャゲーとか作ってるとアクセスは多いし負荷となる処理も多いのでスプライト化は必須ってことが多いんだけど以前はCompassを使ってやるのが一般的だった。
けどCompassがさすがに2年くらい放置(更新されない)されているので仕様がいろいろ古い感じがするし単純にメンテナンスされてないことに不安をがあったりするのでできれば使うのを避けたい。
そうなるとSassを使うにしろPostCSSを使うにしろこれらのフレームワークは画像をスプライト化するような仕組みは持ってないのでCompassでやっていたスプライト画像化を何かしら代替した方法方法でやらないといけない。
いくつかあるらしいんだけど無難なのはspritesmithかなってことで採用してやってみた。

https://github.com/Ensighten/spritesmith

いつものごとくwebpackから使いたいってことでwebpack-spritesmithです。

https://github.com/mixtur/webpack-spritesmith

webpackのpluginとして使えるようになるwebpack-spritesmithを使った。
ファイル構成としてはこんな感じです。
appの直下がドキュメントルート。 Sassを使ってます。 src/images/sprite_assetsのなかにいれたpng画像は残らずスプライト化します。
app/images/sprite.pngがスプライト化された後の画像。

├── app
│   ├── images
│   │   └── sprite.png
│   └── styles
│       └── bundle.css
├── node_modules
├── package.json
├── src
│   ├── images
│   │   └── sprite_assets
│   │       ├── sprite01.png
│   │       ├── sprite02.png
│   │       └── sprite03.png
│   └── scss
│       ├── _sprite.scss
│       └── style.scss
└── webpack.config.js

package.jsonの中はこんな感じ。

{
  "name": "01",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "webpack -w --config webpack.config.js"
  },
  "repository": {},
  "license": "MIT",
  "devDependencies": {
    "browser-sync": "^2.18.6",
    "browser-sync-webpack-plugin": "^1.1.3",
    "css-loader": "^0.26.1",
    "extract-text-webpack-plugin": "^1.0.1",
    "node-sass": "^4.4.0",
    "path": "^0.12.7",
    "sass-loader": "^4.1.1",
    "style-loader": "^0.13.1",
    "webpack": "^1.14.0",
    "webpack-spritesmith": "^0.3.1"
  }
}

サーバーはBrowserSyncの機能を使ってます。 src/scss/styles.scssからimportしやすいように同じ場所に_sprite.scssというファイルで出力します。

var path = require("path");
var webpack = require("webpack");
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');
var SpritesmithPlugin = require('webpack-spritesmith');

module.exports = {
  entry: {
    application: './src/scss/style.scss'
  },
  output: {
    path: './app/styles',
    filename: 'bundle.css'
  },
  module: {
    loaders: [
      {
        test: /\.css|scss$/,
        loader: ExtractTextPlugin.extract("style-loader", "css-loader?minimize!sass-loader")
      },
      {
        test: /\.png$/,
        loaders: ['file?name=i/[hash].[ext]']
      }
    ]
  },
  resolve: {
    modulesDirectories: ["web_modules", "node_modules", "sprite_assets"]
  },
  plugins: [
    new ExtractTextPlugin('bundle.css'),
    new BrowserSyncPlugin(
                  {
                    host: 'localhost',
                    port: 8081,
                    server: { baseDir: ['public'] }
                  }
                ),
    new SpritesmithPlugin({
      src: {
        cwd: './src/images/sprite_assets/',
        glob: '*.png'
      },
      target: {
        image: './app/images/sprite.png',
        css: './src/scss/_sprite.scss'
      },
      apiOptions: {
        cssImageRef: "/images/sprite.png"
      }
    })
  ]
}

使うときはこんな感じでincludeして元のファイルは変数化されているのでそれを指定してやれば背景にスプライト化された画像が配置される。

.hoge {
  @include sprite($sprite01);
}

スプライト化された画像全般に言えることだけど繰り返して表示するものは背景画像の位置を指定するものなどは向いてないので注意が必要だったりする。
次回は複数のスプライト画像を出力する方法やRetinaの場合はどうやったらいいのかなどを書きたいと思う。

shigeki.takeguchi

渋谷の某ソーシャルゲームの会社でフロントエンドエンジニアとして働いてます。20世紀よりウェブ業界。気づいたらアラフィフ業界人です。
まだまだ現場で粘り強く作る側でいたいと思います。

By year

  1. 2017 (10)
  2. 2016 (23)
  3. 2015 (13)
  4. 2013 (15)
  5. 2012 (21)
  6. 2011 (34)

© 2012 shigeki.takeguchi