代码优化
Contents
1 CSS和JS的位置
-
考虑到HTML、CSS、JS三者存在互相堵塞的情况(详见“浏览器渲染原理”):
-
CSS建议放在head标签中:
- 由于CSS的下载与解析并不阻塞HTML的解析,所以尽早下载即可
- 防止被JS阻塞(CSS优先,毕竟用户是先看到页面的)
- 对于内联的JS,可以放在head标签中CSS之前
-
JS建议放在body标签的最后:
- 可以直接访问DOM,无需监听DOM Ready事件
- 避免阻塞html的解析
2 白屏与闪烁问题
- 如果CSS下载过慢,将导致页面白屏(页面空白)或闪烁(样式从无到有),根据浏览器不同以及link标签的位置不同,出现的结果也不相同。
- 在Chrome浏览器中,无论link标签在head标签中还是在body标签的首部,都会出现白屏现象
- 在FireFox浏览器中,如果link标签在head标签内,则出现白屏;如果link标签在body标签的上部,则出现闪烁
- 实际上CSS并没有阻塞HTML的解析,但确实是阻塞的JS的下载与执行过程。
3 代码拆分技巧
-
如果打包后仅生成一个js文件的话,当服务端做一个很小的改动,那么客户端就需要更新整个js文件,造成资源的浪费。
-
因此建议将js代码根据js文件改动频率进行拆分:
-
runtime.js,该文件是webpack自己生成的代码,跟我们的代码隔离开比较好。假设我们不修改代码,只是将webpack升级,那么就只有runtime.js会改变
1 2 3
optimization: { runtimeChunk: 'single', }
-
vendors.js,该文件包含全局使用的第三方基础库,比如React全家桶和Vue全家桶,一般来说我们不会去修改这部分代码,只会升级它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
optimization: { splitChunks: { cacheGroups: { vendor: { priority: 10, minSize: 0, // 如果不写0,由于React文件尺寸太小,会直接跳过 test: /[\\/]node_modules[\\/]/, // 为了匹配/node_modules/或\node_modules\ name: "vendors", chunks: 'all', // async表示异步加载,initial表示同步加载,all表示以上两者 // 这三行的整体意思就是把两种加载方式的来自node_modules目录的文件打包为vendor.xxx.js // 其中vendor是第三方的意思 } } } }
-
common.js,该文件包含公司内部的基础库,比如公司内部的UI组件库,大概几个月会被更新或升级一次
1 2 3 4 5 6 7 8 9 10 11 12 13
optimization: { splitChunks: { cacheGroups: { common: { priority: 5, minSize: 0, minChunks: 2,// 如果一个模块被其他两个模块引用了,说明该模块是公共库 chunks: 'all', name: 'common' } } } }
-
page-index.js,该文件只包含当前页面的逻辑,每周都会变动。不同的页面有不同的index.js
1 2 3 4 5 6
// 多页面可以通过配置多入口来做 // 若文件过多,也可以单独遍历全部入口配置,再传入entry中 entry: { main: './src/index.js', admin: './src/admin.js' }
-
-
CSS代码拆分也是一样的思路
4 JS动态导入
- 原生js动态导入
|
|
- Vue动态导入
|
|
- React动态导入
|
|
5 懒加载
-
懒加载就是一开始不加载,但需要用到的时候再加载。这听起来跟动态导入很像,不过懒加载一般指的是非JS资源,比如图片和样式等。
-
常见的懒加载思路举例:
- 页面中有大量商品图片需要展示。假设代码为
<img src='product.png'>
。 - 可以用一个1k大小的占位图片代替所有商品图片。代码改为
<img src='placeholder.png' data-src='product.png'>
。也就是说,创建一个自定义属性data-src
存放真正需要显示的图片路径,而img自带的src放一张大小为1 * 1px的图片路径。 - 在某个时刻(如页面加载的一秒钟后、用户滚动页面且快要看到下一页的产品时)使用JS去加载商品图片,替换掉占位图片。即当页面滚动直至此图片出现在可视区域时,用js取到该图片的data-src的值赋给src。
- 页面中有大量商品图片需要展示。假设代码为
|
|
- 淘宝、天猫这类电商网站大量采用了这种方案,提速效果明显,而且可以为公司节省很多带宽成本。
6 预加载
-
懒加载导致一些资源的加载被推迟,影响了用户体验。那么我们能不能把被推迟的资源提前下载下来呢?听起来很矛盾对不对,想象一下用户的操作:
- 打开淘宝,查看首屏
- 移动鼠标并点击,或者滚动鼠标滚轮
- 点看商品链接,或者查看第二屏
-
第二步大概有一秒钟的时间,那么我们能不能在这个时候,预测用户的动作,并提前加载第三步的资源呢?答案是可以。
-
比如,当用户的鼠标离某个链接还有200px时,我们提前用JS去get链接内容,那么就可以让用户提前看到内容了。(浏览器自带一个优化:不会在同一时间对同一个资源发两个请求,而是复用同一个请求。所以你不用担心有多余的请求。)代码如下:
|
|
- 比如,当用户屏幕滚动到距离图片还有200px时,我们提前用JS去get图片内容,那么等用户滚到第二屏时,说不定图片已经加载好了。代码如下
|
|
- 除了预加载这些动态内容,程序员也可以预加载一些静态资源,写法如下:
|
|
7 CSS代码优化技巧
- 使用
uncss
删掉无用的CSS,但这个方法无法保证不误删,所以实践中要谨慎使用,面试的时候可以提一下。 - 使用更高效的选择器
- 减少重排(
reflow
)。在比较多种样式修改方案时,尽量选择不会引起重排的方案。比如在做动画时,修改transform
永远比修改left、top、bottom、right
更好,因为transform不会引起重排。 - 不要使用
@import url.css;
因为被加载的CSS不能与当前文件并行下载。实践中本来就没人用它,面试的时候提一下就行。 - 启用GPU硬件加速:在动画的元素上添加
transform: translate3d(0, 0, 0);
启用GPU加速渲染(默认是CPU计算并渲染的)
总的来说,CSS 能优化的空间并不大,而且节点数少于 2000 的页面也没有必要过早优化 CSS。
8 JS代码优化
JS代码的优化技巧主要指是不要使用那些「容易引起性能问题」的特性,比如
-
尽量不用全局变量,因为全局变量太多会使变量查找变慢。
-
尽量少操作DOM,可以使用Fragment一次性插入多个DOM节点。
-
不要往页面中插入大量的HTML,一定会卡
-
尽量少触发重排,可以使用节流和防抖来降低重排频率。
-
尽量少用闭包,减少内存占用,避免内存泄漏(只有IE有内存泄露问题)。
-
如果渲染10w条列表数据:用虚拟滚动列表(第三方库)
-
-
监听滚轮事件/触摸事件,记录列表的总偏移量。
-
根据总偏移量计算列表的可视元素起始索引。
-
从起始索引渲染元素至视口底部。
-
当总偏移量更新时,重新渲染可视元素列表。
-
为可视元素列表前后加入缓冲元素。
-
在滚动量比较小时,直接修改可视元素列表的偏移量。
-
在滚动量比较大时(比如拖动滚动条),会重新渲染整个列表。
-
事件节流。
-
Author gsemir
LastMod 2021-11-17