Skip to content
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 block edit functions #55585

Draft
wants to merge 27 commits into
base: trunk
Choose a base branch
from
Draft

Conversation

jsnajdr
Copy link
Member

@jsnajdr jsnajdr commented Oct 24, 2023

This is an experiment to wrap block edit functions with React.lazy, offloading the edit code into lazily loaded modules. The block-library script now contains only the block registration code without the edit functions. Block attribute schema, support info, transforms, variations, migration code, icons.

The main question is: does this make the block-library script significantly smaller? The answer is that lazy loading edit functions removes exactly 50% of the code. The rest, the non-edit registration code, remains statically loaded. For me this result is a bit disappointing, I expected more. The "full scale" block lazy loading in #51778 removes 100% of the static code, the block-library script completely disappears there. But on the other hand the 50% approach is much easier to implement and has no backward-compatibility issues.

How does it work? It uses webpack native dynamic chunks in combination with React's Suspense and React.lazy. I needed to amend the webpack configuration to setup writing the dynamic chunks into the right subdirectories of ./build/block-library, with the right file names, and to prevent writing the 3rd party packages from node_modules (like colord) into separate "vendor" chunks. No WordPress-specific script code like wp_enqueue_script or *.asset.php file are not needed. The block-library script starts shipping the webpack chunk loading runtime, and loads individual chunks from URLs relative to the main block-library script's URL.

The same approach can be used to dynamically load specific libraries, like the compressorjs library in the Image block, as mentioned by @youknowriad recently.

ES modules and import maps
The next step would be to eliminate the webpack runtime and use native ES modules. The import( './edit' ) statement would remain untouched, except updating the ./edit module name to something unique and symbolic. The import would be performed natively by the browser, according to a generated import map. This should be achievable with a handful of webpack plugins.

But would it really work in practice? I have some doubts. The ES modules are all-or-nothing, are hard to combine with non-modules. What if the ./edit module wants to be a bundle (chunk) of multiple modules? Webpack can bundle multiple modules into a single chunk, but how would that work with native modules? Can we really "resolve" all the internal imports and exports, and expose only the exports of the top-level module of the chunk?

Also, the importing module needs to be a module. We can't use import in a non-module. Makes it hard to incorporate ES modules into an existing non-module system.

There are many nice opportunities to experiment, that's for sure, but the result will almost certainly be not shippable.

Other open questions
Localizations of the edit function probably won't work. At best they'll be all bundled together as localizations for block-library. To resolve this, we'd need something like Jetpack's i18n-loader-webpack-plugin in Core. And it's a good idea to have something like this in Core. Jetpack needs this, and I think Woocommerce also has its own solution for this.

Similarly, we need dynamic loading of styles. mini-css-extract-plugin or something functionally equivalent.

When loading the edit chunks for individual blocks, we currently use suspense with empty placeholder: <Suspense fallback={ null }>. What we probably really want is to plug this in into the "global loading indicator", created by @tyxla some time ago.

@jsnajdr jsnajdr self-assigned this Oct 24, 2023
@github-actions
Copy link

github-actions bot commented Oct 24, 2023

Size Change: -115 kB (-6%) ✅

Total Size: 1.78 MB

