トップ頁
先頭
最後

大変!スクリプトのバージョン違ってますよ

Webアプリを修正したら

Webアプリを修正/変更して公開したら突然、意味不明なエラーが多発し始めることがあります。

こんな時、通常は「再読み込みしてください」とお願いします。修正/変更前の古いJavaScriptファイルをWebブラウザがキャッシュしているのが原因であれば、これで解決です。

以上、終わり...で終わるのであれば、ことは簡単です。しかし、世の中そう甘くはありません。

実際に、顧客のパソコン上で、意味不明なエラーが多発したら、それこそ大変なことになります。

今まで聞いたことのない部署の、聞いたことのない偉いさんから、訳の分からないメールがじゃんじゃん飛んできます。

やれ「なぜXXXXXXXXXXXXだ!」とか、やれ「いつもXXXXXXXXXXXX」とか、挙句の果てに「※※※※※※※※※※※※※※※※」とかとか。

えーい鬱陶しい!!!

こうならないために

注意深い設計者なら、そもそもこのような状況に陥らないでしょう。解決策はいくらでもあるのですから。

JSPやPHPなどの、動的にページを編集できる環境であれば、スクリプトやスタイルシートを、全てHTML内に埋め込んでしまえば解決です。

では、静的なHTMLの場合はどうでしょう。

この場合、ページ専用のスクリプト以外を埋め込むことはできない(やったら大変なことに...)ので、スクリプトファイルの名前に、バージョン番号を入れることになります。

XXXXXX_1.0.0.8.js

といった感じになります。修正や変更が入る度にバージョンを上げます。

しかし、大量にあると

スクリプトのファイル名にバージョン番号を入れるのは手っ取り早いのですが、HTMLが100ページ・300ページ、いや1000ページあったら、どうなるでしょう?

最初に作るときは、別に問題ないかもしれません。テンプレートを作って、それをコピーして使えば良いのですから。しかし、一度スクリプトに変更が入りバージョンが変わった時が大変です。

全てのページを修正しなくてはなりません。同じディレクトリに存在すれば、コマンド一発かもしれませんが、複数のディレクトリどころか、複数のマシン・複数の国にまたがって存在するかもしれません。

管理ツールで一括管理されていれば問題ないかもしれませんが、どれだけのサイトが正しく管理されているのでしょうか?

キャッシュなんかさせない(使わせない)

キャッシュをさせない(正確には使わせない)という選択肢があります。Webブラウザがキャッシュした古いファイルを使いさえしなければ、スクリプトのファイル名にバージョン番号を入れる必要もなく、大量のHTMLを修正する必要もありません。

方法は簡単です。スクリプトでスクリプトをロードするコードを書けば良いのです。えっ「訳がわからん」?

では、以下のコードをご覧ください。

スクリプトをロードするスクリプト
1: <script type="text/javascript">//<![CDATA[
2:     var url = "./example.js?" + new Date().getTime();
3:     document.writeln('<script type="text/javascript" src="' + url + '"><\/script>');
4: //]]></script>

このスクリプトの肝は、2行目の最後のnew Date().getTime()です。このコードはミリ秒単位の現在時刻を返します(正確には少し違うが)。

スクリプトファイル名の後ろに、クエリとしてミリ秒単位の現在時刻を付与すれば、毎回異なるURLでリクエストすることになるので、WebブラウザのキャッシュにはそのURLは存在せず、キャッシュが使われてしまうことはありえません。

この方法は、スクリプトだけではなくスタイルシートにも使えます。これで、ある時突然画面が壊れるという無残な結果も回避できます。

スタイルシートをロードするスクリプト
1: <script type="text/javascript">//<![CDATA[
2:     var url = "./example.css?" + new Date().getTime();
3:     document.writeln('<link rel="stylesheet" href="' + url + '" type="text/css" />');
4: //]]></script>

これで、Webブラウザがキャッシュの古いファイルを使ってしまうことによる問題は解決されました。

しかししかし、昨今のWebアプリは...

GUIの出来を競うかのような昨今のWebアプリでは、巨大なJavaScriptファイルをいくつもロードすることが当たり前のようになっています。実際のところ、汎用的に設計され実装されたJavaScriptのライブラリ群は、メガバイトレベルになりつつ有ります。

改行やコメント・余分な空白を削除し、メソッド名やフィールド名を変更して、サイズを縮小するコンパイル(と言うか圧縮)を施すことで、サイズを小さくする方法も有りますが、エラーが発生しようものなら障害箇所の特定が難しく(不可能に近い?)、障害対応にシビアなシステムに適応するのははばかられます。

できれば、一回ロードしたら次からはキャッシュを使って欲しいものです。そして、修正や変更があった場合は、確実に最新のファイルをロードしなおしてもらいたいものです。そして、その度にロードするファイル名を書き換えたくなど有りません。

やってみましょう

考え方は簡単です。サーバ上で定義した通りのファイル名で、Webブラウザがスクリプトやスタイルシートをロードしてくれるようにするのです。

このコンテンツの「キャッシュなんかさせない(使わせない)」で紹介した方法を使えば、任意のファイル名でスクリプトやスタイルシートをロードさせることができます。

