CLOVER🍀

That was when it all began.

webpackとwebpack-dev-server、html-loaderと合わせて、JsRenderを試す

webpackと組み合わせて、簡単なJavaScriptでのテンプレートエンジンを使いたいと思いまして。

で、ちょっと事情からjQueryが視野に入らざるをえない感じなんですけど、jQueryでテンプレートといえばjQuery Templates plugin…

jQuery Templates plugin

だと思っていたのですが、今は名を変えてJsRender/JsViews/JsObservableとなっているようです。

JsRender/JsViews

JsRender: best-of-breed templating

このあたりの事情、ホントに知らないなぁ…。

で、これとwebpackを合わせて使うことを考えまして、以下のような感じでやってみようかなと。

  • 依存関係はBowerで解決(npmはちょっとムリでした)
  • HTMLテンプレートは、webpackのhtml-loaderを使ってrequireするようにする
  • webpack-dev-serverを利用して、コードに変更があったらオートリロード

ここまでの内容をやってみます。

追記
Bowerなしで、チャレンジする方法も試してみました。

webpackとwebpack-dev-server、html-loaderと合わせて、JsRenderを試す(再) - CLOVER

準備

まずは、下準備から。以下のコマンドを実行します。

$ npm init
$ npm install -g gulp webpack bower
$ npm install --save-dev gulp gulp-util bower webpack webpack-dev-server html-loader
$ bower init
$ bower install --save jquery jsrender

html-loaderというのが、webpackのHTML Loaderです。

先ほどこういうエントリを書きまして

webpackを使ってJavaScriptのビルド/minify(+Bower) - CLOVER

ここでBower要らんのでは?というツッコミをいただいたのですが、JsRenderの場合「npm install」ではちょっと違う物が入ってしまうので、いろいろ試した挙句やめました。

GitHubのREADME.mdでは、以下のコマンドで良いことになっているのですが、

$ npm install jsrender --save

実際に実行すると、すでにメンテナンスが終了している以下のリポジトリのモジュールが引き込まれます。

https://github.com/shtylman/node-jsrender

関連っぽいもの…。

common js compliant lib #15

あまりハマり続けても仕方がないので、Bowerにしました。

コードの用意

動作確認用のコードはこちら。

まずは、HTML。
index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>webpack/webpack-dev-server &amp; JsRender example</title>
</head>
<body>
  <h1>webpack/webpack-dev-serverとJsRenderのサンプル</h1>
  <span>プログラミング言語</span>
  <span id="languages"></span>
  <script type="text/javascript" src="dist/scripts/app.js"></script>
</body>
</html>

エントリポイントとなるJavaScript
src/scripts/main.js

var $ = require("jquery");
window.jQuery = $;

var jsrender = require("jsrender");

//var template = require("html!../templates/list.html");
var template = require("html!html-templates/list.html");

var tmpl = $.templates(template);

var scopes = {
    languages: ["Java", "JavaScript", "CoffeeScript"]
};

$("span#languages").html(tmpl.render(scopes));

JsRenderのオブジェクトをそのまま使ってもよかったんですけど、jQueryと統合するには以下の1行を書くことに…。

window.jQuery = $;

これ、requireの追加引数とかにすればいいのかな?とも思いましたが、なんかダメっぽい。

言い換えれば、グローバルにjQueryがあれば、それに追加しにいくということですね…。

HTMLテンプレート。
src/templates/list.html

<ul>
  {{for languages}}
  <li>{{>}}</li>
  {{/for}}
</ul>

JavaScriptは「src/scripts」、HTMLテンプレートは「src/templates」配下に置いています。

$ find index.html src -type f
index.html
src/templates/list.html
src/scripts/main.js

gulpの設定

gulpfile.jsは、以下のようになりました。
gulpfile.js

var gulp = require("gulp");
var gutil = require("gulp-util");
var webpack = require("webpack");
var WebpackDevServer = require("webpack-dev-server");
var webpackConfig = require("./webpack.config.js");

gulp.task("default", ["webpack-dev-server"]);

gulp.task("webpack", function(callback) {
    var config = Object.create(webpackConfig);
    webpack(config, function(err, stats) {
        if(err) throw new gutil.PluginError("webpack", err);
        gutil.log("[webpack]", stats.toString({
            colors: true
        }));
        callback();
    });
});

gulp.task("webpack-dev-server", function(callback) {
    var config = Object.create(webpackConfig);
    var compiler = webpack(config);

    new WebpackDevServer(compiler, {
        publicPath: "/" + config.output.publicPath,
		stats: {
			colors: true
		}
    }).listen(8080, "localhost", function(err) {
        if(err) throw new gutil.PluginError("webpack-dev-server", err);
        gutil.log("[webpack-dev-server]", "http://localhost:8080/webpack-dev-server/index.html");

        // keep the server alive or continue?
        callback();
    });
});

