LESS.jsでブラウザ上からlessファイルの更新をwatchして変更を自動で反映する

LESSファイルの更新を監視&反映

お手軽にLESSを使うときは、HTMLにLESS.jsを直接読み込んで、オンブラウザで実行すると便利です。リモートに仕込みをする必要もありませんし、ローカルでコンパイルしてからアップロードするといった手間からも解放されます。

そいで今回は、あまり知られていない(と思われる)ブラウザ上のLESS.jsでlessファイルの更新をwatchする方法について。

後日の追記

以下に色々書いてありますが、lessファイルにLast-Modifiedヘッダが付いてるとwatchしても、developmentでもlocalStorageにキャッシュされたり、XHR監視されても反映されなかったりうまく動かない。

nginxであれば、例えばadd_header Last-Modified "";等の記述を加える必要が。HTTPヘッダ関わらず、フロント側のみで、ちゃんと適用させる方法は追って…。

watch中にコンソールを眺めるとこんな感じ

オンブラウザなLESS.jsのwatch機能を有効にして実行すると、リモート上のlessファイルに更新があれば、自動でスタイルの変更が適用されるようになります。もちろん、ブラウザのリロードなしで。


less.js on browser console

development環境+less.watchによる監視


ブラウザにウインドウを切り替えてリロードしたりしなくても、リモートのファイルを書き換えればすぐにスタイルの変更が適用されるので、編集に専念しやすくなります。

オーサリングソフトのデザインビューやライブビューの恩恵と似たようなものですが、エディタ(ないしIDE)+ブラウザの自分からすると、そこそこ快適な環境になってくれています。

実際に監視する方法

簡単です。LESS.jsの読み込み前後に、少し書き足すだけでwatchできるようになります。

<link href="less/main.less" rel="stylesheet/less" />
<script type="text/javascript">
var less = {
    env: 'development'
};
</script>
<script src="less/less-1.3.0.min.js"></script>
<script type="text/javascript">
less.watch();
</script>
  1. lessファイル
  2. スクリプト(lessの環境定義)
  3. LESS.js 本体
  4. スクリプト(watchの実行)

以上の読み込み&記述の順で、lessファイルの監視が始まります。実際にバックグラウンドで行われることは、XHRで片っ端からアクセスして取りに行くpoilling的なアレです。

一番最後のless.watch()を省略して、アクセス時のURLに#!watchを付加することでも同様の動作になります。

@importも追跡してくれます

基点になるlessファイル内の@importも追跡できます。

@import 'less/vars.less';
@import 'less/mixin.less';
@import 'less/common.less';

自分の環境では基点のlessファイルはホスト含めたフルパスでないと、うまく追ってくれませんでした。(LESS.js 1.3.0時点)

<link href="http://example.com/less/main.less" rel="stylesheet/less" />

使い所は工夫する

もちろんHTMLの変更は反映されないので、まずはHTMLを一通り組んでから、調整・検証で複数のブラウザをまとめて開いたりしてwatchすると便利そうですね。

development環境の判定

上記のless.watch()および#!watchは、less.envdevelopmentでなければ動作しませんが、普通にless.jsを読み込むだけだとdevelopmentにはなりづらいです。

less.js/lib/less/browser.js at master · cloudhead/less.js · GitHubより。

var isFileProtocol = (location.protocol === 'file:'    ||
                      location.protocol === 'chrome:'  ||
                      location.protocol === 'chrome-extension:'  ||
                      location.protocol === 'resource:');

less.env = less.env || (location.hostname == '127.0.0.1' ||
                        location.hostname == '0.0.0.0'   ||
                        location.hostname == 'localhost' ||
                        location.port.length > 0         ||
                        isFileProtocol                   ? 'development'
                                                         : 'production');

LESS的に環境がdevelopmentであるための判定は、プロトコロルがファイルまたはローカル実行系であったり、ホストがローカルだったりポート番号が指定していたり、のような条件が必要です。さもなくば強制的にproductionになります。

先にenvを指定しておく

これを回避するために、最初の例ではless.jsを読み込む前にlessをグローバルにつくり、less.envを明示して先決めしています。

var less = {
    env: 'development'
};

これで実行環境がリモート等であっても、development環境としてwatch類が利用できるようになります。

production環境でのキャッシュクリア

production環境のときは、LESSのパース結果はlocalStorageにキャッシュされます。

@importなしですべて読み込んでいれば、productionのままでもリロードすればキャッシュはクリアされますが、@importがあるとそれらのlessはキャッシュされっぱなしでファイルを更新してもlocalStorageを消さないと反映されません

そんなときは、Destroy the localStorage copy of less.js client-side-generated CSS — Gistを参考にすると良さそう。gistに掲載されているコードの冒頭判定からless.env !== 'development'を取り除けば、productionであってもリロードするたびにきちんと更新されるようになります。

冒頭のwatchのことを思うと、あまり使い所は思いつきませんが、特定のファイルにローカルプロキシを通して本番環境の調整をするとき等には使えるかもしれません。

本番環境だったらlessファイルのまま読み込んでることはないか...

参考