问题场景:

在使用前端框架构建单页应用时,服务器会返回一份html文件、抽取出来的css文件和一份打包好的js文件。html体积是极小的,相当于只提供了一个舞台,而页面结构的生成和更新完全由js根据路由和框架特点、业务逻辑来控制,加上css样式渲染出完整的页面。打包后的js文件包括了框架和依赖库(react、react-router等)还有业务逻辑代码,所以功能越复杂,体积很大。

问题原因分析:

浏览器从输入url到完成显示页面有一个复杂的过程,这里从加载文件开始分析。浏览器解析获得的html文档并生成DOM树,当浏览器解析到外部的script时,解析会暂停,并发送请求来加载script文件并执行,然后才会继续解析DOM。这就导致了过大的外部js文件拖慢了页面的加载渲染。有人提出可以加入 defer 属性支持同步加载和浏览器缓存策略,但这只是有限的优化手段,请求时间和执行时间过长也会影响到首屏的加载,影响了用户体验。

代码拆分原理

对于单页应用来说,页面是根据路由区分的,代码拆分其实就是按照页面来拆分,每个页面都对应一个分片。对多个页面共享的代码又独立拆分出来,对于有很多页面和复杂依赖的应用,共享代码还可以进一步拆分为所有页面共享的(React框架代码)和部分页面共享的(组件模块),这样当一个页面加载时只需要加载页面自己的分片和共享的分片就行,很大地提高了首屏渲染的速度。

三点注意:

  1. 使用webpack拆分。webpack 是根据依赖关系进行打包的模块打包工具。根据import()动态导入的方式进行页面、组件分片。拆分框架代码有两种方式:一是使用SplitChunksPlugin将框架代码拆分并与其他分片放在一起;二是使用DllPlugin将框架代码拆分放在静态资源服务器上,html页面上使用script引入,应用打包时配置映射关系文件。对比SplitChunksPlugin的优点是可以将框架代码文件放在cdn服务器并配置强制缓存,进一步提高加载速度。
  2. 由于代码拆分主要是针对首次首屏渲染的优化,所以页面不能拆得太分散,3~4个为宜。
  3. 使用相应的代码拆分方案。比如React 代码拆分 和 Vue 的动态组件