V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lete
V2EX  ›  程序员

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

  •  1
     
  •   lete ·
    Lete114 · 2023-04-23 22:37:18 +08:00 · 1484 次点击
    这是一个创建于 565 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

    比如:

    • 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 为开源项目做贡献

    8 条回复    2023-04-24 19:23:41 +08:00
    sunwang
        1
    sunwang  
       2023-04-24 10:50:21 +08:00
    不错!有个疑问,对于 Prefetch 来说,浏览器貌似不一定会加载这些资源,使用 preload 是否更加合理?
    lete
        2
    lete  
    OP
       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
        3
    kkocdko  
       2023-04-24 12:31:08 +08:00 via Android
    sunwang
        4
    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
        5
    sunwang  
       2023-04-24 14:38:02 +08:00
    @lete 关于报错我搜到了一个问题: https://github.com/ampproject/amphtml/issues/2492 ,貌似是 chrome 的问题?不太清楚
    lete
        6
    lete  
    OP
       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
        7
    lete  
    OP
       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
        8
    sunwang  
       2023-04-24 19:23:41 +08:00
    @lete 确实,看来只能用 prefetch 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   967 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:09 · PVG 05:09 · LAX 13:09 · JFK 16:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.