タイトル長ぇ。
jquery Validationサイコー
フロントエンドのValidationでお手軽かつ無くてはならないjQuery Validation Plugin。
入力チェックが必要なところではほぼ必ず使っていたんだけど、ユーザーの操作によっては数千のチェック対象ができてしまうようなFormでチェックをかけると数秒~数十秒もブラウザごと固まってしまうという現象が出た。
(jQuery Validation Plugin v1.16.0)
why?
whyも何もそんな大量のタグ吐くような設計が悪い。
/ \::/\
/。(一)::(一)。
|::。゚(_人_)゚| まったくもってそのとおりです
\ ゚ `⌒´/゚
/ ⌒ヽ ̄ ̄ヽ゚。
/ __\ \/\ \
と__)_ヽ_つ ヽ_つ
プロファイリング
ChromeのデベロッパーツールでSubmit時のパフォーマンススキャンを撮ってみる。
これはひどい。
Bottom-UpでTotal Timeでソートしてみると
あった。
EventLogで見ると
jquery.validation#showErrorsからの流れで起こっているらしい。
コード詳細
#912 showLabel()
「対象のコントロールに対するエラー表示用ラベルが無ければラベルを登録しておく」的な処理っぽい。
このために1000を超えるコントロールに対して非表示のラベルを追加する処理が走って時間がかかっているようだ。
しかしエラーが無くてもとりあえずコントロールを作っておくってのはどうなんだ。
#875 defaultShowErrors()
defaultShowErrors: function() {
var i, elements, error;
for ( i = 0; this.errorList[ i ]; i++ ) {
error = this.errorList[ i ];
if ( this.settings.highlight ) {
this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
}
this.showLabel( error.element, error.message );
}
if ( this.errorList.length ) {
this.toShow = this.toShow.add( this.containers );
}
if ( this.settings.success ) {
for ( i = 0; this.successList[ i ]; i++ ) {
this.showLabel( this.successList[ i ] );
}
}
んんん? エラーがあったらthis.showLabel()、それとは関係なく this.settings.success
ならthis.showLabel()?
settings.successとはなんぞや
document漁ると
success
Type: String or Function()
If specified, the error label is displayed to show a valid element. If a String is given, it is added as a class to the label. If a Function is given, it is called with the label (as a jQuery object) and the validated input (as a DOM element). The label can be used to add a text like "ok!".
と、エラー表示するラベルのエラーになっていないときの表示を設定できるようだ。
ということは、検証OKで何か表示する時用に必要なのであって、OKの場合とくに何も表示しないならそもそも作る必要が無いんじゃないか・・・?
チェックの流れをおさらいしてみる
- $.validator.form / $.validator.element から element.check()が呼ばれてチェック
- エラーなら
formatAndAdd()
経由でerrorList
に追加される - OKなら
successList
に追加
- エラーなら
showErrors()
-
successList
からerrorList
のものを除外 -
this.setting.showErrors()
か**this.defaultShowErrors()
** -
errorList
のものをshowLabel()
-
this.settings.success
ならsuccessList
に対してshowLabel()
- 表示すべきものを
toShow
表示すべきでないものをtoHide
に整理してからhideErrors()
で非表示を処理
あ、やっぱこれOKで何も表示しないなら作る必要ないわ。
ということで
$("#myform").validate({
success: null
});
にしたところ
0.8秒ぐらいまで短縮できた。
「Major GC(1.9MB collected)」で160msもかかってるのはそもそもこんな構造にしてるからです。
/ \::/\
/。(一)::(一)。
|::。゚(_人_)゚| すみませんすみません
\ ゚ `⌒´/゚
/ ⌒ヽ ̄ ̄ヽ゚。
/ __\ \/\ \
と__)_ヽ_つ ヽ_つ
まとめ
jquery.validationを使ってエラーチェックする際、エラーでなければ単純にエラー表記を消す場合、
form.validate({success: null});
にすべき。