webpackでejsを使い、共通要素をインクルードして効率的にやりたいよね。

フロントエンド系技術 フロントエンドエンジニア npm Node.js Yarn webpack ejs

Posted on 2017-02-10


数ページのサイトとはいえテンプレ化はしたい、includeを使うと共通要素を外部ファイル化できて効率いいよね。

サンプルとしてファイルは以下におてます。
https://github.com/shigekitakeguchi/yarn-webpack-ejs

ちょっとした、例えばブランドサイトとかキャンペーンサイト、小規模な企業や組織、お店のサイトというと構成によっては1ページってこともあるけど数ページから20ページくらいの規模になると思う。
いやいやもっとあるよってことになるかもしれないけど今回紹介しようと思うやり方は数ページから20ページくらいの規模のものが便利なんじゃないかな。
それ以上のサイトになるともうちょっと本格的な静的サイトジェネレーターとかを検討したほうが良いと思う。
数ページとはいえヘッダーやフッター、あとmeta要素やナビゲーション、細かいところでいうとGoogle Analitycsの記述ってサイト全体で共通するものを手作業、ベタ書きでやるってちょっと変更とかあるとツラい。ひと昔前だったらサーバー側でssiなんてものもあった。
サーバー管理している人にセキュリティ的懸念があるよと言われながら設定を依頼したりローカルで開発する時はMAMPとか使っているとhttpd.confを変更してでも本番の環境でうまく動かなかったり、そもそも今だったらS3とかもちろんssi使えない。
じゃあどうするかというとmiddlemanだの静的サイトジェネレーターの出番なんだけどmiddlemanでやるとなるともうすべてmiddlemanにおまかせしないとだめだったりする。
それならってことでwebpackでincludeつかえるようなパッケージないかなと探したら思ったよりもなくて調べるとhtml-webpack-pluginってのとejs-render-loaderを組み合わせるとできそうだってことでやってみました。

テンプレートエンジンに何を使うかなんだけど経験的なものだけなんだけどejsでだいたいがおさまるかなというのがある。

EJS Embedded JavaScript templates (https://github.com/mde/ejs)

プログラム的な要素ががっしり使ってHTMLを出力するってことになるとカッコがない、閉じ要素がないってことでJadeの方が見通しが良かったりするんだけど今回はejsです。 Node.jsのフレームワークであるexpressを利用したことない人にはejs馴染みないと思いますが扱いやすいテンプレートエンジンだと思います。

初期設定

node -v

まずはお決まりのNode.js入ってるか確認。

yarn -v

Yarn(もしくはnpm)も入ってるか確認。個人的には完全にYarnに移行しました。

webpack -v

webpackも入っているか確認。どうも2.1なのか2.0からなのかextract-text-webpack-pluginが使えないような 不具合があるようで1.1系を使ってます。

webpack --version

もし入ってなかったら今回はバージョン指定してインストールします。

yarn add global webpack@1.14.0
npm install -g webpack@1.14.0

以下yarnはnpmに置き換えて実行してもらえれば問題なしです。

どんなパッケージを使ってるか(package.json)

node-sassはグローバルで入っているはずなので不要かと思ったのですがエラーはかれたのでインストールしてます。scssをロードしてcssにコンパイルしてってのもいれてます。 もしpost-css的なものがいいとかautoprefixerいれたいとかあれば置き換えてもらえたらと思います。webpack使ってもろもろすると複雑になりがちなのでわかりやすいように最低限な感じです。

{
  "name": "yarn-webpack-ejs",
  "version": "1.0.0",
  "main": "index.js",
  "repository": {},
  "license": "MIT",
  "scripts": {
    "start": "webpack -w --config webpack.config.js"
  },
  "devDependencies": {
    "browser-sync-webpack-plugin": "^1.1.4",
    "css-loader": "^0.26.1",
    "ejs": "^2.5.5",
    "ejs-render-loader": "^1.0.0",
    "extract-text-webpack-plugin": "^1.0.1",
    "html-webpack-plugin": "^2.28.0",
    "node-sass": "^4.5.0",
    "sass-loader": "^4.1.1",
    "style-loader": "^0.13.1"
  }
}

ファイル・フォルダ構成

まぁこんな感じの構成になってます。

├── app
│   ├── index.html
│   ├── scripts
│   │   └── bundle.js
│   └── styles
│       └── bundle.css
├── src
│   ├── includes
│   │   ├── footer.ejs
│   │   └── header.ejs
│   ├── scripts
│   │   └── app.js
│   ├── scss
│   │   ├── _normalize.scss
│   │   └── app.scss
│   └── index.ejs
├── package.json
├── webpack.config.js
└── yarn.lock

normalize.scssはわかりやすいように入れているだけです。 yarnなのでyarn.lockというファイルが入ってます。 appの直下がサーバーのドキュメントルートになります。

これがindex.htmlとして作られる元のindex.ejs

src/index.ejsからsrc/includesの中のheader.ejsとfooter.ejsをインクルードしてapp/index.htmlを作ってます。ここではincludesの記述方法をみてもらえれば大丈夫です。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Title</title>
  <link rel="stylesheet" href="styles/bundle.css">
</head>
<body>
  <% include includes/header %>
  <h1>index</h1>
  <% include includes/footer %>
</body>
</html>

webpack.config.js

webpackの設定ファイルです。 html-webpack-pluginというプラグインを使ってHTMLを作ってます。 browser-sync-webpack-pluginはbrowsersync(開いているブラウザすべてで同じ操作ができる的な)の機能だけではなくローカルサーバーとしても使えるので別でサーバー機能のパッケージは使っていません。

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');

module.exports = [{
  entry: {
    application: './src/scripts/app.js',
  },
  output: {
    path: './app/scripts',
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({
      filename: '../index.html',
      template:  'ejs-render-loader!./src/index.ejs'
    })
  ]
},{
  entry: {
    application: './src/scss/app.scss'
  },
  output: {
    path: './app/styles',
    filename: 'bundle.css'
  },
  module: {
    loaders: [
      {
        test: /\.css|scss$/,
        loader: ExtractTextPlugin.extract('style-loader', 'css-loader?minimize!sass-loader')
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('bundle.css'),
    new BrowserSyncPlugin(
                  {
                    host: 'localhost',
                    port: 8080,
                    server: { baseDir: ['app'] },
                    files: [
                      'app/scripts/*.js',
                      'app/styles/*.css'
                    ]
                  }
                )
  ]
}];

今回ejsにインクルードしてHTMLをレンダリングするためにejs-render-loaderというパッケージを使っています。 HtmlWebpackPluginでtemplateとして扱うindex.ejsにこのように指定します。

plugins: [
  new webpack.optimize.UglifyJsPlugin(),
  new HtmlWebpackPlugin({
    filename: '../index.html',
    template:  'ejs-render-loader!./src/index.ejs'
  })
]

複数のページへ共通要素をインクルードすれば効率的にHTMLを管理できると思います。 例えばabout.html(index.htmlとは内容の違うHTML)を作る場合は以下のような感じで指定します。 about.ejsを用意してindex.ejsと同じように共通要素をインクルード指定しpluginsで生成するHTMLのファイル名、元となるejsを指定します。

plugins: [
  new webpack.optimize.UglifyJsPlugin(),
  new HtmlWebpackPlugin({
    filename: '../index.html',
    template:  'ejs-render-loader!./src/index.ejs'
  }),
  new HtmlWebpackPlugin({
    filename: '../about.html',
    template:  'ejs-render-loader!./src/about.ejs'
  })
]

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