Filename Size Change
build/block-directory/index.min.js 7.11 kB -116 B (-2%)
build/block-editor/index.min.js 251 kB -1.53 kB (-1%)
build/block-library/index.min.js 108 kB -109 kB (-50%) 🏆
build/blocks/index.min.js 52 kB +230 B (0%)
build/commands/index.min.js 15.6 kB +45 B (0%)
build/components/index.min.js 222 kB -879 B (0%)
build/compose/index.min.js 12.7 kB +47 B (0%)
build/core-data/index.min.js 72.8 kB +48 B (0%)
build/customize-widgets/index.min.js 11.1 kB -47 B (0%)
build/data/index.min.js 8.99 kB +46 B (+1%)
build/date/index.min.js 17.9 kB +47 B (0%)
build/edit-post/index.min.js 23.5 kB -227 B (-1%)
build/edit-site/index.min.js 214 kB -1.96 kB (-1%)
build/edit-widgets/index.min.js 17 kB -238 B (-1%)
build/editor/index.min.js 63.4 kB -624 B (-1%)
build/element/index.min.js 4.87 kB +41 B (+1%)
build/format-library/index.min.js 7.86 kB -28 B (0%)
build/i18n/index.min.js 3.58 kB +3 B (0%)
build/keycodes/index.min.js 1.39 kB -67 B (-5%)
build/media-utils/index.min.js 2.88 kB -20 B (-1%)
build/patterns/index.min.js 5.59 kB -89 B (-2%)
build/preferences/index.min.js 2.78 kB -41 B (-1%)
build/primitives/index.min.js 1.02 kB +46 B (+5%) 🔍
build/redux-routine/index.min.js 2.75 kB +48 B (+2%)
build/reusable-blocks/index.min.js 2.69 kB -36 B (-1%)
build/rich-text/index.min.js 10.3 kB -33 B (0%)
build/server-side-render/index.min.js 1.92 kB -34 B (-2%)
build/vendors/react-dom.min.js 41.8 kB +54 B (0%)
build/vendors/react.min.js 4.07 kB +48 B (+1%)
build/widgets/index.min.js 7.22 kB +10 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 955 B
build/annotations/index.min.js 2.69 kB
build/api-fetch/index.min.js 2.32 kB
build/autop/index.min.js 2.1 kB
build/blob/index.min.js 578 B
build/block-directory/style-rtl.css 1.03 kB
build/block-directory/style.css 1.03 kB
build/block-editor/content-rtl.css 4.4 kB
build/block-editor/content.css 4.4 kB
build/block-editor/default-editor-styles-rtl.css 394 B
build/block-editor/default-editor-styles.css 394 B
build/block-editor/style-rtl.css 15.7 kB
build/block-editor/style.css 15.7 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/editor.min.js 641 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/editor.min.js 3.62 kB
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 126 B
build/block-library/blocks/audio/theme.css 126 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/editor.min.js 1.81 kB
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/block/editor.min.js 2.21 kB
build/block-library/blocks/button/editor-rtl.css 415 B
build/block-library/blocks/button/editor.css 414 B
build/block-library/blocks/button/editor.min.js 2.62 kB
build/block-library/blocks/button/style-rtl.css 627 B
build/block-library/blocks/button/style.css 626 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/editor.min.js 598 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/editor.min.js 937 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/editor.min.js 1.55 kB
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/editor.min.js 496 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/column/editor.min.js 970 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/editor.min.js 1.69 kB
build/block-library/blocks/columns/style-rtl.css 421 B
build/block-library/blocks/columns/style.css 421 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-author-avatar/editor.min.js 873 B
build/block-library/blocks/comment-author-name/editor.min.js 823 B
build/block-library/blocks/comment-content/editor.min.js 530 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-date/editor.min.js 690 B
build/block-library/blocks/comment-edit-link/editor.min.js 585 B
build/block-library/blocks/comment-reply-link/editor.min.js 451 B
build/block-library/blocks/comment-template/editor.min.js 1.57 kB
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-next/editor.min.js 522 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination-numbers/editor.min.js 414 B
build/block-library/blocks/comments-pagination-previous/editor.min.js 522 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/editor.min.js 800 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments-title/editor.min.js 1.12 kB
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/editor.min.js 2.56 kB
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/editor.min.js 22.4 kB
build/block-library/blocks/cover/style-rtl.css 1.69 kB
build/block-library/blocks/cover/style.css 1.68 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/editor.min.js 705 B
build/block-library/blocks/details/style-rtl.css 98 B
build/block-library/blocks/details/style.css 98 B
build/block-library/blocks/embed/editor-rtl.css 322 B
build/block-library/blocks/embed/editor.css 322 B
build/block-library/blocks/embed/editor.min.js 4.56 kB
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 126 B
build/block-library/blocks/embed/theme.css 126 B
build/block-library/blocks/file/editor-rtl.css 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/editor.min.js 2.61 kB
build/block-library/blocks/file/style-rtl.css 280 B
build/block-library/blocks/file/style.css 281 B
build/block-library/blocks/file/view.min.js 324 B
build/block-library/blocks/footnotes/editor.min.js 770 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 227 B
build/block-library/blocks/form-input/editor.css 227 B
build/block-library/blocks/form-input/editor.min.js 988 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 340 B
build/block-library/blocks/form-submission-notification/editor.css 340 B
build/block-library/blocks/form-submission-notification/editor.min.js 611 B
build/block-library/blocks/form-submit-button/editor.min.js 363 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/editor.min.js 1.04 kB
build/block-library/blocks/form/view.min.js 471 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/freeform/editor.min.js 2.33 kB
build/block-library/blocks/gallery/editor-rtl.css 947 B
build/block-library/blocks/gallery/editor.css 952 B
build/block-library/blocks/gallery/editor.min.js 7.93 kB
build/block-library/blocks/gallery/style-rtl.css 1.72 kB
build/block-library/blocks/gallery/style.css 1.72 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 647 B
build/block-library/blocks/group/editor.css 647 B
build/block-library/blocks/group/editor.min.js 2.31 kB
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/editor.min.js 1.13 kB
build/block-library/blocks/heading/style-rtl.css 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/home-link/editor.min.js 808 B
build/block-library/blocks/html/editor-rtl.css 336 B
build/block-library/blocks/html/editor.css 337 B
build/block-library/blocks/html/editor.min.js 956 B
build/block-library/blocks/image/editor-rtl.css 894 B
build/block-library/blocks/image/editor.css 893 B
build/block-library/blocks/image/editor.min.js 8.46 kB
build/block-library/blocks/image/style-rtl.css 1.6 kB
build/block-library/blocks/image/style.css 1.59 kB
build/block-library/blocks/image/theme-rtl.css 126 B
build/block-library/blocks/image/theme.css 126 B
build/block-library/blocks/image/view.min.js 1.54 kB
build/block-library/blocks/latest-comments/editor.min.js 645 B
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/editor.min.js 3.16 kB
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list-item/editor.min.js 2.58 kB
build/block-library/blocks/list/editor.min.js 2.05 kB
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/loginout/editor.min.js 516 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/editor.min.js 2.16 kB
build/block-library/blocks/media-text/style-rtl.css 505 B
build/block-library/blocks/media-text/style.css 503 B
build/block-library/blocks/missing/editor.min.js 861 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/more/editor.min.js 683 B
build/block-library/blocks/navigation-link/editor-rtl.css 668 B
build/block-library/blocks/navigation-link/editor.css 669 B
build/block-library/blocks/navigation-link/editor.min.js 4.86 kB
build/block-library/blocks/navigation-link/style-rtl.css 259 B
build/block-library/blocks/navigation-link/style.css 257 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 296 B
build/block-library/blocks/navigation-submenu/editor.css 295 B
build/block-library/blocks/navigation-submenu/editor.min.js 4.99 kB
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.26 kB
build/block-library/blocks/navigation/style.css 2.25 kB
build/block-library/blocks/navigation/view.min.js 1.02 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/nextpage/editor.min.js 272 B
build/block-library/blocks/page-list-item/editor.min.js 1.35 kB
build/block-library/blocks/page-list/editor-rtl.css 377 B
build/block-library/blocks/page-list/editor.css 377 B
build/block-library/blocks/page-list/editor.min.js 2.45 kB
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 335 B
build/block-library/blocks/paragraph/style.css 335 B
build/block-library/blocks/pattern/editor.min.js 1.16 kB
build/block-library/blocks/post-author-biography/editor.min.js 572 B
build/block-library/blocks/post-author-name/editor.min.js 785 B
build/block-library/blocks/post-author/editor.min.js 1.36 kB
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comment/editor.min.js 760 B
build/block-library/blocks/post-comments-count/editor.min.js 649 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/editor.min.js 1.21 kB
build/block-library/blocks/post-comments-form/style-rtl.css 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-comments-link/editor.min.js 808 B
build/block-library/blocks/post-content/editor-rtl.css 74 B
build/block-library/blocks/post-content/editor.css 74 B
build/block-library/blocks/post-content/editor.min.js 1.36 kB
build/block-library/blocks/post-date/editor.min.js 1.44 kB
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/editor.min.js 1.67 kB
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 666 B
build/block-library/blocks/post-featured-image/editor.css 662 B
build/block-library/blocks/post-featured-image/editor.min.js 3.25 kB
build/block-library/blocks/post-featured-image/style-rtl.css 342 B
build/block-library/blocks/post-featured-image/style.css 342 B
build/block-library/blocks/post-navigation-link/editor.min.js 1.34 kB
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/editor.min.js 1.52 kB
build/block-library/blocks/post-template/style-rtl.css 409 B
build/block-library/blocks/post-template/style.css 408 B
build/block-library/blocks/post-terms/editor.min.js 1.32 kB
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/editor.min.js 663 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/editor.min.js 1.31 kB
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/editor.min.js 511 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/editor.min.js 709 B
build/block-library/blocks/pullquote/style-rtl.css 354 B
build/block-library/blocks/pullquote/style.css 354 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 B
build/block-library/blocks/query-no-results/editor.min.js 357 B
build/block-library/blocks/query-pagination-next/editor.min.js 530 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination-numbers/editor.min.js 647 B
build/block-library/blocks/query-pagination-previous/editor.min.js 529 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/editor.min.js 855 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/editor.min.js 931 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/editor.min.js 6.86 kB
build/block-library/blocks/query/view.min.js 958 B
build/block-library/blocks/quote/editor.min.js 1.02 kB
build/block-library/blocks/quote/style-rtl.css 237 B
build/block-library/blocks/quote/style.css 237 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/editor.min.js 596 B
build/block-library/blocks/read-more/style-rtl.css 140 B
build/block-library/blocks/read-more/style.css 140 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/editor.min.js 1.27 kB
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/editor.min.js 2.82 kB
build/block-library/blocks/search/style-rtl.css 629 B
build/block-library/blocks/search/style.css 628 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 478 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/editor.min.js 616 B
build/block-library/blocks/separator/style-rtl.css 229 B
build/block-library/blocks/separator/style.css 229 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 323 B
build/block-library/blocks/shortcode/editor.css 323 B
build/block-library/blocks/shortcode/editor.min.js 572 B
build/block-library/blocks/site-logo/editor-rtl.css 754 B
build/block-library/blocks/site-logo/editor.css 754 B
build/block-library/blocks/site-logo/editor.min.js 3.68 kB
build/block-library/blocks/site-logo/style-rtl.css 204 B
build/block-library/blocks/site-logo/style.css 204 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-tagline/editor.min.js 780 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/editor.min.js 1.1 kB
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-link/editor.min.js 1.56 kB
build/block-library/blocks/social-links/editor-rtl.css 682 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/editor.min.js 1.63 kB
build/block-library/blocks/social-links/style-rtl.css 1.49 kB
build/block-library/blocks/social-links/style.css 1.48 kB
build/block-library/blocks/spacer/editor-rtl.css 350 B
build/block-library/blocks/spacer/editor.css 350 B
build/block-library/blocks/spacer/editor.min.js 1.93 kB
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table-of-contents/editor.min.js 2.03 kB
build/block-library/blocks/table/editor-rtl.css 395 B
build/block-library/blocks/table/editor.css 395 B
build/block-library/blocks/table/editor.min.js 3.6 kB
build/block-library/blocks/table/style-rtl.css 639 B
build/block-library/blocks/table/style.css 639 B
build/block-library/blocks/table/theme-rtl.css 146 B
build/block-library/blocks/table/theme.css 146 B
build/block-library/blocks/tag-cloud/editor.min.js 1.03 kB
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 403 B
build/block-library/blocks/template-part/editor.css 403 B
build/block-library/blocks/template-part/editor.min.js 4.94 kB
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/editor.min.js 459 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/editor.min.js 719 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/editor.min.js 606 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/editor.min.js 5.63 kB
build/block-library/blocks/video/style-rtl.css 185 B
build/block-library/blocks/video/style.css 185 B
build/block-library/blocks/video/theme-rtl.css 126 B
build/block-library/blocks/video/theme.css 126 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12.4 kB
build/block-library/editor.css 12.3 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.8 kB
build/block-library/style.css 14.8 kB
build/block-library/theme-rtl.css 688 B
build/block-library/theme.css 693 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/commands/style-rtl.css 935 B
build/commands/style.css 930 B
build/components/style-rtl.css 11.8 kB
build/components/style.css 11.8 kB
build/core-commands/index.min.js 2.77 kB
build/customize-widgets/style-rtl.css 1.36 kB
build/customize-widgets/style.css 1.36 kB
build/data-controls/index.min.js 640 B
build/deprecated/index.min.js 451 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.65 kB
build/edit-post/classic-rtl.css 558 B
build/edit-post/classic.css 558 B
build/edit-post/style-rtl.css 5.58 kB
build/edit-post/style.css 5.57 kB
build/edit-site/style-rtl.css 15 kB
build/edit-site/style.css 15 kB
build/edit-widgets/style-rtl.css 4.17 kB
build/edit-widgets/style.css 4.16 kB
build/editor/style-rtl.css 5.34 kB
build/editor/style.css 5.33 kB
build/escape-html/index.min.js 537 B
build/format-library/style-rtl.css 492 B
build/format-library/style.css 490 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 448 B
build/interactivity/file.min.js 447 B
build/interactivity/image.min.js 1.67 kB
build/interactivity/index.min.js 12.9 kB
build/interactivity/navigation.min.js 1.15 kB
build/interactivity/query.min.js 740 B
build/interactivity/router.min.js 1.36 kB
build/interactivity/search.min.js 618 B
build/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.74 kB
build/list-reusable-blocks/index.min.js 2.11 kB
build/list-reusable-blocks/style-rtl.css 851 B
build/list-reusable-blocks/style.css 849 B
build/modules/importmap-polyfill.min.js 12.2 kB
build/notices/index.min.js 948 B
build/nux/index.min.js 2 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 742 B
build/patterns/style-rtl.css 553 B
build/patterns/style.css 552 B
build/plugins/index.min.js 1.8 kB
build/preferences-persistence/index.min.js 2.05 kB
build/preferences/style-rtl.css 710 B
build/preferences/style.css 712 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 1 kB
build/react-i18n/index.min.js 623 B
build/react-refresh-entry/index.min.js 9.47 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/router/index.min.js 1.79 kB
build/shortcode/index.min.js 1.39 kB
build/style-engine/index.min.js 2.1 kB
build/token-list/index.min.js 582 B
build/url/index.min.js 3.72 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/viewport/index.min.js 957 B
build/warning/index.min.js 249 B
build/widgets/style-rtl.css 1.17 kB
build/widgets/style.css 1.17 kB
build/wordcount/index.min.js 1.02 kB

