マイコミジャーナルにあるHTML5 APIのWeb Workersについての解説見て作ってみたものを、
最近サービス開始されたjsdo.itにうpしてみたよ。
jsdo.itはまだ開始まもなく、研究に余念の無い変態ユーザーの密度が計り知れない強さなので必見だと思う 😀
とりあえずWeb Workersっていうのが使ってみたかったんだけど、負荷の軽減具合を試すようなサンプルじゃ面白くないので、
ライブラリファイルにあるHTMLソースを組み合わせてベースになるソースを組む手抜きツールを作ってみた。
連想配列として設定済みの値を並べるだけでも量が増えればそれなりに重くなると思うし。
読み込んでるワーカーファイルとライブラリファイルを編集すれば出力をカスタマイズできる。
Web Workers?
使うメリットは、マイコミのサンプルにあるように重い処理を全部裏側でやっつけられるので表の動作が軽くなるという事なんだと思う。
Web Workesを使うにはまずオブジェクトを作成しなければならない:
var worker = new Worker('./source-worker/js/');
ここで別途作成したsource-worker.jsを読み込む。
Workerクラスに渡したurlはXHRと同じくクロスドメイン通信は出来ない。
ワーカーにデータを送信:
worker.postMessage(this.conf);
手抜きツールでは何がチェックされたのかっていう設定を入れたオブジェクトを渡してます。
this.conf = {
charset:"string",
doctype:"string",
lang:"string",
meta:Array(),
link:Array(),
area:Object()
}
postMessage()でワーカーに送信されたデータはevent.dataで受け取ることができる。
ワーカーから送信されたデータもonmessageイベントで受け取れる。
onmessage = function(event) {
var opt = event.data;
};
渡されたオブジェクトをワーカーが受け取ると値がtoString()された文字列になる。
event.data = {
charset:"string",
doctype:"string",
lang:"string",
meta:Array String,
link:Array String,
area:Object String
}
配列とかはevalで元に戻す。
opt.meta = eval(opt.meta);
ワーカーはimportScripts()で別のソースファイルを読み込むことができる。
手抜きツールではライブラリファイルを読み込んでます。
importScripts('http://jsdo.it/Tenderfeel/sourcelib/js/');
WorkerからはDOMにアクセス出来ないっていう制約があるんで、
document.getElementByIdとかで要素取得して.valueだの.innerHTMLだので値を参照することはできない。
その代わりデータの受け渡しはonmessage/postmessageの他にWeb Database、Storage、XHR、WebSocketでも出来るんで
これらを介して処理に必要な情報をやり取りすることになります。
個人的な使い勝手で言うとSessionStorageとLocalStorageが便利だと思った。
console.log()使えないからエラーチェックが非常に面倒くさいんだけど、
ChromeかSafariならworker内のエラーも表示してくれます。
HTMLSourceBuilderについて
デモとソースはjsdo.itに置いてあります。
配列に格納されているソースの出力をチェックボタンのON/OFFで切り替えるだけの単純なものです。
たとえばlinkタグについては次のようになってます。
HTMLファイル側:
<tr> <th scope="row">Link</th> <td id="link"> <ul> <li><label><input type="checkbox" name="linkrss" value="linkrss">RSS Feed</label></li> <li><label><input type="checkbox" name="touchicon" value="touchicon">apple-touch-icon</label></li> <li><label><input type="checkbox" name="favicon" value="favicon">Favicon</label></li> </ul> </td> </tr>
core.jsが使うのはnameだけなのでvalueはidにしてもいい。
ライブラリ側(抜粋):
'link':{
'linkrss':'<link rel="alternate" type="application/rss+xml" href="" title=""{slash}>\n',
'touchicon':'<link rel="apple-touch-icon" href=""{slash}>\n',
'favicon':'<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"{slash}>\n',
},
見ての通りHTMLとライブラリの構造が同じなので、日本語にすると次のようになります。
’親要素のID':{
'input要素のname値':'出力されるソース',
'input要素のname値':'出力されるソース',
'input要素のname値':'出力されるソース'
}
{slash}はXHTMLの場合に/へ変換されるタグです。source-worker.jsで置換してます。
if(opt.link){
opt.link = eval(opt.link);
for(j = 0; j < opt.link.length; j++){
s += source['link'][opt.link[j]].replace(/{slash}/g, shash);
}
}
[/js]
置換するタグは他に{lang}と{tag}があります。{lang}はクラスオプションで指定出来るlangの値が入る。
{tag}はHTML5のタグ切り替え用で、switchTag()という関数で処理しています。
#area以下の場合はinputの下にさらに子要素を設定できる。
Footerのcopyrightの項目がそれです。
[html]
<li><label><input type="checkbox" name="footer" value="footer">Footer</label>
<ul>
<li><label><input type="checkbox" name="copyright" value="copyright" class="footer">copyright</label></li>
</ul>
</li>
[/html]
この場合は親となるinput要素と同じクラス名を子に付与しときます。
source-worker.js(抜粋):
[js]
if(opt.area.footer!==null){
s += source.area.footer.start.switchTag('footer');
if(opt.area.footer.copyright){
s+=source.area.footer.copyright.switchTag('span');
}else{
s += '\t\t<!--Footer Content-->\n';
}
s += source.area.footer.close.switchTag('footer');
}