標籤:
最近在項目中有遇到一個Form表單中有200多個標籤。在提交表單時網頁會出現等待時間很長,甚至會出現網頁奔潰的情況。
主要的原因是因為在使用jQuery.Validate.js進行Form驗證的時候會花銷大量時間。這些時間主要用在兩個地方:
1.表單中標籤的檢查對應jQuery.Validate.js中checkForm()方法。
2.檢查完標籤後需要顯示錯誤或成功資訊對應jQuery.Validate.js中ShowErrors()方法。
先前我們是用的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。但是經過測試這裡的時間提升並不是十分明顯。
但是這樣處理後反而使得項目出現了不相容的問題,因為像HTML編輯框等需要驗證的背後隱藏元素也會被隱藏。
出於項目的需要,這裡我將這個參數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。
jQuery.Validate.js在Form標籤很多的時候驗證速度慢的處理。