検索
リンク
タグ
ASP.NET
.NET
ASP.NET MVC
F#
Azure
Visual Studio
ASP.NET Core
ライトニングトーク
Plone
Selenium
AJAX
C#
jQuery
ADO.NET Entity Framework
JavaScript
SQL Server
EFCore
LINQ
WebMatrix
Fizz-Buzz
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
2024年 02月 2024年 01月 2023年 12月 2023年 11月 2023年 10月 2023年 09月 2023年 08月 2023年 07月 2023年 06月 2023年 05月 2023年 04月 2023年 03月 2023年 02月 2023年 01月 2022年 12月 2022年 11月 2022年 10月 2022年 09月 2022年 08月 2022年 07月 2022年 06月 2022年 05月 2022年 04月 2022年 03月 2022年 02月 2022年 01月 2021年 12月 2021年 11月 2021年 10月 2021年 09月 2021年 08月 2021年 07月 2021年 06月 2021年 05月 2021年 04月 2021年 03月 2021年 02月 2021年 01月 2020年 12月 2020年 11月 2020年 10月 2020年 09月 2020年 08月 2020年 07月 2020年 06月 2020年 05月 2020年 04月 2020年 03月 2020年 02月 2020年 01月 2019年 12月 2019年 11月 2019年 10月 2019年 09月 2019年 08月 2019年 07月 2019年 06月 2019年 05月 2019年 04月 2019年 03月 2019年 02月 2019年 01月 2018年 12月 2018年 11月 2018年 10月 2018年 09月 2018年 08月 2018年 07月 2018年 06月 2018年 05月 2018年 04月 2018年 03月 2018年 02月 2018年 01月 2017年 12月 2017年 11月 2017年 10月 2017年 09月 2017年 08月 2017年 07月 2017年 06月 2017年 05月 2017年 04月 2017年 02月 2017年 01月 2016年 12月 2016年 11月 2016年 10月 2016年 09月 2016年 08月 2016年 07月 2016年 06月 2016年 05月 2016年 04月 2016年 03月 2016年 02月 2016年 01月 2015年 12月 2015年 11月 2015年 10月 2015年 09月 2015年 08月 2015年 07月 2015年 05月 2015年 04月 2015年 03月 2015年 02月 2015年 01月 2014年 12月 2014年 11月 2014年 10月 2014年 09月 2014年 08月 2014年 06月 2014年 04月 2014年 03月 2014年 02月 2014年 01月 2013年 12月 2013年 10月 2013年 09月 2013年 08月 2013年 07月 2013年 06月 2013年 05月 2013年 04月 2013年 03月 2013年 02月 2013年 01月 2012年 12月 2012年 11月 2012年 10月 2012年 09月 2012年 08月 2012年 07月 2012年 06月 2012年 05月 2012年 04月 2012年 03月 2012年 02月 2012年 01月 2011年 12月 2011年 11月 2011年 10月 2011年 09月 2011年 08月 2011年 07月 2011年 06月 2011年 05月 2011年 04月 2011年 03月 2011年 02月 2011年 01月 2010年 12月 2010年 11月 2010年 10月 2010年 09月 2010年 08月 2010年 07月 2010年 06月 2010年 05月 2010年 04月 2010年 03月 2010年 02月 2010年 01月 2009年 12月 2009年 10月 2009年 09月 2009年 07月 2009年 06月 2009年 05月 2009年 04月 2009年 03月 2009年 02月 2009年 01月 2008年 12月 2008年 11月 2008年 10月 2008年 09月 2008年 08月 2008年 07月 2008年 06月 2008年 05月 2008年 04月 2008年 03月 2008年 02月 2008年 01月 2007年 12月 2007年 11月 2007年 04月 2007年 03月 2007年 02月 2007年 01月 2006年 11月 2006年 10月 2006年 09月 2006年 08月 2006年 07月 |
2013年 12月 05日
※本稿は One ASP.NET Advent Calendar 2013 / 5日目の参加記事です。
ASP.NET の Minify & Bundle 機能ASP.NET MVC にまつわる話。Minify & Bundle とは?ASP.NET MVC4 から、JavaScript および CSS の Minify & Bundle 機能が提供されるようになった。Minify 機能は、JavaScript や CSS を、ブラウザが解釈/実行するにあたっては不要な空白や改行を削ったり、変数名や関数名を短い名前に付け替えたりして、そのファイルサイズを小さく削減する機能。 Bundle 機能は複数の JavaScript や CSS を一つのファイルのように見せて、1回の HTTP 要求でひとまとめにブラウザに返す機能である。 こうすることで、少ない通信量と通信手順で JavaScript や CSS をブラウザに渡すことができ、速度向上が見込めるというわけだ。 Minify や Bundle を実践するには、事前に Minify や Bundle を行うツールがいろいろ流通している。 別段、ASP.NET MVC4 を待つ必要はない。 しかし ASP.NET MVC4 が提供する Minify & Bundle 機能は、実行時にそれら処理を行うという点で大変便利だ。 実行時に動的に Minify & Bundle する ASP.NETそもそも「Minify & Bundle する」という明示的な作業が要らない。統合開発環境などが提供する機能によって、.css ファイルの保存と同時に Minify された .css を自動生成される便利環境も目にする(Visual Studio Pro以上の拡張機能にもあったりする)。 しかし オリジナルの .css ファイルと Minify 版の .css ファイルの双方をバージョン管理に追加する手間と煩雑さは変わらないことであろう。 さらには、開発/デバッグ中はオリジナル版を、リリース環境では Minify & Bundle 版を参照するようにするには、「事前に Minify & Bundle を実施」方式ではなかなかつらいものがあるのではないだろうか。 ASP.NET MVC4 による Minify & Bundle 機能は、これらの課題を解決してくれる。 やり方次のような JavaScript、foo.js とbar.js があったとしよう。※コードを読み解く必要はない。分量と雰囲気が掴めればOK。 // foo.js // bar.jsこれを Minify & Bundle するには以下の手順となる。 まず、ASP.NET Web アプリケーションが起動するタイミングで1回でいいので、 - どういったファイルを Minify & Bundle するのか、 - 及びどういう URL でその Minify & Bundle されたリソースを参照するのか、 を ASP.NET の Minify & Bundle 機構に登録する。 具体的には、Global.asax.cs で定義される Application クラスの Start イベントコールバック ( App_Start メソッド ) 内で以下のように実装すればよい。 BundleTable.Bundles.Add(次にこうして Minify & Bundle されたリソースを参照したい Razor ビュー (.cshtml) では、先に Minify & Bundle 機構に登録した URL を引数に指定して、System.Web.Optimizatio.Scripts クラスの Render メソッドを呼び出して、script タグを生成すればよい。 @Scripts.Render("~/bundles/foobar") そうすると、まず一般的な開発環境で実行すると、@Scripts.Render("~/bundles/foobar") と記述された箇所は、下記のとおりなんの変哲もない2つの script タグとして出力される。<script src="/Scripts/foo.js"></script>ここで web.config 中の compilation セクションの debug=true の設定を debug=flase に書き換えて再実行すると、@Scripts.Render("~/bundles/foobar") の出力が以下のように、単一の script タグに変化する。 <script src="/bundles/foobar?v=S0oL9G83-ZH6naQPl4k7q0..."></script>この URL による GET 要求の応答内容は以下のとおり、 foo.js と bar.js の中身がくっついた、かつ、 不要な空白と改行が除去され可能なかぎり変数名が短縮された JavaScript コードが返されるのだ。 var message=message||{};(function(n){n.foo="Foo1"})(message);message=message||{},※コードを読み解く必要はない。先のオリジナルの foo.js, bar.js からの変化が掴めればOK。 Minify & Bundle 結果をオフラインキャッシュするさてこのように大変便利な Minify & Bunlde 機能。これを、HTML5 の オフラインモードに応用することも可能だ。 HTML5 オフライン対応にあたっては、まず、cache.manifest という URL に対して、ネットワーク接続がなくてもあらかじめブラウザのキャッシュ内に取り込みしておいて使えるようにしておきたいリソース ( .js や .css や HTML ページやそのほか HTTP 経由で取り寄せる諸々 ) を所定の書式で列記したテキストを返すようにしておく。 そしてオフライン対応させたい HTML ページの html 要素に、manifest 属性として cache.manifest を指定しておく。 <html manifest="cache.manifest">詳細は割愛するが、これら処置に加えて、オンライン/オフラインの検出とオフラインキャッシュの鮮度チェック & 更新を行う若干の JavaScript コードを加えればオフライン対応の Web アプリができあがる。 そして、この cache.manifest に、Minify & Bundle された JavaScript や CSS を指定することもできるのだ。 そのためには、cache.manifest を ASP.NET の仕組みにて動的に返すようにする必要がある。 cache.manifest を ASP.NET MVC のビューで生成するもっとまともなやり方がきっとあると思うのだが、自分が実現できたやり方を以下に記す。まず、ASP.NET Web アプリケーション起動時のルーティング登録にもうひとつルーティングを付け加えて、"cache.manifest" という URL が要求されたら、所定の MVC コントローラ/アクションが呼び出されるようにしてみた。 routes.MapRoute(さて普通は、cache.manifest などという URL は静的ファイルハンドラによって処理しようとされ、ファイルとしては実在しないので HTTP 404 Not Found になってしまう。 そこでこの要求を ASP.NET のパイプラインに確実に流し込むよう、web.config に以下の記述を付け加える。 <system.webServer>次に、cache.manifest への HTTP 要求で呼び出される MVC コントローラ/アクションだが、こちらは実質何もしていない。 単にビューの描画を返すだけだ。 public class HomeController : Controller 肝心なのはこのアクションの結果返される Razor ビュー。 AppManifest.cshtml になるわけだが、この Razor ビュー内で、System.Web.Optimization.Scripts クラスの Url メソッドを使うのだ。 CACHE MANIFEST ※ Scripts.Render メソッドではない。Render メソッドだと、HTML5オフラインキャッシュマニフェストとしては余計な "<script>" タグにくるまれた文字列が返されるため。 こうすることで cache.manifest への要求で返されるコンテンツは、 CACHE MANIFESTとなり、foo.js と bar.js が Minify & Bundle されたコンテンツを要求し、オフラインキャッシュに収録するようになるわけだ。 オフライン対応Webアプリ開発で本領発揮するASP.NET!オフラインキャッシュはキャッシュ最優先!さて HTML5 のオフラインキャッシュだが、cache.manifest で指定されキャッシュされたリソースは、たとえネットワークがオンラインであっても再読み込みされない。そう、再読み込みされないのだ。 Ctrl + F5 によるスーパーリロードも無駄である。 例えば、cache.manifest が以下のように記述されていたとして、 CACHE MANIFESTこのマニフェストを含む Web ページをいちどロードしキャッシュ取り込みに成功したとする。 するとその後は、foo.js および bar.js を変更しても、ネットワークのオンライン/オンラインにかかわらず、決して再読み込みされずに、キャッシュされているコンテンツが使われてしまうのだ。 再読み込みのためには cache.manifest を更新するではどうすれば、サーバー側で変更された foo.js, bar.js をブラウザに再読み込みさせるかというと、cache.manifest の内容を変更する必要があるのだ。※尚、実際には、ブラウザ側 JavaScript にて、ネットワークがオンラインのときは明示的に cache.manifest が新しくなっていないかチェックする処理を予め実装しておく必要がある。 本質的には、foo.js の変更を反映する目的のためには、cache.manifest を書き換えることに意味は無い。 只々、キャッシュを破棄して再読み込みしてもらわんが為だけに、cache.manifest を何とか変更する必要が発生するのだ。 他所で見かけた手法は、cache.manifest 中に、 CACHE MANIFESTみたいなコメント行 ( cache.manifest では行頭が # から始まる行はコメントとみなされる ) を加えておき、foo.js を変更したら、この Etag 部分の英数字の羅列を違う羅列に書き換える、というものだ。 しかしだ。 こんな手法は、とりわけ開発中は面倒この上ない。 加えて、バージョン管理システムに、本質的でない、cache.manifest の変更履歴が commit されてしまう。 それ、ASP.NET MVC が解決してくれます!その点、ASP.NET MVC の Miify & Bundle の仕組みは大丈夫。先ほどの例でいくと、foo.js と bar.js を Minify & Bundle した内容を返す URL は MANIFESTとなる(@Scripts.Url("~/bundles/foobar") の出力結果)わけだが、ここで joo.js なり bar.js なりを書き換えると、上記 URL のクエリ文字列部分が変化するのだ (下記)。 CACHE MANIFESTすなわち、 foo.js の変更にともなって、cache.manifest が自動的に書き換わる わけだ。 このように ASP.NET MVC の Minify & Bundle 機能を経由して、cache.manifest でオフラインキャッシュ指定しておけば、 HTML5 オフライン対応した Web アプリ開発も超楽ちん なのである。 ...でも少しハマった orz実を言うと、上記までの手順を丁寧に再現するだけではまだうまくいかない。Content-Type 応答ヘッダの指定cache.manifest への HTTP 要求に対する応答では、Content-type 応答ヘッダが text/cache-manifest である必要がある。そこで、AppManifest.cshtml 中に C# コードブロックを書き加えて、以下のように ContentType 応答ヘッダを明示的に指定してやる必要がある。 .ContentType = "text/cache-manifest"; レイアウトページの不使用、だが謎の空行が...!さらに、たいていの ASP.NET MVC アプリケーションでは、_ViewStart.cshtml 等によって「レイアウト」ページが既定で指定されているはずだ。cache.manifest ではレイアウトページの自動適用はいい迷惑なので、これを断ち切る必要がある。 最初は AppManifest .cshtml に以下のように書き加え、レイアウトページは使わないようにしてみた。 @{ ところが、である。 どうしてか、こうして実装した cache.manifest の第1行目に空行が入ってしまうのだ。 cache.manifest の仕様として第1行目は "CACHE MANIFEST" ではじまることと規定されている。 そのため、無用な空行が第1行目に存在していると、ブラウザ側で "parse error" などと console に出力されて、マニフェストとして読み込んでくれないのである。 専用のレイアウトページを用意してとりあえず回避原因を深く追いかけていないのでなぜ空行が入ってしまうのかわかっていない。とりあえずの回避として、AppManifest 専用にレイアウトページ「AppManifest_Layout.cshtml」を作成した。 CACHE MANIFESTで、ApManifest.cshtml はこうである。 @{なんだか若干こんがらがりそうな構成になってしまった。 しかしとりあえずこれで謎の空行は発生しなくなり、無事、期待どおりの動作となった。 cache.manifest への応答を返す実装がどうにもスマートではないので、ASP.NET の達人に添削していただき、よりよい解を求めたいところではある。 実際のソースコード事例一式とりあえずこの実装で HTML5 オフラインを利用している事例として、"Turtle Graphics Do It!" を挙げておく。ソースコードは GutHub で公開中。 https://github.com/jsakamoto/turtle-graphics-do-it このブログ記事では割愛してしまった、オフライン/オンライン状態の検出と、オンライン時のキャッシュマニフェストの更新チェックの JavaScript 実装も、上記ソースコードから調べることができる。 以上、One ASP.NET Advent Calendar 2013、5日目用の投稿である。 明日は、驚異の「全部オレ(orワタシ) Advent Calendar」を実践中の強者達の一人、@miso_soup3 さんの番である。
by developer-adjust
| 2013-12-05 08:04
| .NET
|
Comments(0)
|
ファン申請 |
||