I'm building a shopping cart page that could potentially contain dozens of separate items. Each item has a collapsible panel that contains several form elements that can be used to customize it. Most of the cart is wrapped in an UpdatePanel so that I can avoid a full postback when the user makes changes. When there are many items in the cart, there are of course many many postback elements, all of which are included in the raw form post, even though each post is really only triggered by the change of a single element (ChildrenAsTriggers=True).
I find that the uncompressed size of the redundant form name/value pairs is 25K. The size is probably far smaller in practice due to gzip compression in the browser (I assume browsers routinely compress the postback values, but haven't gone to the trouble to verify this -- anyone know for sure?) I know I'm being a little anal here ("profile before you optimize!") but I would really like to find a way to eliminate this redundant data. I came up with the following JavaScript:
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest
(
function(sender, args) {
var elPostBackTrigger = args.get_postBackElement();
// skip the first input element, which is expected to be the ScriptManager's hidden field
removeNonEssentialPostBackValues(document.aspnetForm.getElementsByTagName("input"), elPostBackTrigger, 1);
removeNonEssentialPostBackValues(document.aspnetForm.getElementsByTagName("select"), elPostBackTrigger);
removeNonEssentialPostBackValues(document.aspnetForm.getElementsByTagName("textarea"), elPostBackTrigger);
}
);
function removeNonEssentialPostBackValues(aElements, elPostBackTrigger, iFirstElement) {
if (iFirstElement == undefined)
iFirstElement = 0;
for (var i = iFirstElement; i < aElements.length; ++i) {
if
(
aElements[i] != elPostBackTrigger
&& aElements[i].name
&& aElements[i].name != ''
&& aElements[i].name.indexOf('_') != 0
) {
aElements[i].removeAttribute('name');
aElements[i].disabled = true;
}
}
}
The idea is that if you remove the "name" attribute of a form element, it should no longer be "successful" as per the HTML spec, and ASP.NET should omit it from the postback. Of course you don't want to mess with __VIEWSTATE, __EVENTTARGET, or really anything that begins with an underscore. And you don't want to remove the postback trigger itself.
Unfortunately, this has no effect on the postback values captured through Firebug. It seems that by the time the PageRequestManager fires the beginRequest event, it has already generated the postback name/value pairs. I find this strange, since I would imagine that the main reason for the beginRequest event is to make small changes to the form elements before postback. Ma开发者_开发百科ybe PageRequestManager doesn't actually generate the name/value pairs, but rather just pre-generates a list of which elements will be included? Debugging into the MS JavaScript libraries is hard slogging, and I was wondering if anyone could enlighten me here.
EDIT 1: I also tried disabling the form elements in addition to removing their name attributes, but it didn't help. I verified through Firebug that the elements are disabled and names removed before the beginRequest event completes, but somehow ASP.NET still adds them all to the postback.
Jordan Rieger
I finally got around to stepping through MicrosoftAjaxWebForms.debug.js and verified that the PageRequestManager does indeed generate the post collection prior to raising the beginRequest event. Worse, the collection is stored in a StringBuilder in a local variable, so I couldn't manipulate it, even if I was willing to access private members.
The solution I found was to call ScriptManager.RegisterOnSubmitStatement from Page_Load in code behind, to setup a JavaScript beforePostback() function. I verified that this function does indeed get called before the PageRequestManager begins its dirty work, so I was able to put my calls to removeNonEssentialPostBackValues() in there.
If Not ScriptManager.GetCurrent(Me).IsInAsyncPostBack Then
ScriptManager.RegisterOnSubmitStatement(Me, Me.GetType(), "beforePostback", "beforePostback()")
End If
And the JavaScript:
function beforePostback() {
var nlTriggers = document.getElementsByName(document.aspnetForm.__EVENTTARGET.value);
if (nlTriggers && nlTriggers.length > 0) {
var elPostBackTrigger = nlTriggers[0];
}
// skip the first input element, which is expected to be the ScriptManager's hidden field
removeNonEssentialPostBackValues(document.aspnetForm.getElementsByTagName("input"), elPostBackTrigger, 1);
removeNonEssentialPostBackValues(document.aspnetForm.getElementsByTagName("select"), elPostBackTrigger);
removeNonEssentialPostBackValues(document.aspnetForm.getElementsByTagName("textarea"), elPostBackTrigger);
}
With this code, I found that I could reduce the size of the postback request in my hand-crafted nightmare scenario from about 34K down to about 15K. I also verified that the important form values were still getting passed through to the page, and that the code-behind could still discern which element triggered the postback.
Obviously, this technique is quite heavy-handed, and would not fit many scenarios. For example, in a typical web form scenario where the user must edit multiple fields and pass validation, you wouldn't want to do this, because you would need all the form values to be posted. Really, this technique is a simple way of getting closer to a client-side interface where the JavaScript makes only the minimum server calls necessary to carry out its commands. I've built such applications before and while their performance is very good, they are tedious to develop and maintain. For this project I wanted to leverage ASP.NET as much as possible while maintaining high performance.
UPDATE: I just tested this in IE 8 (I had been using Firefox 3.6) and it is a very slow process to disable the hundreds/thousands of form elements. It takes 1 or 2 seconds, unlike Firefox 3.6, which doesn't even blink. I shudder to think how slow IE 7 might be. I guess I will have to abandon this technique for now, since the delay introduced for the large amount of IE users would be much greater than the delay caused by the extra 20K of postback data for the unnecessary form elements.
UPDATE 2: If you are still maintaining complex ASP.NET WebForms pages 10 years later (my sympathies) you will probably find that modern browsers and devices have no problem blasting through the JS in my helper function almost instantly.
精彩评论