让普通网站页面跳转时显示加载进度条 - 一种实现思路

2023-04-23 22:37:18 +08:00
 lete

为了方便大家使用,我已经将代码开源,如果觉得不错的话点个 ✨star✨ 支持一下下

欢迎大佬们一起参与维护😁👍

开源仓库: https://github.com/Lete114/page-load-progress

体验 Demo: https://lete114.github.io/page-load-progress/test/index.html

有没有人注意到 GitHub 和 YouTube 这些网站在页面跳转时,网页顶部总是会显示一个加载进度条?

比如:

这个效果是怎么实现的呢?其实我也不知道,也并没有特意去查看或逆向过它们 Web 端的 js 代码,我能想到的办法就只有使用 pjax 或者 turboturbolinks。但它们也有一些弊端,需要网站管理员去维护,甚至重新编码调整

如果页面需要交互或者需要 js 操作 dom 的,如获取页面上的所有外链 a 标签,给 a 标签的 href 属性值开头添加跳转拦截 https://link.example.com/?target=,js 不会再次重载,因为页面跳转是不刷新网页的(它只负责替换网页中变化的结构),得手动重载 js ,以上提到的库它们都有提供重载 js 的解决方案,需要你对网站进行一些调整或代码调整

正文

那么如果不使用这些库如何让传统的普通网站(指点击链接后页面跳转会刷新页面)实现这一功能呢?

prefetch

关于 prefetch 的作用自己看 MDN prefetch

prefetch 兼容性表格 (Safari 全版本不支持 prefetch)

自从 IE 浏览器下岗后,Safari 浏览器就变成了新一代 IE 浏览器

使用 link 标签 rel 属性的 prefetch 值,然后通过监听 link 标签的 onload 事件即可知道,页面已经加载完成

function prefetch(url){
    var link = document.createElement('link')
    link.rel = 'prefetch'
    link.href = url
    link.onload = function(){
      window.open(url)
    }
    document.head.appendChild(link)
}

阻止 a 标签默认事件

prefetch 只对当前标签页且同源的 HTML 管用,如果是其它资源如 js 、css 、font 、img 等可以不是同源,但必须在同一个标签页内才管用,否则就会失效 (我说的也不一定对,MDN 也没有这方面的详细说明。以上只是我在实际测试中得到的结论,如有问题欢迎各位大佬在评论区补充,感谢)

获取页面上的所有 a 标签(内链)

const aTags = document.querySelector('a[href]:not([target^=_]):not([download])')
aTags.forEach((el)=>{
  window.addEventListener('click', function (event) {
    const target = event.target
    const href = target.href
    if(href && location.origin === new URL(href).origin){
      event.preventDefault()
      // 写进度条的逻辑
      prefetch(href)
      // 或者改造 prefetch 函数将 onload 最为回调函数传入
      // prefetch(href,()=> {
      //   // 触发 onload 后让进度条加载到 100% 然后就调用 window.open(href) 完美实现
      //   window.open(href)
      // })
    }
  })
})

总结

以上便是页面跳转加载进度条的实现思路,以及核心代码逻辑,如有问题欢迎各位大佬指出,也欢迎各位大佬 PR 为开源项目做贡献

1319 次点击
所在节点    程序员
8 条回复
sunwang
2023-04-24 10:50:21 +08:00
不错!有个疑问,对于 Prefetch 来说,浏览器貌似不一定会加载这些资源,使用 preload 是否更加合理?
lete
2023-04-24 11:26:20 +08:00
@sunwang #1 浏览器不一定会加载这些资源是说明意思?我没遇到过类似的问题,能详细描述一下吗?

不知道为什么我用 preload 的时候控制台总是警告 "ink rel=preload> uses an unsupported `as` value",并且 Netwoker 中并没有发送请求(也可能是我不会用)

```js
const l = document.createElement('link')
l.as = 'document' // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload#what_types_of_content_can_be_preloaded
l.href = url
l.rel = 'preload'
document.head.appendChild(l)
```
kkocdko
2023-04-24 12:31:08 +08:00
sunwang
2023-04-24 14:30:13 +08:00
@lete 可以看看 https://www.jianshu.com/p/8920dc078689 这篇文章,mdn 上的链接: https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ ,大概意思是:利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档,并不会保证一定会获取。
sunwang
2023-04-24 14:38:02 +08:00
@lete 关于报错我搜到了一个问题: https://github.com/ampproject/amphtml/issues/2492 ,貌似是 chrome 的问题?不太清楚
lete
2023-04-24 15:23:04 +08:00
@sunwang #4 在我的实际测试过程中,浏览器并没有处于空闲状态时(Netwoker 中有资源还在 Pending) prefetch 依然会正确执行进行预取

而我把 prefetch 改为 preload 后 Netwoker 中并没有发现 preload 发送请求

https://stackblitz.com/edit/page-load-progress-preload-and-prefetch?file=index.html
lete
2023-04-24 15:48:59 +08:00
@sunwang #5 该 issues 是 2016 年开启的,如今仍然存在问题,而 https://bugs.chromium.org/p/chromium/issues/detail?id=593267 也并没有说明好用的办法,对于 as=document 存在着争议,已经过去了那么多年,问题仍然存在,至今仍未实现 as=document ,所以大家都选择拥抱 prefetch
sunwang
2023-04-24 19:23:41 +08:00
@lete 确实,看来只能用 prefetch 了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/934901

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX