记得我刚毕业那会,前端还是jQuery的天下,没有什么项目是一个jQuery搞不定的,如果有,那就再加个插件。
后来慢慢出现了一些前端模块化工具,直至seajs的出现,把模块化推向了一个高潮,期间听说了gmail前端有44万行的JS代码,也是非常震惊。
现在的前端为了更好的用户体验,更快地适应业务,逻辑正在变得越来越复杂,同时也带来越来越多的问题。
体积臃肿的前端
使用webpack的同学对下面这张图很熟悉了,一般我们用webpack打出来的包,都会包含一大堆东西,node_modules文件内容甚至能占到90%以上。
冰山效应
很多时候,用户打开一个页面,同时也加载了很多在这个页面上并不会运行的代码,甚至是永远都不会运行的代码,页面上的这些代码就像海洋里的冰山,运行的只是海平面上很小一部分,很大一部分在海平面下的代码,仅仅只会拖慢我们首页的打开时间。
按需加载
从有模块化起,貌似就有了按需加载功能,很多工具都提供了按需加载的功能,以webpack为例:
const jQuery = await import( 'jQuery' ); const model = await import( './model/data.js' );
webpack内置函数 import
很容易就可以实现按需加载的功能,看起来似乎问题就已经解了?然而这只是问题的第一步,仅仅有了按需加载的能力,问题也会随之而来。
预加载
按需加载必然会带来一次网络请求,假设一个场景:
用户点击一个按钮,然后跳出一个弹框。
普通情况下,点击按钮之后发生的事情如下:
点击按钮(ms) -> 渲染弹框(ms)
如果我们把弹框做按需加载,那么点击按钮之后发生的事情如下:
点击按钮(ms) -> 加载弹框代码(s) -> 渲染弹框(ms)
中间多了一个步骤,并且是按秒计算的从网络加载代码,点击一个按钮之后需要等待一段时间,才会弹出弹框,用户操作非常不流畅,如果存在大量这种场景,体验或许还不如不用按需加载。
有什么办法能解这个问题么?一种方案是: 预加载
,请看下图:
预加载步骤
- 首先页面上收集用户的点击行为日志。
- 拿到点击行为日志,得出用户最常用的操作路径,这部分数据在静态打包的时候注入到前端。
- 每个人的操作路径会有区别,这里可以在前端也记录一份当前用户的点击数据,这里很多时候虽然本地都已经有缓存了,不过提前把缓存加载到内存,也可以节省很多时间。
- 在浏览器空闲时,按照第二步和第三步的数据计算权重,最终决定加载顺序。
- 同时,前端可以根据鼠标运动轨迹,实时地来预测下一步可能会加载的内容,作为一个旁路辅助。这里目前并没有想到一个很好的办法。
缓存
鉴于目前前端代码普遍存在于CDN的情况,按需加载带来的另外一个非常有优势的地方在于CDN缓存,假设我们有一份 jQuery
,有两个文件分别依赖了jQuery,情况如下:
a.js
var jQuery = function(){ /*jquery代码*/ } jQuery.query( 'xxx' );
b.js
var jQuery = function(){ /*jquery代码*/ } jQuery.query( 'xxx' );
用户在打开A页面和B页面的时候,分别需要下载 a.js
和 b.js
,这时候重复下载了两份 jQuery
,即使他们是一样的,同时在用户的磁盘,也缓存了两份 jQuery
。
按需加载后的CDN缓存
那么如果我们用按需加载的方式呢?
var jQuery = function(){ /*jquery代码*/ }
a.js
var jQuery = import( 'http://cdn.com/jquery.js' ); jQuery.query( 'xxx' );
b.js
var jQuery = import( 'http://cdn.com/jquery.js' ); jQuery.query( 'xxx' );
jQuery.js
只会被下载一次,所有页面如果用的都是一个cdn地址,那么只要用户打开过任何一个页面,之后的页面即使用户从未打开过, jQuery
也已经被缓存,这带来的是一个 1+1>2
的效果。
理想中的按需加载
最后说一说我理想中的按需加载,看下图:
- 逻辑层和展示层完全剥离。
- 用户操作一个页面的顺序,一定是:
- 眼睛看到展示层,发现有一个按钮。
- 大脑告诉你,这个是一个按钮,是可以点击的。
- 用手控制鼠标去点击按钮。
- 执行点击按钮之后事情。
- 首屏加载的时候,我们可以只加载展示层,逻辑层代码一定是在用户点击按钮之后才会执行。
- 首屏加载完成,利用用户看页面,并点击按钮的时间,预加载逻辑层代码,实现最快速的首屏展示。
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。