-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Optimizations for large documentation #1887
Comments
Thanks for digging into this.
That's necessary for navigation on mobile devices. Navigation is only included once and used inside the drawer on mobile and the sidebar on desktop. It's not possible to drop any items here, even when using tabs, as we would lose mobile navigation. If you don't need mobile navigation, you might remove it using overrides as documented in the customization guide.
I guess we could use
We can think about moving this to CSS, yes. |
The latest commit moves the SVG icon definitions which are part of the navigation to CSS, as suggested. The number of nodes should now be greatly reduced for large documentation projects. I (currently) don't see the Disqus optimizations as being necessary, so we'll leave it as it is. Also, overriding Disqus is simple. Reopening until released. |
Okay, one little change to the Disqus integration – it's now wrapped with an event listener to be executed on |
Released as part of 5.5.10. Thanks for your effort digging into this topic! |
Unfortunately, it's not the case. The results are still the same with an extremely long contentful paint: Only if we use the intersection observer to load it if the user scrolls down, the problem is solved. The intersection observer also has the advantage that the Disqus threads wouldn't be loaded most of time. There's one further optimization for the future: |
Interesting, as my testing showed improvements, but Lighthouse scores will always depend on the actual, final HTML being used. Note that you're only escaping from Lighthouse taking notice of the Disqus integration when using the
This isn't big of a problem when content is served with Yes, we could compress or shorten class names, but that would either come at the cost of worse maintainability if done directly in the SCSS code that is compiled to CSS, or result in unstable class names when done as a post-processing step. This would mean that it would be much harder for authors to override certain parts, as class names are less readable or might change in unexpected ways, increasing cost of maintenance downstream. Material for MkDocs strives to be a hackable and extendable theme and clear and concise CSS naming is an essential part of it. |
@squidfunk I still think it would be nice to be able to exclude all nav items from other tabs when I think If you think that's an edge case, some help on how to get this done via customization would be much appreciated. |
I'd really consider this as an edge case, as most documentation projects are not that large. However, I understand that the file size is causing you trouble. I would have thought that gzip compression should be quite efficient, as there are a lot of shared strings/prefixes, but I may be wrong. So in general, one could replace the non-active tab in the drawer with a link to that tab page, as you said, and only render the navigation hierarchy for the active tab. This would need to be done in I'm pretty sure that it could be done, but some research is definitely necessary. I'm unsure whether this could be an option that goes into |
I think that this is a edge case. Have you tried techniques on your web server like using compression when sending documents, add expiration time for the content, or enabling the cache? |
The main problem are the huge number of HTML nodes due to the full navigation menu that can cause a slow down of rendering for smartphones. It takes several seconds until the page becomes usable. |
IMHO, it's just 90% unnecessary data that shouldn't be there, regardless of whether any web server techniques can work around it. We also deliver the documentation as a download for local use, so every byte matters. |
I'd be curious to see a reproducible case with some benchmarks (before + after trimming down the navigation structure). This would yield the necessary baseline on how much improvement could be expected, and if it's worth going down that path. |
The more I think about it, the more I like the idea of removing the nav items from other tabs. Right now (as you pointed out), with If we had a real "tabbed mode" in both desktop and mobile – maybe with more visual clues in the mobile version, not just with replaced links – that would bring a consistent experience for all users AND dramatically reduce the file/DOM size on large doc projects. |
I don't agree with this point. While it might be possible to show the tabs on all screen sizes (by making them scrollable, which would make it harder to discover navigation), you're effectively breaking navigation into multiple UI elements on mobile. My personal experience is that users have learned that navigation on mobile hides entirely behind the hamburger menu. Breaking navigation into multiple components might in fact be less intuitive. This is the main reason navigation is entirely contained in the drawer on mobile.
Yes, larger docs projects might benefit from this. However, this would mean a significant change in behavior + a serious refactoring for which I currently can't afford the time, given the current funding situation. I have too many other things on my plate, so if somebody really really wants this, there are two options:
I'm hoping for your understanding. This project is very, very complex already. When adding more features, especially regarding navigation, many things have to be considered. Nonetheless, a benchmark, which clearly shows the upside of this refactoring (given gzip compression) is a mandatory requirement that has yet to be provided. |
I don't think we can argue whether it's inconsistent (it is, unless you rename the option to Anyway, I came up with a fairly simple solution that only requires customizing Step 1: Nav items should only be generated for the active main navigation item and its nested item listWe change this: mkdocs-material/src/partials/nav-item.html Lines 32 to 33 in 702de82
To this: <!-- Main navigation item with nested items -->
{% if nav_item.children and (nav_item.active or level > 1 ) %} That's it! Does all the magic. (I hope.) EDIT: If this should only be applied when <!-- Main navigation item with nested items -->
{% if nav_item.children and ("navigation.tabs" not in features or (nav_item.active or level > 1 )) %} Step 2: The main level of the mobile view should behave like tabsTo achieve this, we can borrow some code from mkdocs-material/src/partials/nav-item.html Lines 129 to 136 in 702de82
To this: <!-- Main navigation item with nested items -->
{% elif nav_item.children %}
{% set title = title | d(nav_item.title) %}
{% set nav_item = nav_item.children | first %}
<!-- Recurse, if the first item has further nested items -->
{% if nav_item.children %}
{% include "partials/nav-item.html" %}
<!-- Render item -->
{% else %}
<li class="{{ class }}">
<a href="{{ nav_item.url | url }}" class="md-nav__link">
{{ title }}
</a>
</li>
{% endif %}
<!-- Main navigation item -->
{% else %}
<li class="{{ class }}">
<a href="{{ nav_item.url | url }}" class="md-nav__link">
{{ nav_item.title }}
</a>
</li>
{% endif %} EDIT: If this should only be applied when <!-- Main navigation item with nested items -->
{% elif nav_item.children and "navigation.tabs" in features %} |
Thanks for providing your solution. I'm yet waiting for somebody to provide some real-world data that proves that dropping the navigation items from the DOM really benefits loading times on slower connections and/or devices. |
File size difference in our project (yes, I know, doesn't say much about the loading time, but maybe still helpful) Before: After: -> ~41.6 % of original size |
Optimizations for large documentation projects are now on the Insiders roadmap, called "navigation pruning": |
I think this is not an edge case and makes mkdocs unusable for bigger projects. We've got over 4k md files and have now an 8GB build, which is definitly anything other than great . The build times are extremly long (approx 15-20min) and if I enable the minify plugin, it can go up to an hour. I guess we really need some optimization otherwise it wont work properly for bigger projects. Still thanks for all the effort you put in there :) I really hope this gets fixed. |
@malohr Did you try my solution? |
@malohr – I'm actively working on bringing down the size of the final output and think I have a working prototype ready soon. Regarding build times – I think this should be raised over at the MkDocs repository. |
@wilhelmer Haven’t tried it but will definitely give this a shot until there is an official release that will fix it, thank you :) @squidfunk Awesome, really appreciate all your work. This is just the missing piece to make the project perfect for big docs. |
@wilhelmer Thanks! :) |
If any of you have public repositories with large documentation, it would be of great help for me in seeing what we can optimize, so feel free to post links. |
@squidfunk Here's a project with a large topic: large-topic.zip Yes I know, the If I remove the assets and load the pure HTML file, it's loading fast. So it must be something about Material. It would be great if you could take a look at this. EDIT: When using the default MkDocs template, it's also loading fast. |
@wilhelmer please open a new issue, as this issue has become too high-level. |
I've got a working solution for navigation pruning ready to be tested 😊 Does somebody have a public Open Source project with a huge navigation structure that might benefit from those optimizations? I'd like to run some numbers before releasing. |
Our docs are not that large as the ones from @wilhelmer but you can see if there's an improvement: |
Thanks for providing the link to your docs. I've tested with the new The feature should also be ready to be released tomorrow. |
Whoa! I just tested No optimizations: Custom optimizations (see above): navigation.prune: |
Yeah! Love it! Definitely need to write a blog article about this, as it might get lost in the vast amount of options that we offer. |
However ... the downside of your implementation is that it actually changes the UX. You can't just look around and expand different sections of the nav anymore while staying on the same page. Expanding a section will always cause a page reload. I'm not sure this is worth the extra gain in file size. I think I'd rather go with my 72 MB and not sacrifice the UX for it. No offense though - you know I absolutely admire your work, and maybe that's the best way to go for most users. I'm just not sure whether it's right for my project. |
Jup, the current solution is really the maximum we can save without sacrificing navigation layout. My motivation for the navigation pruning feature was the main use case being "download for offline use", where a page load doesn't matter at all, because loading from the file system is instant anyway. |
I'd argue that even when browsing locally, the page load affects the UX – the screen flickers, and the content changes as you are effectively moving to a new page. It just doesn't feel as smooth as browsing through the nav tree without any reloads. My point is that you can still save a lot without any noticeable change if So maybe |
@wilhelmer @squidfunk stumbled upon this issue today after a poor lightspeed test of our Mkdocs page showing the same excessive DOM elements: We have about 300 docs pages and lightspeed is showing 1500 DOM elements. I inspected the page in Chrome and was surprised to see that the entire navigation is apparently loaded into the HTML of every single docs page, even for pages that I would think are incapable of displaying those navigation elements. I really like the EDIT: Lastly I should say that this means if I add new pages in other remote regions of my documentation, this |
@glenn-jocher I'm not sure I understand.
Also, when providing a minimal reproduction, please make absolutely sure to remove any customizations, because the error might hide in them. We need to know if stock Material for MkDocs exhibits this problem. |
@squidfunk thanks for your patience. I've isolated a minimum reproducible example in a new Reproduce# Clone the repo
git clone https://github.com/ultralytics/ultralytics
# Change directory and checkout the branch
cd ultralytics
git checkout mkdocs_reproduce
# Install mkdocs-material
pip install "mkdocs-material"
# Serve the docs
mkdocs serve Then when I go to i.e. the help page at http://127.0.0.1:8000/help/ and inspect the html I see relative links to all ~300 pages, I think because of the navigation, even though visually I only see about 20 pages available to navigate to (10 on the top bar, 10 on the left). I believe this is causing the Pagespeed "Excessive DOM Size" errors on our Docs. |
Thanks for the reproduction. Indeed, pruning does not work correctly, as there's a bug when the following three feature flags are all enabled at once:
It works correctly if the last two features are not enabled, or if either one is enabled, but not if both are enabled. This should now be fixed with 292d563, where I also expanded the kind of cryptic boolean condition to make it more explicit. My testing shows the issue is now fixed, and pruning works as expected. Can you confirm, @glenn-jocher? For debugging, it's good to add the following CSS to see pruning in action: .md-nav__item--section {
background: green !important;
}
.md-nav__item--pruned {
background: red !important;
} Edit: I checked your example again: DOM nodes count is down from 1.598 to 266 (-83%) on the help page. |
@squidfunk the update works! Also our Docs without any plugins build 3x faster now, from 3s to 1s. Everything feels a little faster while navigating too. Nice work on the fix. Please release a pip update when possible, thank you! |
Released as part of 9.5.9. For all other navigation pruning related bugs, please open new issues. |
Perfect, thank you! |
Fantastic work @squidfunk, thanks for your work on this subject 👍 I had the issue with the Gitlab Pages size limit being attained, the |
Wow, 80% reduction! That's awesome! |
I checked that...
Description
We are currently trying to optimize page render times of the generated docs. It largely depends on the content and we have a lot in our documentation, e.g.: https://aimeos.org/docs/2020.x/config/client-html/account-download/
We found out that the biggest problem is the navigation, which contains all navigation items on all pages. Google Lighthouse reports an exessive number of DOM nodes for each page (~1400) and an extremely long largest contentful paint:
The first though was to reduce the number of navigation items but that isn't that easy as we can't merge more pages because they are rather long in most cases and the value/experience for the user would get worse.
We identified two things that could be optimized and have a big effect:
1.) The biggest problem is how Disqus is added
The Material theme uses
document.write()
to add it's HTML code but we can use inpartials/integrations/disqus.html
instead:This postpones the
document.write
until the user scrolls to the comment section.2.) Reduce the HTML tags per nested navigation
There are 65 span tags
<span class="md-nav__icon md-icon">
on each page which contains an<svg><path/></svg>
. If we remove them and add alabel.md-nav__link:after { content: url(caret.svg); }
in CSS, we save ~200 nodes at once. Also, the file size reduces because the icon is only defined once.Afterwards we get:
Use Cases
We only have >300 pages which I consider a medium sized documentation. Nevertheless, the DOM nodes are currently >5x the number of navigation items, which has a big effect the more pages are added.
The text was updated successfully, but these errors were encountered: