在Svelte Sapper应用中实现延迟加载Markdown文件内的图片

问题的提出

Markdown 允许您将纯文本转化为格式化的元素。一旦你了解了 Markdown 的语法,就可以轻松快速地编写网页文档。在当前流行的静态博客应用中,很多都使用 Markdown 格式来存储博客文章。如 Hugo、Jekyll、Hexo 等均支持或依赖于带有 “前置内容(front matter)” 的 Markdown 文件作为元数据;Svelte 的官方博客也是如此,其源码 repo 参见:https://github.com/sveltejs/svelte/tree/master/site

在 Markdown 中,图像(Images)语法格式如下:

内联方式(标题可选): 
![alt text](/path/to/img.jpg "Title")

引用方式:
![alt text][id]
[id]: /path/to/img.jpg "Title"

以上两个示例产生相同的输出:

<img src="/path/to/img.jpg" alt="alt text" title="Title" />

当一个 Markdown 文件中引用了大量图片的时候,如何加快页面的渲染速度就成了一个首当其冲需要解决的问题。在这里,提出两种解决方案及其在 Svelte Sapper Blog 应用中的实例:

  • 原生延迟加载(懒加载)图像(Native lazy loading images)
  • JavaScript 驱动的图像懒加载(使用 lazysizes)

两种实现方法都是采用成熟的 JavaScript Markdown 库来对 Markdown 文件进行处理,因此也适用于其他 web 框架或应用。

原生延迟加载图像

延迟加载是一种提高网站或 web 应用程序性能的方法,它通过延迟加载 “折页下(below the fold)” 的内容来最大化 “折页上(Above the fold)”的图像和 iframe(有时还有视频)的渲染速度。

注:“折页下” 指的是用户需要向下滚动(scrolled down)才能看到的内容。

现代浏览器实现了内置的延迟加载(懒加载),可以使用图像和 iframe 上的 loading 属性来启用延迟加载。原生延迟加载规范(native lazy loading specification)仍在开发中。目前,大多数流行的 Chromium 驱动的浏览器(Chrome、Edge、Opera)和 Firefox 都支持 caniuse.com 有关于跨浏览器支持的详细信息。其语法大致如下:

<img src="/path/to/img.jpg" loading="lazy" alt="..." />
<iframe src="/path/to/video-player.html" loading="lazy"></iframe>

在 Chrome 76+ 浏览器中,您可以使用 loading 属性完全延迟屏幕外图像的加载,这些图像可以通过滚动来达到:

<img src="image.png" loading="lazy" alt="…" width="200" height="200">

以下是 loading 属性支持的值:

  • auto:浏览器默认的懒加载行为,这与不包含该属性相同。
  • lazy:推迟资源的加载,直到它到达计算出的离视口的距离。
  • eager:立即加载资源,无论资源在哪里。立即加载资源,不管它在页面上的位置。

由此可见,对于在 “折页上” 的重要图片,可以设置 loading="eager" 使其即刻加载,尽快显示在用户眼前。

在最新版本的 Chrome 浏览器上「我的版本是 87.0.4280.88(正式版本) (64 位)」,在地址栏输入 "chrome://flags/",搜索 “lazy image”,可以看到 “Enable lazy image loading” 的功能开发,并且有 “Enabled (Automatically lazy load where safe even if not marked 'loading=lazy')”——“开启(即使没有标记 'loading=lazy',也会在安全的情况下自动懒加载。” 如下图所示:

这也就意味着,将来可能不需要做任何额外的工作,Chrome 浏览器就能真正原生支持图片懒加载了。

根据以上所述,标准 Markdown images 语法格式对应于 HTML 语言,所支持的 <img> 标记中的属性只有: src、alt 和 title。因此,对于原生支持懒加载的现代浏览器而言,至少需要在转换过程中加上这个属性 loading="lazy"。

JavaScript 驱动的图像延迟加载(使用 lazysizes)

为了懒加载图片或 iframe,一个很常见的做法是通过用一个类似 data-src 的数据属性来代替适当的 src 属性来标记它们,然后依靠 JavaScript 解决方案来检测图像/iframe 何时接近网站的可见部分(通常是因为用户向下滚动),并将数据属性复制到适当的属性中,然后触发其内容的延迟加载。

lazysizes 是最流行的懒加载图片库。它是一个脚本,可以在用户浏览页面时智能加载图片,并优先处理用户即将遇到的图片。它还包括一组可选的插件,以进一步扩展其功能。

Svelte Sapper Blog 应用中的具体实现

先决条件

一、原生图像懒加载测试

1)用 Sapper 模板生成你的应用项目

在你的工作目录下,运行