compressed-size-action

@github-actions
Copy link

github-actions bot commented Oct 24, 2023

Flaky tests detected in b007e3c.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/8170009404
📝 Reported issues:

@jsnajdr jsnajdr force-pushed the try/block-edit-lazy-loading branch 3 times, most recently from 5578c20 to 83feb93 Compare October 25, 2023 15:28
@gziolo gziolo added [Type] Performance Related to performance efforts [Package] Block library /packages/block-library labels Oct 28, 2023
@jsnajdr jsnajdr requested review from gziolo and luisherranz November 2, 2023 13:11
Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure this was ready for review, but leaving some early comments.

This is a neat compromise between reaping lazy block loading benefits and preserving backward compatibility 👍

I've left some questions, let me know what you think.

Localizations of the edit function probably won't work. At best they'll be all bundled together as localizations for block-library. To resolve this, we'd need something like Jetpack's i18n-loader-webpack-plugin in Core. And it's a good idea to have something like this in Core. Jetpack needs this, and I think Woocommerce also has its own solution for this.

I'm curious if @yuliyan has any feedback on this part since he worked on chunking localizations (including the extra string extraction work) for lazy-loaded modules in another project.

What do you think about all the e2e flakiness and failures introduced by this PR? Have you been able to dig into the cause? Seems like many of them could be solved by making the tests aware of waiting for the block to load.

