思考
关于图片懒加载的方法,基于Intersection Observer API实现起来很简单。
但是在Astro中实现起来却不同,因为这里要实现的不是网站上的图片懒加载,而是博客中图片的懒加载。
Astro中的博客是使用Markdown写的,在Frontmatter中添加layout
来指定渲染的组件,我们在打开博文的时候,组件已经加载。博文中的image标签都有了src属性,已经去发送请求获取数据了。
我一开始的想法是,在打开页面之后,图片发送请求之前,找到一个钩子函数,把image的src属性删除,存到data-src中。一个比window.onload
更早的钩子函数,但是没有这样的方法。
所以我就想在把markdown渲染成html的时候,把image标签进行处理。这个思路应该是可行的,我就去翻看Astro的文档,还真找到了配置方法markdown.remarkPlugins。
顺着官网的线索在github中找,我找到了这个包:remarkjs/remark,里面介绍了一段代码示例可以将h标签的层级缩小一级,即h2标签会变成h3标签。
1import { visit } from 'unist-util-visit'2
3function myRemarkPluginToIncreaseHeadings() {4 return (tree) => {5 visit(tree, (node) => {6 if (node.type === 'heading') {7 node.depth++8 }9 })10 }11}
原始md文档:
1# Hi, Saturn!
页面html为:
1<h1>Hi, Saturn</h1>
格式化后,页面html为:
1<h2>Hi, Saturn</h2>
从上面的示例中,我感觉到这个插件能够满足需求,于是开始动手实现。
实现
在astro.config.mjs
中添加如下代码:
1import { visit } from 'unist-util-visit'2
3function myRemarkPluginToLazyLoadImage() {4 return (tree) => {5 visit(tree, (node) => {6 if (node.type === 'image') {7 // 将url属性给alt,另外清空url,这样页面加载的时候,图片没有url属性就不会加载8 node.alt = node.url9 node.url = ''10 }11 })12 }13}14
15export default defineConfig({7 collapsed lines
16 ..., // 其他配置17 markdown: {18 remarkPlugins: [myRemarkPluginToLazyLoadImage],19 // 一定要加上这个,否则不会把md处理为html,而只处理插件内的代码20 extendDefaultPlugins: true,21 }22})
在引入md的astro组件中,添加如下代码:
1<script>2/* 查找到博客中的所有img标签 */3var markdownBody = document.querySelector(".markdown-body");4let images = markdownBody.querySelectorAll("img");5
6const callback = (entries) => {7 entries.forEach((entry) => {8 if (entry.isIntersecting) {9 const image = entry.target;10 const data_src = image.getAttribute("alt");11 image.setAttribute("src", data_src);12 observer.unobserve(image);13 }14 });15 };7 collapsed lines
16/* 给每个img标签添加监听方法 */17 const observer = new IntersectionObserver(callback);18 images.forEach((image) => {19 observer.observe(image);20 });21}22</script>
到这里,我们的图片懒加载就完成啦!