某天上班,偶然打开网页版的喜马拉雅,随手点了首推荐音频。。。于是伴随着动词大词,动词大词,继续逛着它的首页。

一个不小心,又点进了老郭的相声,哎,还是木有更新,不过老段子也可以再听听,正当老夫要点播的时候,突然!哎呀!卧槽!卧槽!牛逼啊!我都逛了这么多页面了,耳机里的”动词大词”竟然连顿都没打,当时还打开了一下本地播放器,以为是它播放的音乐。

详细情形是这样的:我在喜马拉雅的 a 页面播放了音乐,然后又去 b 页面,c 页面,音乐却并没有卡顿现象,稳如死狗!注意,这里说的是连卡顿都没有,不是说跳到别的页面会继续播放。

国内的在线音乐平台有很多,实现喜马拉雅的这种哥还是头一回见啊。大部分都是采取的本地缓存音乐进度,跳到别的页面再读取进度,继续播放,但切页面的时候肯定是会有卡顿的。所以这里给喜马拉雅的用户体验 32 个赞!

那么问题来了,这种网页播放器是如何实现的?

简单点说,其实就是使用了 html5 的 api: History.pushState

1. 页面中,所有的 a 标签,js 对其做了点击事件的拦截,如果是支持该 API 的浏览器不会发生跳转,而是通过 ajax 请求该链接,返回结果是一串类似于如下的 json:

1
2
3
4
{
"html": "<div class='xxx'>xxx</div>",
"title": "页面标题"
}

2. 通过 js 将 html 里的内容插入到页面的指定容器中去。

3. 使用 Histoyr.pushState,强势插入一条浏览历史。
于是当前浏览器的页面内容会被更新,访问地址会变成该 a 标签的链接,页面标题变成返回结果中的 title 。所以看起来就会和跳转页面一样,但实际上页面并未发生过任何跳转,至始至终都是在一个页面中进行各种 ajax 请求而已,所以歌曲的播放当然不会卡顿了。

好了,揭秘完毕!小伙伴们是不是也可以使用上述步骤来创建自己的单页应用了呢?别急,待我先喷一喷这种单页应用的缺点先!

  • 需要后端配合渲染页面。区分 ajax 请求和浏览器请求,即如果是 ajax 请求页面,则返回上述 json,否则正常返回整个页面内容以供跳转。
  • 需要 js/css 路由(某些框架就可以实现)或初次载入即加载完所有 js/css,并解决冲突。

因为 ajax 请求返回的仅仅是 html 部分,没有也不能够包含页面对应的 js/css,一旦包含了,很可能就会形成全局变量冲突,事件冲突,样式混乱等问题,请求的页面越多,问题越严重!这对于前端的小伙伴来说真是日了狗了。。没法再轻松愉快的维护各自页面各自的静态资源了,写着 a 页面还得操着 b 页面的心,遵循一堆约定。而且本人也比较反感因为使用了某些框架(比如:react.js)就要放弃 html 各种纯天然的写法,毕竟原生态的,才是我们最熟悉的。

  • 感觉两条太少了,凑个数行不。。。

我知道,看到这里肯定有人想喷我了。时代在进步,现在前端各种 mvc 框架完全能够实现这种需求,为毛还嫌麻烦?我想说,每种框架都有它的适用情景,也都有值得我们学习的地方,but!每个团队的开发模式,编程水平,项目时间都不同,适合自己的才是最好的。并且现代浏览器的性能,人们的带宽都取得了长足的进步,有时候为了一点性能提升而要改变适合整个团队的编码习惯,是不是有点舍近求远了呢?

而优点当然也有了,就 1 条!

太特么省流量了。因为各个页面会有很多相同部分,ajax 仅返回异同部分即可,所以页面的体验会更加流畅,并可以有效减少 http 请求。。好像不止 1 条,不管了。

那么接着 html 原生写法的思路,有没有一种方式能够让我们不用管前后端配合,也不用鸟静态资源路由,就像做多页站点那样无痛创建单页应用呢?相信你脑海中已经出现那个古老科技了,对!就是 iframe。

  1. 用 iframe 将页面包起来。
  2. 播放器给挪到外面去,这样 iframe 中的页面爱咋跳咋跳,同样也不会影响到外面播放器的运行。
  3. 使用 html5 的另一个 api: History.replaceState 改改浏览器地址,改改标题
  4. iframe 的跳转又天然会插入一条浏览历史,简直完美!

利用以上原理,本人写了一个插件:singlePage —— 帮助大家更自然地创建单页应用,没有那么多的屁事儿,你要做的仅仅只有一步,引入它!

查看演示(仿喜马拉雅播放器)


 评论