色々あって、この令和の時代にjQuery2.xで書かれたコードを読んでいました。
対象のHTMLから、要素の数だけループして特定の値を抜き出す、というごくごくシンプルなjQueryです。
シンプルなのですが、毎回DOMツリーを全探索していて、要素数が多いと遅くなりそうだったので、実際に検証した結果をメモとして残しておきます。
結論
結論から先に書くと、やっぱり遅かったので、高速化大事だなぁという話です。
まとめると以下です。
- ループのlengthは毎回計算しない
- 無駄にDOMツリーを全探索してはいけない
- 取得したオブジェクトはキャッシュして使いまわす
- jQueryを使わなくていいなら極力使わない
対象のHTML
<div>
<div class="hoge">
<a href="#a1">リンク1</a> <!-- ★1 hrefの値を取得したい -->
</div>
<div class="fuga">
なにかしらテキスト1 <!-- ★2 fugaのテキストをhrefの値とペアで取得したい -->
</div>
</div>
<div>
<div class="hoge">
<a href="#b2">リンク2</a>
</div>
<div class="fuga">
なにかしらテキスト2
</div>
</div>
...
<!-- 以降、データの数だけhogeとfugaを含むdivが続く -->
対象のjQuery
for(var i = 0; i < $(".hoge").length; i++) {
var hoge_a = $(".hoge a")[i].href;
var fuga_text = $(".fuga")[i].innerHTML;
}
ループが回るたびに、3回DOMツリー全探索とjQueryオブジェクトへの変換が行われるため、要素数が多いページだと遅くなりそうです。
比較対象用のコード
比較対象がないと遅いかどうかわからないので、以下の比較用のコードを書きました。なるべく元のコードと似た形で目的を達成しようとしています。
jQueryを使ったまま改良したコード
const target = $(".hoge"); // 取得したオブジェクトはキャッシュする
const len = target.length; // lengthは計算しておく
for (let i = 0; i < len; i++) { // eachは遅いので使わない
let hoge_a = $(target[i]).children().prop("href"); // 全探索せずに、検索対象を絞り込む
let fuga_text = $(target[i]).next().text();
}
jQueryを一切使わないコード
もうこれjQuery使うメリット1つもねえだろと思ったので素のJavaScriptで書きました。
const target = document.getElementsByClassName("hoge");
const len = target.length;
for(let i = 0; i < len; i++) {
let hoge_a = target[i].firstElementChild.href;
let fuga_text = target[i].nextElementSibling.textContent;
}
検証結果
divを1000個と100個を生成して、それぞれのコードで比較しました。自分のマシンで10回実行した平均値です。
対象 | 要素数1000の実行時間(ms) | 要素数100の実行時間(ms) |
---|---|---|
元のコード | 561 | 10 |
改良版 jQuery使用 | 10 | 2 |
改良版 jQuery無し | 1 | 0 |
順位は大体の予想通りになりました。
高速化テクニックに関しては実際の効果の程がわかっていなかったのと、意識しないと自分でもやらかしてしまうので、試してみてよかったです。