λ npx degit mikenikles/sapper-template-with-markdown sapper-markdown-image-lazyload
npx: 1 安装成功,用时 5.794 秒
> cloned mikenikles/sapper-template-with-markdown#master to sapper-markdown-image-lazyload
2)安装依赖
λ cd sapper-markdown-image-lazyload
λ yarn
3)运行应用
λ yarn dev
yarn run v1.22.4
$ sapper dev
✔ server (1.5s)
✔ client (1.5s)
✔ service worker (32ms)
> Listening on http://localhost:3000
4)在浏览器地址栏输入: http://localhost:3000,即可访问你的博客应用了。
5)增加包含多幅图片的 Markdown 文件

博客文章的 Markdown 文件保存在 sapper-markdown-image-lazyload\src\posts 目录下。

新建一个文件 sample-blog-with-images.md,内容如下:

---
title: 'Sample post with images'
slug: 'sample-post-with-images'
---

# Image loading demo

## Scroll to see images fade-in

![Example image](https://picsum.photos/seed/1/800/800 "Example image 1")

![Example image](https://picsum.photos/seed/2/800/800 "Example image 2")

![Example image](https://picsum.photos/seed/3/800/800 "Example image 3")

![Example image](https://picsum.photos/seed/4/800/800 "Example image 4")

![Example image](https://picsum.photos/seed/5/800/800 "Example image 5")

![Example image](https://picsum.photos/seed/6/800/800 "Example image 6")

![Example image](https://picsum.photos/seed/7/800/800 "Example image 7")

![Example image](https://picsum.photos/seed/8/800/800 "Example image 8")

刷新浏览器窗口,或访问 http://localhost:3000/blog, 可以看到 "Sample post with images" 已经列在其中了。点击访问,这时候图片的加载是标准的全部加载方式。

6)扩展 Marked 覆盖标准 image renderer 标记

mikenikles/sapper-template-with-markdown 模板采用了 markedjs/marked 库对 Markdown 文件进行解析和编译,这里我们只需要利用 Marked 的扩展能力,覆盖默认输出的 image 标记即可。

编辑 sapper-markdown-image-lazyload\src\routes\blog\_posts.js

在 L12 行之后加入如下内容:

const renderer = new marked.Renderer();
renderer.image = (href, title, text) => {
  text = typeof text === 'string' ? text : 'no description';
  return `<img loading="lazy" src="${href}" alt="${text}" width="800" height="800">`;
  // If you use lazysizes, you need to return something in the following format
  // return `<img class="lazyload" src="${href}" alt="${text}" width="800" height="800">`;
};

然后在现在的 L31 行(原 L22),由

html: marked(postFrontMatter.body)

改为

html: marked(postFrontMatter.body, {renderer})
7)访问测试
在浏览器窗口,按 F12 打开开发者工具,切换到 “Network” Tab,为了方便观察可以按下 “Filter” 这一排中的 "Img",只观察 Img 的加载情况。然后访问 http://localhost:3000/blog/sample-post-with-images。正常的情况下,视口之外的图像是随着你页面的向下滚动而逐一加载的。在 Firefox Browser 83.0 (64 位) 下测试,也是同样的结果。

二、使用 lazysizes 懒加载图像测试

步骤与(一)基本上是一样的。有小小不同:

1)在第6步,编辑 sapper-markdown-image-lazyload\src\routes\blog\_posts.js

在 L12 行之后加入如下内容:

const renderer = new marked.Renderer();
renderer.image = (href, title, text) => {
  text = typeof text === 'string' ? text : 'no description';
  // If you use lazysizes, you need to return something in the following format
  return `<img class="lazyload" src="${href}" alt="${text}" width="800" height="800">`;
};

2)在 Sapper 应用的 sapper-markdown-image-lazyload\src\src\template.html 文件头部 </head> 之前添加 lazysizes script

%sapper.head%
  <!-- If you use lazysizes, you need to add lazysizes script in the following format -->
  <script src="lazysizes.min.js" async=""></script>

这个在做原生测试的时候是注释掉的,这时打开注释即可。

结论

在需要对 Markdown 文件进行解析的情况下,无论哪种方式都需要写一点 JS 代码。跟原生图像懒加载支持相比,lazysizes 具备更好地跨浏览器支持能力,lazysizes 甚至还有一个 “原生加载扩展(native loading extension)”,可在支持原生懒加载的浏览器中自动转换 img.lazyload/iframe.lazyload 元素。在当前的环境下,更推荐采用 lazysizes 这样的 JavaScript 驱动的图像延迟加载方案。

为方便测试,代码已经上传到 GitHub,repo 是:vulcangz / sapper-markdown-image-lazyload

参考和链接


Like:
0
To the top