ミリ秒単位の現在時刻を使う代わりに、ロードさせたいバージョン番号をファイル名に付与することが出来れば、全てが解決します。

以下のスクリプトで、本体のスクリプトをロードします。すでに紹介した、キャッシュを使わせないためのスクリプトと、ファイル名を除き同じものです。

スクリプトをロードするスクリプト
1: <script type="text/javascript">//<![CDATA[
2:     var url = "./autoload.js?" + new Date().getTime();
3:     document.writeln('<script type="text/javascript" src="' + url + '"><\/script>');
4: //]]></script>

autoload.jsの内容は、(例えば)以下のようになります。

スクリプトをロードするスクリプト
1: var url
2: url= "./example_1.9.css";
3: document.writeln('<link rel="stylesheet" href="' + url + '" type="text/css" />');
4: url = "./example_1.12.js";
5: document.writeln('<script type="text/javascript" src="' + url + '"><\/script>');

スクリプトやスタイルシートの修正が必要となったら、ファイル名のバージョンを上げて、autoload.jsの内容を書き換えれば、関連した全てのページのバージョンアップが完了します。

しかし、実際にシステムをメンテする場合、特定のライブラリを使っているプログラムを一気に全てバージョンアップするなどという事は、通常ありえません。

出来る限り影響範囲を小さくしたい(テストの範囲を減らしたい)というのが、正直なところでしょう。

これを実現するためには、どのプログラムのどのページに、どのバージョンのどのライブラリをロードさせればよいかを、サーバ側で管理することになります。

また、クライアント側では、自分がどのプログラムのどのページであるかを認識させ、それをサーバに通知する必要があります。

ちなみに、ここからは当社で公開しているライブラリの紹介も兼ねています。「ちぇっ、宣伝かよ!」と思ったあなた、世の中そんなもんです。

後ほど、ダウンロード用のページを用意しますので、興味のある方はダウンロードしてください。

但し、ライブラリ管理のような機能は、ユーザ毎・システム毎に考え方や方法が異なると思われるので、当社のライブラリの実装(このコンテツの内容も含めて)は、ほんのサンプルレベルであることを、ご了承ください。

まずは、クライアントに認識させる

クライアントに自分自身を認識させるために、meta要素を使用します。どのように分類する必要があるかはそれぞれなので、共通的なライブラリで全てを網羅というのは難しいのですが、少々オーバースペック気味にしてみました。

meta要素による分類
<meta name="com_yscjp_vctrl_SITE" content="Yscjp.com" />
<meta name="com_yscjp_vctrl_CONTENTS" content="Use at work the Web" />
<meta name="com_yscjp_vctrl_PACKAGE" content="Use at work the Web" />
<meta name="com_yscjp_vctrl_PAGE" content="Version Ctrl" />
<meta name="com_yscjp_vctrl_VERSION" content="0.9.0.0" />

name属性は、バッティングを避けるために、当社のドメインを使用していますが、要は「サイト」レベル、「コンテンツ」レベル、「パッケージ」レベル、「ページ」レベルに分けて、それに加えてコンテンツのバージョン毎にも指定できるようにしています。

以下のような状況を想定しています。

複雑な構成の例
サイト1
[ コンテンツA ]
バージョン2.2
パッケージ01
ページ1 - 120
[ コンテンツB ]
バージョン1.4
パッケージ01
ページ1 - 100
パッケージ02
ページ101 - 200
パッケージ03
ページ201 - 300
[ コンテンツC ]
バージョン1.0
パッケージ01
ページ1 - 30
パッケージ02
ページ31 - 80
サイト2
[ コンテンツA ]
バージョン2.2
パッケージ01
ページ1 - 120
[ コンテンツC ]
バージョン1.2
パッケージ01
ページ1 - 30
パッケージ02
ページ31 - 80
パッケージ03
ページ81 - 83
[ コンテンツD ]
バージョン1.0
パッケージ01
ページ1 - 150

サイトやコンテンツが実際の何に該当するかはそれぞれですが、例えばサイト1が日本向け、サイト2が米国向け。または、サイト1がA社向け、サイト2がB社向けと言ったところでしょうか。

コンテンツは、Webアプリケーションや静的なHTMLの塊を想定しています。それぞれのコンテンツは、複数のパッケージで構成され、パッケージは複数のページを含みます。

全てのページにこの指定をすれば、クライアントは自分自身が何者かを認識できることになります。

顧客要求は

上記のような構成のサイト群が実際に存在し、全てのコンテンツの全てのページで共通で使用されるスクリプトファイルが有るとします。

さて、この度、機能追加が必要となり、共通のスクリプトをアップデートすることになりました。しかし、実際に影響があるのは、「コンテンツA」の数ページと、「コンテンツC」の「パッケージ02」、「コンテンツD」の数ページだけなので、影響のないページに関してはアップデートしません。

また、全てを同時にアップデートできないので、コンテンツ毎に順次アップデートします。

この依頼を完遂するためには、そもそもどのページにどのスクリプトやスタイルシートが使われているか、管理する必要があります。そこで、以下のような形で管理することにします。実際に何で管理するかはここでは触れませんが、DBで管理するというのが一番簡単で使いやすいのではないでしょうか。

具体的な方式は、また後ほど追記します。しばらくお待ちください。