最近在項目中有遇到一個Form表單中有200多個標籤。在提交表單時網頁會出現等待時間很長,甚至會出現網頁奔潰的狀況。jquery
主要的緣由是由於在使用jQuery.Validate.js進行Form驗證的時候會花銷大量時間。這些時間主要用在兩個地方:git
1.表單中標籤的檢查對應jQuery.Validate.js中checkForm()方法。github
2.檢查完標籤後須要顯示錯誤或成功信息對應jQuery.Validate.js中ShowErrors()方法。ide
先前咱們是用的jQuery.Validate.js-1.8.0版本,我更新到最新的jQuery.Validate.js-1.15.1版本,發現驗證時間沒有獲得明顯的優化。函數
反而會與以前的版本會有衝突,衝突的地方在沒法驗證隱藏的控件,例如用了第三方的HTML編輯框插件,後面會隱藏一個textarea控件,以前低版本的會檢測到這個,可是新版本的會忽略。問題在於新版本的js默認會跳過頁面中不可見的元素。性能
1.8.0版本的ignore:[] 這裏改成「.hidden」在checkForm()時會默認過濾掉頁面中全部的不可見元素。測試
這是1.15.1種checkForm的方法:優化
checkForm: function() { this.prepareForm(); for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) { this.check( elements[ i ] ); } return this.valid(); },
elements: function() { var validator = this, rulesCache = {}; // Select all valid inputs inside the form (no submit or reset buttons) return $( this.currentForm ) .find( "input, select, textarea, [contenteditable]" ) .not( ":submit, :reset, :image, :disabled" ) .not( this.settings.ignore )//在這個地方會對隱藏元素進行過濾,返回的elements會少了不少隱藏的元素。 .filter( function() { var name = this.name || $( this ).attr( "name" ); // For contenteditable if ( !name && validator.settings.debug && window.console ) { console.error( "%o has no name assigned", this ); } // Set form expando on contenteditable if ( this.hasAttribute( "contenteditable" ) ) { this.form = $( this ).closest( "form" )[ 0 ]; } // Select only the first element for each name, and only those with rules specified if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) { return false; } rulesCache[ name ] = true; return true; } ); },
這種處理的確能夠減小不少沒必要要元素的check。可是通過測試這裏的時間提高並非十分明顯。this
可是這樣處理後反而使得項目出現了不兼容的問題,由於像HTML編輯框等須要驗證的背後隱藏元素也會被隱藏。spa
出於項目的須要,這裏我將這個參數defaults中的ignore參數改成了input[type="hidden"]這樣解決了兼容的問題。由於這個只對<input>標籤中設置了"type=hidden"的元素忽略檢查。
說了這麼多並無提到如何提高驗證性能的方面。下面討論下,因爲本人也是小菜,但願大神勿噴。
上面有提到驗證所花銷的時間主要花在兩個函數上,那麼咱們就從這兩個函數提及:
1.checkForm().
從上面其實咱們已經看出來了,將ignore設置值從而過濾掉一些隱藏的元素自己就是一種對checkForm()函數執行的優化。可是這裏並無起到實質性的做用,由於每每表單中隱藏的元素其實並非不少,想要獲得更大的提高應該是把頁面中顯示出來可是又不須要驗證的標籤進行過濾,真正作到只去check須要check的標籤元素。這裏網上有一種方法就是給每一個不須要Check的元素標籤添加一個相似於「class='validate-ignore'」這樣的類,而後在elements()方法中把這樣不須要驗證的元素也過濾掉。這種作法我並無去實踐,由於項目中表單太多,這樣一個個去添加新的class顯的有點不現實。有興趣的朋友能夠去研究一下。
2.showErrors().
這個方法之因此會花銷大量的時間。由於這個方法會使得HTML DOM樹發生改變,來顯示和修改錯誤或正確的提示信息。
原生的showErrors方法以下
默認頁面中每一個須要驗證的elment都會通過這個else分支去執行defaultShowErrors()函數,而這個函數就是改變DOM樹結構的入口。因此咱們在這裏新增一個判斷的邏輯會提高很多的時間。新增判斷以下:
showErrors: function( errors ) { if ( errors ) { var validator = this; // Add items to error list and map $.extend( this.errorMap, errors ); this.errorList = $.map( this.errorMap, function( message, name ) { return { message: message, element: validator.findByName( name )[ 0 ] }; } ); // Remove items from success list this.successList = $.grep( this.successList, function( element ) { return !( element.name in errors ); } ); } if ( this.settings.showErrors ) { this.settings.showErrors.call( this, this.errorMap, this.errorList ); } else { var anyElementsNeedUpdate = false; //參數表示是否須要去更新DOM樹中的元素 for (var i = 0; i < this.errorList.length; i++) { if (!$(this.errorList[i].element).hasClass(this.settings.errorClass)) { anyElementsNeedUpdate = true;//1.當以前驗證有錯誤的元素已經修改正確即沒有了這個errorClass,須要去更新element break; } } if (!anyElementsNeedUpdate) { for (var i = 0; i < this.successList.length; i++) { if ($(this.successList[i]).hasClass(this.settings.errorClass)) { anyElementsNeedUpdate = true;//2.當以前驗證成功的元素如今含有這個errorClass,須要去更新element break; } } } if (anyElementsNeedUpdate) {//若是有上面兩種狀況之一都須要去更新DOM元素,不然不該該去調用defaultShowErrors(); this.defaultShowErrors(); } } },
從這個能夠明顯的看出,checkForm()函數時間沒有太大的變化。可是showErrors()時間變成了以前的十分之一。
通過測試這個修改對驗證功能是沒有影響的,並且性能也提高了很多。
參考:http://stackoverflow.com/questions/5542014/jquery-validate-large-forms-script-running-slowly。