找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2045

积分

0

好友

291

主题
发表于 6 天前 | 查看: 20| 回复: 0

近年来,HTML 的一项重要改进是引入了 loading="lazy" 属性,它可以应用于图像或 iframe 元素。这个属性会指示浏览器延迟加载资源,直到元素滚动进入视口为止。这极大地提升了页面加载性能。

<img src="/images/your-image.png" loading="lazy">

这种做法简单又实用。但你是否想过,能否对 JavaScript 脚本也实现类似的懒加载效果呢?这样一来,你就可以按需加载组件,只在它们真正被需要时才引入,从而进一步优化应用性能。

幸运的是,<img> 元素还提供了 onloadonerror 属性,允许我们在图片加载成功或失败时执行脚本。

<img
    src="/images/your-image.png"
    loading="lazy"
    onload="() => console.log('image loaded')"
>

这里的 onload 回调只会在图片加载完成后触发。如果图片本身是懒加载的,那么这个回调也只会当图片进入视口时才执行。看,一个基于图片懒加载触发的脚本执行机制就出现了!

不过,上面的例子还不够完善。首先,页面上会多出一张你可能并不需要的图片;其次,你需要将 JavaScript 代码内联写入属性,这在一定程度上背离了模块化与懒加载的初衷。那么,我们该如何改进呢?

图片本身可以“什么都不是”。正如前面提到的,onerror 回调会在图片加载失败时触发。这并不意味着你必须将 src 指向一个不存在的文件,那样会导致控制台充满 404 错误。

如果 src 属性指向的“图片”实际上并非有效的图片格式,onerror 回调同样会被触发。最简单的方法是利用 data: 协议来“错误地”编码一个图片。这样做的好处是,不会向控制台输出任何缺失图片的警告。

<img
    src="data:,"
    loading="lazy"
    onerror="() => console.log('image not loaded')"
>

虽然这仍会在页面上显示一个破损的图片图标,但稍后我们会解决它。现在,我们面临另一个问题:如何避免内联 JavaScript?答案就在现代 JavaScript 的 ES 模块动态导入功能中。

我们可以利用动态 import() 语法,在 onerror 事件触发后异步加载所需的脚本模块:

<img
    src="data:,"
    loading="lazy"
    onerror="import('/js/some-component.js').then(_ => _.default(this))"
>

注意:此技巧同样适用于 onclickonchange 等其他事件。
注意:代码中的下划线 _ 只是接收导入模块的一个简写,你也可以写成 .then(Module => Module.default(this))

这行代码做了什么?它会在图片(实际上是 data: URL)“加载失败”时,动态导入 /js/some-component.js 模块,并执行其默认导出函数,同时将 this(即当前的 <img> 元素)作为参数传递进去。

那么,some-component.js 模块可能是什么样子呢?

// some-component.js

export default element => {
    element.outerHTML = `
        <div class="whatever">
            <p>Hello world!</p>
        </div>
    `;
}

你可能会注意到,在 onerror 回调中,我们将 this 作为参数传给了默认导出函数。这是因为在事件处理函数的上下文中,this 指向触发事件的 <img> 元素本身。

现在,你可以在导入的模块中,轻松地使用 element.outerHTML 将这个“破损的图片”替换为你想要的任何 HTML 标记。这样一来,一个真正按需懒加载的脚本组件就实现了!

缓存和传递参数

如果你在同一个页面上多次使用这项技术,浏览器可能会因为 src 属性相同而进行缓存。为了避免这种情况,你需要为 data:, 添加一个“缓存破坏”参数,例如一个随机数:

<img
    src="data:,abc123"
    loading="lazy"
    onerror="import('/js/some-component.js').then(_ => _.default(this))"
>
<img
    src="data:,xyz789"
    loading="lazy"
    onerror="import('/js/some-other-component.js').then(_ => _.default(this))"
>

data:, 后面的字符串可以是任意内容,只要确保它们彼此不同即可。

向懒加载的组件传递参数也非常简单。你可以在 HTML 中使用 data-* 属性来存储数据:

<img
    src="data:,"
    loading="lazy"
    data-message="hello world"
    onerror="import('/js/some-component.js').then(_ => _.default(this))"
>

由于我们将 this(即 <img> 元素)传递给了函数,你可以在模块中通过 dataset 对象来访问这些自定义数据:

export default element => {
    const { message } = element.dataset
    element.outerHTML = `
        <div class="whatever">
            <p>${message}</p>
        </div>
    `;
}

这种方法提供了一种纯 HTML 声明式、无需构建工具即可实现组件懒加载的巧妙思路。如果你想深入探讨更多前端性能优化与模块化实践,欢迎在 云栈社区 与其他开发者交流分享。




上一篇:0day漏洞利用工具集:Java/CMS/安全产品等EXP与POC整理,用于网络安全研究与渗透测试
下一篇:Meta重金收购Manus:一场关于AI终端与智能体(Agent)主导权的争夺
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 19:01 , Processed in 0.188738 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表