webpack-dev-serverのリッスンポートは、以下のサンプルに習って8080に。

このあたりを参考にしています。

http://webpack.github.io/docs/usage-with-gulp.html

https://github.com/webpack/webpack-with-common-libs/blob/master/gulpfile.js

webpackの設定

webpackの設定は、このように行いました。

webpack.config.js

var path = require("path");
var webpack = require("webpack");

var currentWorkingDirectory = process.cwd();

module.exports = {
    cache: true,
    entry: {
        app: "./src/scripts/main.js"
    },
    output: {
		path: path.join(__dirname, "dist/scripts"),
		publicPath: "dist/scripts/",
		filename: "[name].js",
		chunkFilename: "[chunkhash].js",
        // sourceMapFilename: "[file].map"
    },
    loaders: [
        { test: "/\.html$/", loader: "html?minize" }
    ],
    resolve: {
        root: [path.join(__dirname, "bower_components")],
        alias: {
            "html-templates": currentWorkingDirectory + "/src/templates"
        }
    },
    devtool: "#source-map",
    plugins: [
        new webpack.ResolverPlugin(
            new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
        ),
        new webpack.DefinePlugin({
			"process.env": {
				// This has effect on the react lib size
				"NODE_ENV": JSON.stringify("production")
			}
		}),
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.UglifyJsPlugin()
    ]
};

html-loaderに関しては、npmでインストールしたうえで、以下を追加。

    loaders: [
        { test: "/\.html$/", loader: "html?minize" }
    ],

これで、このような記述でテンプレートをロードすることが可能になります。

var template = require("html!../templates/list.html");

が、これだと現在のスクリプトからの相対パスになるので、なんか微妙だなぁと思ってこういう記述に変更。

var template = require("html!html-templates/list.html");

これを動作させるために、aliasを貼りました。

        alias: {
            "html-templates": currentWorkingDirectory + "/src/templates"
        }

currentWorkingDirectoryというのは、以下の内容です。

var currentWorkingDirectory = process.cwd();

はい。

このあたりは、こちらを参考に。

https://github.com/webpack/webpack-with-common-libs/blob/master/webpack.config.js

http://webpack.github.io/docs/using-loaders.html

https://webpack.github.io/docs/list-of-loaders.html

http://webpack.github.io/docs/loaders.html

html loader for webpack

webpackを使い倒す - Thujikun blog

確認

gulpでwebpack-dev-serverを起動するようにしているので、以下のコマンドを実行。

$ gulp

こんな内容が出力されまして…

[23:00:04] Using gulpfile ~/xxxxx/gulpfile.js
[23:00:04] Starting 'webpack-dev-server'...
[23:00:04] [webpack-dev-server] http://localhost:8080/webpack-dev-server/index.html
[23:00:04] Finished 'webpack-dev-server' after 57 ms
[23:00:04] Starting 'default'...
[23:00:04] Finished 'default' after 6.16 μs
Hash: a21d5a9e49bf19575d1b
Version: webpack 1.12.2
Time: 4173ms
     Asset    Size  Chunks             Chunk Names
    app.js  103 kB       0  [emitted]  app
app.js.map  894 kB       0  [emitted]  app
chunk    {0} app.js, app.js.map (app) 318 kB [rendered]
    [0] ./src/scripts/main.js 354 bytes {0} [built]
    [1] ./bower_components/jquery/dist/jquery.js 248 kB {0} [built]
    [2] ./bower_components/jsrender/jsrender.js 69.9 kB {0} [built]
    [3] ./~/html-loader!./src/templates/list.html 68 bytes {0} [built]

WARNING in app.js from UglifyJs
Side effects in initialization of unused variable jsrender [./src/scripts/main.js:4,0]
Condition always true [./bower_components/jquery/dist/jquery.js:9170,0]
Condition always true [./bower_components/jsrender/jsrender.js:19,0]
Dropping unreachable code [./bower_components/jsrender/jsrender.js:21,2]
Side effects in initialization of unused variable $ [./bower_components/jsrender/jsrender.js:17,0]
Dropping unused variable tag_ [./bower_components/jsrender/jsrender.js:450,0]
webpack: bundle is now VALID.

URLがログにも出ていますが、以下にアクセスすると

http://localhost:8080/webpack-dev-server/index.html

こんな感じの結果になります。

なお、gulp起動中にファイルを修正すると、webpack-dev-serverが変更を反映してくれます。

以上!