Finally, are you planning any other changes before marking this as ready to review?

@@ -17,7 +17,9 @@ export { metadata, name };
export const settings = {
icon,
example: {},
edit,
edit: lazyLoad( () =>
import( /* webpackChunkName: "archives/editor" */ './edit' )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit about the naming choice: is there a good reason to have the webpack chunk name with a different name than the original module name? The convention is to name them edit.[jt]sx? so why not name the chunk names archives/edit for example?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I followed the existing convention for editor styles, which are in editor.css files, one for each individual block.

Copy link
Member

@gziolo gziolo Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subtle difference is that editor.css generated might contain CSS that gets applied not only to the block's edit, but also to inspector controls, toolbars, or anything necessary to make the block editing experience coherent. This CSS might also be tied to the deprecated version of the block, or save function.

Anyway, I don't have strong feelings about the name. I'm curious whether this code comment is essential to make the feature work, though. The other question is how all that would work with custom implementations of the editor that don't use webpack. Example: https://github.com/WordPress/gutenberg/blob/trunk/platform-docs/docs/intro.md which is powered by Vite.

import { Suspense, lazy } from '@wordpress/element';

export default function lazyEdit( cb ) {
// eslint-disable-next-line @wordpress/no-unused-vars-before-return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels off that we need to do that. Does it signal that we might need to improve the rule itself?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is indeed a bug in the rule, I reported it in #55552.


export default function lazyEdit( cb ) {
// eslint-disable-next-line @wordpress/no-unused-vars-before-return
const Load = lazy( cb );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, what's the tradeoff of moving this line inside the Edit function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Load is a React component, it needs to be a stable reference to a function, otherwise it would remount on every re-render of the Edit component.

const Load = lazy( cb );
return function Edit( props ) {
return (
<Suspense fallback={ null }>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding an optional prop to support a fallback UI would be a nice idea for follow-up.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about the idea to hook the fallback UI into the global loading indicator? Can it be done?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about this, too. Currently on a slow connection the experience looks like

test-edit-blocks.mov

Given my naive understanding of how all of this works to me a local loading indicator makes more sense and could prevent jumps? I guess there's a difference between opening a post with dozens of blocks and inserting a block on a slow connection.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, from your example it seems we'll need to design a good loading experience. The global loading indicator is good when loading an existing page. But for individual blocks, we'll need to show something local.

Copy link
Contributor

@flootr flootr Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing we need to consider are blocks which are created while typing and not loaded yet. For example if I type *<space> and continue typing the flow is disruptive

test-list-item.mov

My intention was to type * Hello World (I was continuously typing as I would in any other app).

Edit: Discovered while looking at a failing e2e test in editor/blocks/list.spec.js.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to experiment with the simplest spinner or any other means of inline loading experience - that shouldn't be a blocker for this particular experiment. We can iterate on improving that in a separate step where we also involve design folks.

@@ -156,6 +162,14 @@ module.exports = {
return `webpack://${ info.namespace }/${ info.resourcePath }`;
},
},
optimization: {
splitChunks: {
cacheGroups: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on why we're disabling all the default cache groups? What's the tradeoff that we're deciding on here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this splitChunks wasn't there, the editor chunk for the Cover block wouldn't be just one file (block-library/blocks/cover/editor.min.js), but it would be split into two. Because the Cover block uses the colord library, which lives in node_modules. By default modules in node_modules that are big enough are split into separate, "vendor" chunks. The rationale is better caching, because the vendor libraries don't change so often.

I wanted to avoid this, I wanted to keep the 1:1 relationship between blocks and files. And prevent generating chunks with weird names like 7001.min.js.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main question is: does this make the block-library script significantly smaller? The answer is that lazy loading edit functions removes exactly 50% of the code.

It might be one of the reasons why you only noticed 50% of the code moved to chunk as shared code still needs to be bundled in the main entry point. Do you know the number with the shared vendor chunk? Well I still think that 50% saving for the initial load is good enough to roll out this prototype as it will allow us to be less strict about adding more complex functionality to individual blocks 😄

@@ -146,6 +146,12 @@ module.exports = {
output: {
devtoolNamespace: 'wp',
filename: './build/[name]/index.min.js',
chunkFilename: ( { chunk } ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these changes be made in the tools/webpack/block.js?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block webpack config only builds the view scripts, and copies the styles. All of them are already lazy-loaded, in the sense that the frontend enqueues only the view scripts of the blocks that are actually rendered. The full scale lazy loading in #51778 use the same approach for the editor scripts: they are build and enqueued separately. That PR adds its webpack bits to the block config: https://github.com/WordPress/gutenberg/pull/51778/files#diff-777759e98c224019a699e8294c23bccc34453c91b873a12cb78a828487cd5f15

But this PR is different. All the blocks are still shipped and registered at once in a big block-library script, they just dynamically load some pieces. That's why the webpack config changes are in the packages config, which builds the block-library package (script) and all others.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I confirm it's correct. So far, wp-block-libary was a monolithic entry point with JS code for registering all core blocks in the editor. tools/webpack/block.js handles everything else: JS for the front end, CSS for the editor and front end, copying PHP files and block.json files.

@yuliyan
Copy link
Contributor

yuliyan commented Nov 6, 2023

I'm curious if @yuliyan has any feedback on this part since he worked on chunking localizations (including the extra string extraction work) for lazy-loaded modules in another project.

Not much to add, but I think the suggested approach from Jetpack seems like a good solution for this use case as well. Also, I agree that having this in Core makes a lot of sense as many plugins are already implementing something on their own.

As for the string extraction, the current language pack generation process in translate.wordpress.org should work fine as long as the function names of the gettext calls are preserved (i.e. __, _n, etc.) in the output chunks.

I'm very interested to see how this topic develops and will be following the progress. Thanks for the ping!

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 6, 2023

What do you think about all the e2e flakiness and failures introduced by this PR?

I think they are all caused by the slight delay between rendering a block and actually rendering a block. We'll need to tediously update the tests ☹️

@gziolo
Copy link
Member

gziolo commented Nov 6, 2023

@jsnajdr, thank you so much for working on this prototype. It looks very promising as the changes required are minimal and fully backward compatible from the WordPress core perspective. There might be some challenges with custom editors, so we should look into integrations with other bundlers than webpack like Vite which I flagged in #55585 (comment).

There are two big topics we need to explore further:

  • Internationalization has already been covered in the discussion with great insights from @yuliyan. I think we need to collect more information from the WordPress Internationalization Team.
  • Similar to i18n concerns, it would be great to validate how the asset file for wp-block-library looks like with the changes introduced. Some script dependencies may be only required for chunks, but there is no way to enqueue them dynamically before dynamically importing the editor chunk. It's also likely that the script dependency is automatically added to the entry point, which would be a good middle ground for iterating quickly, but it might not be optimal in the long run as we might end up enqueueing the script that is never consumed.

Anyway, it's great to have this problem to solve. I'm very excited to see this proposal in place that seems like a viable approach!

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 7, 2023

Thanks @gziolo and @tyxla for your feedback. Until now I treated this work as an experiment, but I guess now it's the time to do the work needed to merge it into production. This is what I think needs to be done:

  1. Verify that i18n still works. There are still the same __() calls with the same strings, but now many of them are going to be not in block-library/index.min.js, but in individual files like block-library/blocks/paragraph/editor.min.js. But all the translations will still remain in the block-library script. Are the translation tools going to extract them correctly even from the individual JS chunks? We'll need to verify that.
  2. Are we OK with the chunk file names? Like block-library/blocks/paragraph/editor.min.js for the paragraph edit function. Also, having to put the /* webpackChunkName: "paragraph/editor" */ comment into each import call is fragile. I wish we didn't have to do that, and that it was completely automated, but don't know how to achieve that.
  3. Solve the e2e test failures, probably caused by sync UI suddendly becoming more async.
  4. Integrate the lazy-loading of blocks into the global loading indicator.

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 7, 2023

It might be one of the reasons why you only noticed 50% of the code moved to chunk as shared code still needs to be bundled in the main entry point. Do you know the number with the shared vendor chunk?

No, I don't think the 50% reduction has anything to do with shared vendor chunks and 3rd party libraries. When I look at what remains in the block-library script, it looks like this:

Screenshot 2023-11-07 at 14 21 40

It's the registration code (registerBlockType), the deprecation code, the block transforms. And icons, many SVG icons.

The only node_modules library that is there is remove-accents, because it's used in block-library/src/utils/search-patterns.js. These utils are shared code used by the Query and Template Part blocks, so there might be an opportunity to move it elsewhere.

it would be great to validate how the asset file for wp-block-library looks like with the changes introduced.

This is a good point! If a dependency is no longer used by the entrypoint block-library chunk, but only by one of the dynamic chunks, does the dependency extraction plugin still detect it and declare it as a dependency in the .asset.php file? At this moment I don't know yet.

@youknowriad
Copy link
Contributor

Are we OK with the chunk file names? Like block-library/blocks/paragraph/editor.min.js for the paragraph edit function. Also, having to put the /* webpackChunkName: "paragraph/editor" */ comment into each import call is fragile. I wish we didn't have to do that, and that it was completely automated, but don't know how to achieve that.

Can't we mark these as "entry points" (looping through the folders) in the webpack config maybe and maybe also have some config in place to replace the import with the chunk name (I'm guessing that's how we'll do it once it's ESmodules)

@luisherranz
Copy link
Member

Also, having to put the /* webpackChunkName: "paragraph/editor" */ comment into each import call is fragile. I wish we didn't have to do that, and that it was completely automated, but don't know how to achieve that.

You can automate it by switching to named chunk ids. It works fine, but extracting the block name from the named chunk id also needs to rely on some naming convention. Example:

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 7, 2023

Can't we mark these as "entry points" (looping through the folders) in the webpack config maybe and maybe also have some config in place to replace the import with the chunk name

That's an interesting thing to try out! If we do this, the connection between the import( ... ) statement and the dynamic chunk is no longer automatically managed by webpack, but we somehow need to "recreate" it. It sounds very similar to module federation.

@youknowriad
Copy link
Contributor

That's an interesting thing to try out! If we do this, the connection between the import( ... ) statement and the dynamic chunk is no longer automatically managed by webpack, but we somehow need to "recreate" it. It sounds very similar to module federation.

It is also how we handle the @wordpress/* imports currently (transform these to externals and built them as entry points)

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 18, 2023

it would be great to validate how the asset file for wp-block-library looks like with the changes introduced.

I verified this now and it works. The dependency extraction plugin has a unit test for exactly this, look up the dynamic-import test. Even when a dependency is imported only in the dynamic chunk module, it's correctly added to the parent module's .asset.php dependencies.

@youknowriad
Copy link
Contributor

I verified this now and it works. The dependency extraction plugin has a unit test for exactly this, look up the dynamic-import test. Even when a dependency is imported only in the dynamic chunk module, it's correctly added to the parent module's .asset.php dependencies.

Wouldn't this cause the JS file to be loaded anyway, canceling the benefits of the lazy loading?

@jsnajdr jsnajdr force-pushed the try/block-edit-lazy-loading branch from 83feb93 to 40fd362 Compare November 23, 2023 10:40
@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 24, 2023

Wouldn't this cause the JS file to be loaded anyway, canceling the benefits of the lazy loading?

What I wanted to say that the lazy loading of edit functions doesn't break with dependency extraction plugin, i.e., the dependencies used only inside a dynamic chunk, and not used in the entrypoint, are still included in the entrypoint's .asset.php file. They are not forgotten.

But this is an interesting question anyway. The benefit is mainly that the Edit component code is lazy loaded, cutting the size of the block-library script by half. But what if we were able to lazy load the wp-* dependencies, too?

First, we are probably not able to reliably lazy load them at this moment. To lazy load them from browser JS, the browser JS would need to know everything that the server-side WP_Scripts knows about them: all the inline scripts, transitive dependencies, etc. But we don't know that. This is more in the territory of the full-scale lazy loading in #51778.

Second, on closer inspection it turns out that this lazy loading would have zero impact. All the dependencies of block-library are already loaded by block-editor and other scripts that constitute the block editor. Also, may of them are used not only in the lazy loaded edit functions, but also in the block transforms, which remain a part of the block-library entrypoint.

Let's look at the 29 dependencies of block-library in detail.

  1. react

  2. wp-polyfill

  3. wp-element

  4. wp-primitives

  5. wp-block-editor

  6. wp-data

  7. wp-core-data

  8. wp-hooks

  9. wp-deprecated

  10. wp-private-apis

  11. wp-blocks

  12. wp-compose

  13. wp-i18n

  14. wp-keycodes

  15. wp-notices

  16. wp-url

  17. wp-html-entities (the decodeEntities util)

  18. wp-a11y (the speak util)

  19. wp-dom (the stripHTML util for safe display of content)

    These 19 are ubiquitous platform libraries and utilities loaded by any kind of editor. block-library doesn't trigger loading any of them on its own.

  20. wp-components

    Also ubiquitous, just with one note: we often import SVG primitives like SVG, Rect or Path from wp-components instead of the much smaller wp-primitives. We could refactor that because, although it's purely theoretical, we don't really want to trigger loading the much larger components library.

  21. wp-api-fetch
    Obviously ubiquitous because it's used by wp-data, but there's one notable thing: comments-related blocks use wp.apiFetch do directly fetch from the /wp/v2/comments endpoint. That's strange, shouldn't they use getEntityRecord from core-data?

  22. wp-rich-text
    Used by many block transforms, and also ubiquitous in block-editor.

  23. wp-date
    In block-library used only by edit functions of Post Date, Comment Date and Latest Posts. But block-editor uses it to display various post-related dates in the sidebar, so it's already loaded anyway.

  24. wp-blob
    Used by edit functions of media blocks (audio, video, image, gallery, file, ...) during uploads, but also by their type: 'files' transforms that convert uploaded files into blocks. block-editor also uses wp-blob for MediaPreview etc.

  25. wp-escape-html
    Used only by the Navigation Link block edit function, but already loaded by wp-rich-text and wp-components (Navigator).

  26. wp-viewport
    Used only by the edit function of the Gallery block, so it could well be a dependency of its dynamic chunk. But wp-viewport is also used by wp-interface and its "complementary area" component, so it's already loaded anyway.

  27. wp-wordcount
    Used only by the edit function of Post Time to Read. But block-editor already uses it, too, in MultiSelectionInspector and to show post stats in the Outline view.

  28. wp-autop
    Used only by the Shortcode block, it its transform that converts a shortcode markup to a block. Already loaded in wp-blocks as it's a part of the block parser.

  29. wp-server-side-render
    Used by edit functions of several blocks (archives, calendar, latest-comments, rss and tag-cloud). And not used anywhere else! Unfortunately wp-editor depends on wp-server-side-render, but only to do a deprecated back-compat re-export of the ServerSideRender. It doesn't use it.

To summarize, the only library where loading dependencies only with the dynamic chunk that uses them would have a material effect is wp-server-side-render. But we'd have to remove the deprecated re-export from wp-editor first.

@youknowriad
Copy link
Contributor

Thanks for the details @jsnajdr that makes sense for the moment. I wonder how this would be impacted when we introduce import maps. As in this case, ES modules used in the edit functions shouldn't be loaded from the start but should instead be lazy loaded as well.

@gziolo
Copy link
Member

gziolo commented Nov 24, 2023

Excellent summary, @jsnajdr, of your investigation for the dependency extraction aspect. That's very helpful as it's reasonable to leave that aspect aside for now as it looks like lazy loading edit for core blocks will work correctly and there is no noticeable benefit of lazy loading WP dependencies in the short term.

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 24, 2023

I wonder how this would be impacted when we introduce import maps.

Before introducing import maps, the scripts like wp-api-fetch or wp-editor will have to be "cleaned up" -- in the sense that they don't need any inline scripts (currently used to configure the libraries) and they only need to load a single JS module, without additional files like i18n or styles.

Then they can be loaded with a simple import '@wordpress/api-fetch' statement, and the import map will know the URL where to load them from.

We are not there yet: the existing scripts are using all the legacy WP_Scripts features that are not compatible with import maps. And currently only the server knows how to load them, i.e., it knows what <script> tags it needs to insert when a script like wp-api-fetch is requested, and what should be their contents.

The import maps work that @luisherranz is doing is starting from scratch, from a blank slate where the legacy features don't exist. On the other hand, my block lazy loading work starts with an existing legacy codebase which uses all the WP_Scripts dirty tricks. And it aims to identify and remove obstacles one by one. It's like two teams building a tunnel from both sides, and hoping to meet somewhere in the middle 🙂

@gziolo
Copy link
Member

gziolo commented Nov 24, 2023

There is one very interesting issue with how @wordpress/scripts works when it builds parent and child blocks that share the same React context. When every block has an entry point that bundles the copy of the same context, it doesn’t work as expected. It would be important to keep that in mind when working on lazy loading, as similar edge cases might start to slip in.

@flootr flootr force-pushed the try/block-edit-lazy-loading branch from 40fd362 to 87e6536 Compare November 29, 2023 10:12
jsnajdr and others added 27 commits March 6, 2024 10:45
@jsnajdr jsnajdr force-pushed the try/block-edit-lazy-loading branch from 2f1d6b8 to b007e3c Compare March 6, 2024 09:46
@jsnajdr
Copy link
Member Author

jsnajdr commented Mar 20, 2024

A summary of where we are: the problem where we got stuck is what to do with keyboard input when a block to which the input belongs is still loading and doesn't have rendered UI.

For example, consider that you type this:

> - hello

This is supposed to create a Quote block with a List inside, and a List Item that has a "hello" content. But, at the time when you're typing "hello", the Quote block edit function is still loading, and loading the edit functions of List and List Item hasn't even started!

The typing needs to go into some placeholder element which knows what to do with the input -- that it belongs to the List Item's content attribute.

And it gets harder: when you hit Enter or Backspace, then we're merging and splitting blocks.

A lot of the knowledge on how to do that resides in the edit functions. Although, in principle, it doesn't need to be there, it could be in the block type definition. We should be able to edit the blocks in a "headless mode", just by dispatching edit actions on the core/block-editor store. After all, that store already knows most of the details about the editor state, like the position of the cursor and the selection.

I think the best course of action is to pause the lazy block loading effort now, and return to it after some time working on other projects.

If we continue developing Gutenberg being aware of certain principles, like:

  • it should be possible to do things in headless mode, mounting the edit UI should be optional
  • the parse function can become async in the future, behave like it already was
  • block transforms can also become async in the future
  • block transforms should be registered independently from block registration. "define block A", "define block B", "define how to transform block A to block B" are all independent from each other

Then we can gradually improve the architecture and one day it may turn out that implementing block lazy loading is much easier on the second attempt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Block library /packages/block-library [Type] Performance Related to performance efforts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants