-
-
Notifications
You must be signed in to change notification settings - Fork 7k
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
Lazy load components #3879
Lazy load components #3879
Conversation
This reduces the size of |
Nice work! Based on the Webpack Bundle Analyzer output, though, it does appear that there are lots of common modules that are repeated throughout the async chunks – |
I'm not sure I see the practical benefit of this—won't it just increase latency on poor connections due to the extra round trip requests? Is there a way to measure whether the decreased bundle size will actually reflect real world usage? |
It should decrease time to first paint (less JavaScript to parse, which is the main bottleneck on mobile), as well as memory usage. Don't think we are currently measuring those? |
As a side note, it would be nice to set up Lighthouse integration, to measure these metrics for each PR. |
Unscientific findings (running Lighthouse against current master and this branch):
I would prefer to take the average of a few tests but I don't have the time to do it (is there a tool for it?). |
That can be fixed by adding %link{:rel => "preload", :as => "script", :src => (asset_pack_path 'application.js')}/ Also for your Lighthouse score, what page did you test against? There are a few I think we want to prioritize:
I also imagine there are people who bookmark local/federated/home timelines, so those would be worthwhile to preload as well. Overall, I believe it is worthwhile to only load those routes that the user explicitly requested, since it limits overhead of JS parse/exec (and also memory). But avoiding extra network roundtrips is important, and also I'd like to see some deduplication of the shared modules across async chunks. |
I tested the local timeline (mobile UI). |
I think we could also lazy-load some reducers (see https://stackoverflow.com/questions/32968016/how-to-dynamically-load-reducers-for-code-splitting-in-a-redux-application), |
Is this ready for review? |
Just a few final touches to do. Found a crash as well. |
I'd say it is now ready for review. Could lazy-load more reducers I think but I would rather do it in another PR. Would also be nice to lazy-load the Emoji picker using the same idioms used here, but that can wait as well. |
nice, because it's a big PR and not my area of expertise I will test this branch on glitch.social to make my review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codewise this looks great to me; I just have a few small suggestions. This is also deployed at malfunctioniong.technology (with service worker as well) for those who want to take a look.
|
||
// Dummy import, to make sure that <Status /> ends up in the application bundle. | ||
// Without this it ends up in ~8 very commonly used bundles. | ||
import '../../components/status'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice fix
|
||
= javascript_pack_tag 'features/community_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload' | ||
|
||
= javascript_pack_tag 'features/public_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should have as="script"
as well; it's explained what the browser does with this extra information here.
In terms of bundle sizes this also looks great. I rebuilt and put a Webpack Bundle Analyzer view here for those who want to see. (Protip: make a Gist with an index.html and bl.ocks.org can host it for you. 😃) |
@nolanlawson Waiting on you to check the changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great to me, and is live on malfunctioning.technology to test. (You may need to hard-refresh due to ServiceWorker.)
We may want to warn admins when we ship this that they will probably want to enable HTTP/2 since we are now preloading 13 JS files, which will be slow on HTTP/1 due to the 6-connections-per-origin limit. We may also want to tweak the preloading to limit it to only those components that are guaranteed to be loaded (e.g. "getting started" will only be loaded for first-time users; maybe we can detect that somehow with cookies?).
But those are minor concerns; overall this looks awesome to me and I'm 👍 to merge. I especially love the new async-components.js
so we can keep all of those components in one place. Great work!
BTW http2 is recommended in the tootsuite/documentation nginx config so hopefully most admins already have it. |
Component which is loaded before also seem to be lazy loaded. I think it can be avoided by caching result of Pros: avoid flashing of loading view, slightly reduces total time How about this? |
@sorin-davidoi Please guide me through where the "loading" and "error" views are defined and how to get to see them without a slow connection so I can make sure I like how they look visually. |
@Gargron Open the Network tab in the Chrome development tools and tick "Disable cache". For columns:
For modals:
|
* feat: Lazy-load routes * feat: Lazy-load modals * feat: Lazy-load columns * refactor: Simplify Bundle API * feat: Optimize bundles * feat: Prevent flashing the waiting state * feat: Preload commonly used bundles * feat: Lazy load Compose reducers * feat: Lazy load Notifications reducer * refactor: Move all dynamic imports into one file * fix: Minor bugs * fix: Manually hydrate the lazy-loaded reducers * refactor: Move all dynamic imports to async-components * fix: Loading modal style * refactor: Avoid converting the raw state for each lazy hydration * refactor: Remove unused component * refactor: Maintain modal name * fix: Add as=script to preload link * chore: Fix lint error * fix(components/bundle): Check if timestamp is set when computing elapsed * fix: Load compose reducers for the onboarding modal
* feat: Lazy-load routes * feat: Lazy-load modals * feat: Lazy-load columns * refactor: Simplify Bundle API * feat: Optimize bundles * feat: Prevent flashing the waiting state * feat: Preload commonly used bundles * feat: Lazy load Compose reducers * feat: Lazy load Notifications reducer * refactor: Move all dynamic imports into one file * fix: Minor bugs * fix: Manually hydrate the lazy-loaded reducers * refactor: Move all dynamic imports to async-components * fix: Loading modal style * refactor: Avoid converting the raw state for each lazy hydration * refactor: Remove unused component * refactor: Maintain modal name * fix: Add as=script to preload link * chore: Fix lint error * fix(components/bundle): Check if timestamp is set when computing elapsed * fix: Load compose reducers for the onboarding modal
Roadmap:
~200ms
)minChunks
?Bundles: https://gist.github.com/sorin-davidoi/07def6eb30df015fbfc8ced9fa36babd