-
Notifications
You must be signed in to change notification settings - Fork 748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Core: Ajax performance (cleanseDomElement) #12085
Comments
let me test this change with my client that has the major memory issues and get back to you. If it continues to clean up memory I will implement your change! |
@cocorossello actually that conflicts with this recent change I made. PR: #11998 Issue: #11994 The reason In that ticket above with your fix: Memory: 18.8 MB |
So the problem might be in some destroyListener, interesting |
you can always just MonkeyPatch your code to override removeElement to
|
maybe you can identify a slow destroyListener if like all components are slow, we need to think about a config |
@cocorossello that would be good because the client i am fixing this for has an INSANE amount of components on their page and tabs and I am not noticing much of a slowdown at all. Probably almost 100+ inputs on their tab or pages each. |
OK so I added timer to the AJAX update.... console.time('AJAX updateElement'); // Start the timer with a label
var removed = target.replaceWith(content);
PrimeFaces.utils.cleanseDomElement(removed);
console.timeEnd('AJAX updateElement'); // Stop the timer and print the elapsed time And on my clients crazy pages I am seeing all sub 300ms executions. AJAX updateElement: 1.2080078125 ms
VM43:117 AJAX updateElement: 69.680908203125 ms
VM43:117 AJAX updateElement: 210.800048828125 ms
VM43:117 AJAX updateElement: 35.72607421875 ms
VM43:117 AJAX updateElement: 47.4208984375 ms
VM43:117 AJAX updateElement: 63.093994140625 ms
VM43:117 AJAX updateElement: 32.046875 ms
VM43:117 AJAX updateElement: 22.20703125 ms |
1m is without cleanse, all others with cleanse? or what are the numbers? |
sorry that was just me clicking around through the app on heavy pages with AJAX updates. opening large dynamic tabs with tons of elements and closing them etc. the very first one. I was trying to see if I could get anywhere close to @cocorossello numbers and I have not seen it. In fact this one...
Is recursively calling |
for example
|
@cocorossello if you want to do the same test just use these MonkeyPatches. /**
* Updates an element with the given ID by applying a change set that was returned by an AJAX request. This
* involves replacing the HTML content of the element with the new content.
* @param {string} id ID of the element that is to be updated.
* @param {string} content The new content of the changeset as returned by an AJAX request.
* @param {PrimeFaces.ajax.pfXHR} [xhr] Optional XHR request with `pfSettings` or `pfArgs` with further
* data, such as which forms should be updated.
*/
PrimeFaces.ajax.Utils.updateElement = function (id, content, xhr) {
if (id.indexOf(PrimeFaces.VIEW_STATE) !== -1) {
PrimeFaces.ajax.Utils.updateFormStateInput(PrimeFaces.VIEW_STATE, content, xhr);
} else if (id.indexOf(PrimeFaces.CLIENT_WINDOW) !== -1) {
PrimeFaces.ajax.Utils.updateFormStateInput(PrimeFaces.CLIENT_WINDOW, content, xhr);
}
// used by @all
else if (id === PrimeFaces.VIEW_ROOT) {
// backup our utils, we reset it soon
var ajaxUtils = PrimeFaces.ajax.Utils;
// reset PrimeFaces JS state because the view is completely replaced with a new one
window.PrimeFaces.resetState();
ajaxUtils.updateHead(content);
ajaxUtils.updateBody(content);
} else if (id === PrimeFaces.ajax.VIEW_HEAD) {
PrimeFaces.ajax.Utils.updateHead(content);
} else if (id === PrimeFaces.ajax.VIEW_BODY) {
PrimeFaces.ajax.Utils.updateBody(content);
} else if (id === PrimeFaces.ajax.RESOURCE) {
$("head").append(content);
} else if (id === $("head")[0].id) {
PrimeFaces.ajax.Utils.updateHead(content);
} else {
var target = $(PrimeFaces.escapeClientId(id));
if (target.length === 0) {
PrimeFaces.warn("DOM element with id '" + id + "' cant be found; skip update...");
} else {
count = 0;
console.time('AJAX updateElement'); // Start the timer with a label
var removed = target.replaceWith(content);
PrimeFaces.utils.cleanseDomElement(removed);
console.timeEnd('AJAX updateElement'); // Stop the timer and print the elapsed time
console.log("Removed " + count +" DOM elements");
}
}
};
/**
* Deletes all events, 'on' attributes, data, and the element itself in a recursive manner,
* ensuring that the garbage collector does not retain any references to this element or its children.
*
* @param {JQuery | undefined} jq jQuery object to cleanse
* @param {boolean} [clearData] flag to clear data off elements (default to true)
* @param {boolean} [removeElement] flag to remove the element from DOM (default to true)
* @see {@link https://github.com/primefaces/primefaces/issues/11696|GitHub Issue 11696}
* @see {@link https://github.com/primefaces/primefaces/issues/11702|GitHub Issue 11702}
*/
PrimeFaces.utils.cleanseDomElement = function (jq, clearData = true, removeElement = true) {
if (!jq || !jq.length) {
return;
}
count++;
// Recursively remove events from children elements
jq.children().each(function () {
PrimeFaces.utils.cleanseDomElement($(this), clearData, removeElement);
});
// Remove inline event attributes
var attributes = jq[0].attributes;
for (var i = 0; i < attributes.length; i++) {
var attributeName = attributes[i].name;
if (attributeName.startsWith("on")) {
jq.removeAttr(attributeName);
}
}
// Remove the element from the DOM and trigger onRemove events for widget.destroy.
// IMPORTANT: This must occur before jq.off() to ensure the on("remove") events remain registered.
if (removeElement) {
jq.remove();
}
// Clear data
if (clearData) {
jq.removeData();
}
// Remove event listeners
jq.off();
}; |
I can see the problem now. The page itself has a (long) list of fees (money input) that the user can modify. The page has 380 money input. Each money input has a currency selector (<h:selectOneMenu) and each select has 80 options. So that makes 30k options to cleanse when the whole list of fees have to be updated:
(its still much slower than your 63ms for around 11k elements, I have no idea why. It is a chrome without extensions in a good machine) Maybe I should exclude those plain html elements that cannot have listeners or even children from the cleanse |
33,000 DOM elements 😨 |
Yes, 30.000 of those are options. Excluding them by tagname from the jquery remove works, I can can just do the patch myself, this should not be a problem in general. Should I close the issue? |
Let me know how you exclude them. I think it's worth adding to this patch as I doubt options will ever be the reason for a leak. |
Can you provide a PR with your exclusion fix? |
…to cleanse options elements
…to cleanse options elements
…to cleanse options elements
…to cleanse options elements
… use native remove
… options elements (#12092) * #12085 Core: Ajax performance (cleanseDomElement): No need to cleanse options elements * #12085 Trigger remove event manually to perform cleanup and use native remove * #12085 Improve readability * jsdoc: changed order * #12085: exclude svg from cleanse and improve comment * #12085: typo --------- Co-authored-by: vrossello <vrossello@travelcompositor.com>
…): No need to cleanse options elements
… to cleanse options elements (#12096)
Description
Since #11722 I can see a noticeable performance degradation in client side when there is an ajax and there are many inputs or DOM elements.
I can trace the problem down to the last remove in this function:
If I replace the last
jq.remove();
with
jq.each(function() {this.remove()});
I can see that performance improves very much. In my test I have an ajax that takes about 1.3seconds scripting and with the change it only takes 180ms.
Looks like a safe change, since children are removed before.
Do you think it's worthy to integrate this in PF?
Describe the solution you would like
CleanseDomElement should be faster. Of course another solution is to shrink the DOM tree to update, but there is room to improve cleanseDomElement
Additional context
No response
The text was updated successfully, but these errors were encountered: