-
-
Notifications
You must be signed in to change notification settings - Fork 174
perf(masonry): Enable performant lazy loading via build-time dimension fetching #557
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
base: main
Are you sure you want to change the base?
perf(masonry): Enable performant lazy loading via build-time dimension fetching #557
Conversation
Previously, masonry page needs to load all images at once before displaying any content. When there were a large number of images, this would result in an excessively long page loading time - lazy load: Images will be loaded on demand only when they scroll into the visible area (supported by lazysizes) - Pre-read dimensions during construction: Pre-fetch the URL of the image to read its width and height, and then directly write these size information into the `<img>` tag - more time needed for construction
- Previously, the masonry generator fetched dimensions for all gallery images on every `hexo generate` command, causing significant build delays on image-heavy sites. - To prevent file-watcher build loops, the cache is only written to during production builds (`hexo g`) and remains read-only in development mode (`hexo s`).
Introduces a new console command to manually clear the masonry gallery's image dimension cache. This is necessary for users who update or replace existing images without changing their URLs, allowing them to force a refetch of all dimensions on the next build.
The dimension fetching generator can now process both remote URLs and local images stored within the Hexo `source` directory. User convention requires local image paths in the configuration to be relative to the `source` directory (e.g., `/images/photo.png` in mansonry.yml for `/user/blog/source/images/photo.png`).
Primary Fixes: - **Swup Navigation**: The gallery failed to render when navigated to via swup because its scripts were not re-initialized. `lazysizes` script is now loaded via `scripts.ejs`. (afraid of conflicts with existing 'lazyload.js') - **Windows Build Compatibility**: The glob patterns in `build.js` have been fixed to be platform-agnostic. All backslash path separators are now normalized to forward slashes to prevent parsing errors on Windows.
Recent Fixes (as of Aug 19, 2025)Based on further testing, I've pushed the following fixes to address issues found in the initial implementation:
The implementation should now be stable and ready for a full review. |
Redefine theme shouldn't be dependent on npm package. I am not sure about your added dependency of image-size. What's your thoughts on this? Should we transition to NPM only |
…etch method now, which is standard in modern Node.js versions(requires node version >=18.0.0, referencing: https://zhuanlan.zhihu.com/p/689482871).
The theme should not impose runtime npm dependencies on end-users. This commit vendors the `image-size` library by inlining its code into the theme's utilities. This change makes the masonry gallery feature fully self-contained and improves the theme's portability and ease of use.
…size' backend dependency why not 'js/libs': semantically incorrect, as this directory is for frontend assets and build.js won't deal with 'image-size' folder why not 'scripts/modules‘: Relocated the vendored `image-size` library to `theme_modules` to isolate it from Hexo's automatic script scanning process. This fixes the `SyntaxError` issues that occurred when the library was located within the `scripts` directory tree
Thanks so much for your guidance and feedback. You were absolutely right about avoiding runtime dependencies, and I appreciate you pointing me in the right direction. My apologies for the initial oversight. I've since refactored the entire implementation based on your question. The feature is now fully self-contained and no longer adds any runtime npm dependencies to the theme. Here’s a summary of the key changes:
Apologize again for my immature initial oversight. I should have studied the project's architecture more closely before submitting. It's my fault and I truly appreciate you taking the time to guide me through the correct approach. |
Thanks for your PR. I really appreciate your help. However I would like you to not use ChatGPT to reply to me in PR. When opensource clarity is the most important, as we don't care about how correct your grammar is or how polite you are. You can just tell me directly what you want to say. 用中文也行。 感谢
|
However, by including the whole image-size library into redefine theme is not a viable approach. As your first PR, this is really good work. But on a more practical level, I would say you will need a bit more experience on how to deal with problems like this. My approach will be rethink how we should handle the images that don't load, maybe just remove them completely, instead of depending on a library. Or, we can just write a custom function to get the size of that image. Depends on you. The current state of this PR is not mergeable. Sorry. |
… not all). Based on ITU-T T.81 standard. Application formats like JFIF and EXIF are supported. Correctness of application format that doesn't follow T.81 standard is not guaranteed
…n imageSize inside now. test good on over a hundred images. including 'png'/'jpeg'/'gif'. where 'png' and 'jpeg' can get size automatically and 'gif' cann't, but 'gif' size can be given in `masonry.yml` for gallery
@EvanNotFound 问题定位首先,关于这个PR针对的问题,实际上指的是——原来相册页面必须等到所有图片全部获取之后才会进行渲染,而当图片数量足够多,以至于全部获取时间难以忍受的时候,这个页面也会迟迟渲染不出来,令人难受。我虽然设计了一个“某图片无法获取”的案例,但那其实是为了“模拟” 【获取全部图片的时间难以忍受】的场景,此时就是无穷大。而即便所有图片的资源获取本身都没有问题,当数量足够多的时候,仍然会趋于这种情况。这也是我认为这个修改是perf而不是fix的根本原因。 这次更改现在我已经完成了我的imageSize()函数,并移除了对image-size库的依赖。您可以审阅。 关于为什么我认为只提供PNG和JPEG主流标准支持是可行的据我自己的观察,当前互联网上流通的绝大部分图像都是PNG和JPEG格式,至少我自己手上的图像绝大多数全部这两种格式。其中JPEG虽然似乎有很多变体,但是主流的仍然是基于T.81标准的。—— 线索1: 该函数已经能够实现对绝大多数图像的自动解析 测试我上传了我一个相册文件夹里的所有图像,超过100张,涵盖PNG、JPEG、GIF格式。相册页面可以正常渲染显示。值得一提的是,这里涉及到的所有JPEG图像都没有弹出
关于缓存关于为什么要实现缓存机制,我想我可能也需要解释一下:问题的根本是获取一次全部图像需要的时间长到难以忍受。即便通过预读的设计,能够将读取的时间从浏览转移到构建,但这还不够,每次构建都要额外等这么久获取图像太难熬了。考虑到图像的尺寸是一个静态不变的,而且相册配置大概率也会是处于一个不常大规模变动的设置,将已经读取到的尺寸进行可持久化存储,就能大大减少构建时的等待时间。理想情况下,每张图片只会被读取一次,均摊时间复杂度是1,即便第一次hexo g可能花点时间,但我认为也完全可以接受。 关于fetch这一方面其实我还琢磨不太透。为了移除依赖,我是使用了node原生的fetch,我个人粗浅地认为这是件好事,毕竟官方支持了fetch,无论是出于安全性还是稳定性的考量,都没道理不用。但是问题在于,node v18.0.0及之后才稳定支持原生fetch。但是我瞟了一眼发现该项目node依赖的最低要求是v12.0。疑惑之余(毕竟node都出到v22了...), 也是很纠结,难道对于fetch这么基础性的方法也要自行实现一遍以兼容低版本的node吗? 闲话因为近期开学了,琐事颇多,所以回复可能稍微有些慢。但请您相信,我仍然愿意为实现的不好的地方做出修改。您有任何不满意的地方请尽管开口说,即便水平有限,但既然发起了这次PR我就会为之负责。 |
…mely 0xffffff...); can not distinguish data 0xff00 from marker; can not deal with RST_m markers which do not length. now it can. Improvement of robustness
Description
This PR addresses a significant performance bottleneck in the masonry gallery. Previously, the gallery would fetch all images before rendering, causing slow initial page loads and a poor user experience, especially on image-heavy pages. This approach also lacked reliability, as a single failed image request could prevent the entire gallery from displaying.
This has been resolved by introducing a new Hexo generator that pre-fetches image dimensions at build time (
hexo generate
). This allows the layout to reserve the correct space for each image, eliminating content reflow (layout shift) and enabling true, high-performance lazy loading on the client-side.Key Changes & Implementation Details
Build-Time Dimension Fetching: A new hexo generator processes all images listed in the theme's masonry configuration. (
masonry.yml
)Caching Layer: To dramatically accelerate subsequent builds, fetched dimensions are stored in a persistent JSON cache (
.masonry_cache.json
)hexo masonry-cache clean
, is introduced to allow users to manually clear the dimension cache when neededAbout local images
User convention requires local image paths in the configuration to be relative to the source directory
eg: For an image located at
(your-blog)/source/images/photo.png
, the configuration path inmasonry.yml
should be/images/photo.png
.test
Functionality has been verified in a local environment. A specific test case was designed to ensure reliability under adverse conditions.
Test Setup
Before (Previous Behavior)
This single failure blocked the rendering of the entire gallery. No images were displayed, even the ones that were available locally. The page appeared stuck in a "loading" state (I've waited for over 10 minutes)

After (With This PR)
The broken image request failed gracefully(get the fallback sizes 1:1) without impacting the rest of the page. And all other images, including the locally hosted one, rendered correctly and immediately.

other words
As a student, this is one of my first attempts at a larger open-source contribution. There may be some immature aspects in my implementation, and I would be very grateful for any guidance or feedback. Thank you for your time and consideration!