SPA首屏优化实录,手把手实践,白屏时间减少80%

4,610 阅读7分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

相信很多小伙伴或多或少都涉猎过性能优化,网上的性能优化八股文滚瓜烂熟,合并请求减少请求、减少dom结点多使用css3、合理压缩使用缓存等等,然后按着教程一顿操作猛如虎,满怀期待的试运行,最终发现效果甚微。其实对于中小型项目来说,这些常规的优化脚手架(vueCLi)里都配置好了,你在去配置就显得事倍功半了。我最近在写自己低代码项目的时候发现,打包出的vendors接近2M,由于我用的服务器配置不太好,带宽1M,请求这个js需要8~9秒,就着手想着去做优化,找了很多webpack优化教程看,跟着一个up视频(近几个月发布的)教程一步步优化,花了半天时间最后都没有出现什么跃进式的效果,文档上已经注明是webpack默认配置,但他还要演练实操,闭口没提是否是默认配置,真是为了讲而讲。

开源低代码平台

我最近两个月写的低代码平台

在线地址:dawsky.top/

代码仓库:github.com/iamwhj/mobi…

问题表现

项目依赖: vue3 + webpack5 + elementPlus + echarts

image.png

可以从图中看到这个1.6M的js加载用了9秒,服务器带宽1M,白屏时间近10秒,由于服务器是自己买来学习的,所以没必要升级配置,想要优化就只能从这个js文件下手。

问题猜想和定位

起初我以为是分包问题,可能是没有做分包所有内容都集中到一个js上了,最后查看打包产物发现webpack默认做了分包。

element-plus考虑后续开发便捷使用是全引,echarts按需引入。

image.png

image.png

使用webpack-bundle-analyzer插件进行分析,element-plus和echarts占用空间最大,还有element/icon也占据不少空间。

尝试解决

1、对vendors进一步分包,将大文件拆分出去,打成单独的包。增加以下配置:

    optimization: {
      splitChunks: {
        chunks: 'all',
        cacheGroups: {
          vue: {
            test: /[\/]node_modules[\/]vue(.*)?[\/]/,
            name: 'chunk-vue',
            priority: 10,
          },
          elementPlus: {
            test: /[\/]node_modules[\/]element-plus[\/]/,
            name: 'chunk-elementPlus',
            priority: 9,
          },
          elementPlusIcon: {
            test: /[\/]node_modules[\/]@element-plus[\/]icons-vue[\/]/,
            name: 'chunk-elementIcon',
            priority: 8,
          },
          echarts: {
            test: /[\/]node_modules[\/]echarts[\/]/,
            name: 'chunk-echarts',
            priority: 7,
          },
        },
      },
    },

将上面文件都拆出vendors,打包成js文件。

2、 重新打包结果

image.png

image.png

可以看到打包产物多出了我们拆出的4个js,vendors体积减少了四倍多。速度能变快多少,我们更新到服务器实测看看。

服务器实测

到这我以为访问速度有所改善,其实不然

image.png

直接上服务器实测图,从图中我们可以看出即使分包后文件体积变小了,但是服务器上请求速度还是很慢。

原因分析

环境: 1M带宽服务器,实际请求速度应该在200~300k左右

我们分包后生成四个300-600k的文件,这个四个文件并行请求服务器,那么200~300k的速度实际上是均分到每个并行请求里面的,所以即使文件变小了但是请求变多了,请求还是一样慢。

其他解决方案

  1. 减少文件体积,按需加载减少引用(最后再做这个吧,其实已经没有太多的操作空间,默认打包配置已将代码压缩,treeShaking等)
  2. CDN,据说存储到内容分发网络后,获取资源速度会快很多,具体实测看看
  3. 开启GZIP,这个需要 webpack 配置和服务器 nginx 配合使用,网上很多教程,配置简单收益高

CDN方案

  1. 部分内容上CDN,如只将echarts、elementPlus等第三方库上CDN,查了一些资料发现这是网上常用的方案
  2. 静态资源全上CDN,打包产物除了index.html外全上CDN,包括打包出的js/css/img等,问了朋友上家公司的方案

尝试CDN

尝试了好几个云服务平台都行不通,国内服务器使用CDN必须拥有通过备案的域名,去阿里云买了一个域名,域名实名需要时间,实名通过后须等等待3天信息同步才能备案,备案审核需要很长一段时间。经此,这个方案只能暂时作罢,等域名通过备案后再试试吧。

尝试GZIP

  1. webpack

    yarn add  compression-webpack-plugin@6.1.1 --save
    ​
    configureWebpack: (config) => {
        // 开发环境不配置
        if (process.env.NODE_ENV !== 'production') return;
        // 生产环境才去配置
        return {
          plugins: [
            new CompressionPlugin({
              //此插件不能使用太高的版本,否则报错:TypeError: Cannot read property 'tapPromise' of undefined
              filename: '[path][base].gz', // 这种方式是默认的,多个文件压缩就有多个.gz文件,建议使用下方的写法
              // filename: '[path].gz[query]', //  使得多个.gz文件合并成一个文件,这种方式压缩后的文件少,建议使用
              algorithm: 'gzip', // 官方默认压缩算法也是gzip
              test: /.js$|.css$|.html$|.ttf$|.eot$|.woff$/, // 使用正则给匹配到的文件做压缩,这里是给html、css、js以及字体(.ttf和.woff和.eot)做压缩
              threshold: 10240, //以字节为单位压缩超过此大小的文件,使用默认值10240吧
              minRatio: 0.8, // 最小压缩比率,官方默认0.8
              //是否删除原有静态资源文件,即只保留压缩后的.gz文件,建议这个置为false,还保留源文件。以防:
              // 假如出现访问.gz文件访问不到的时候,还可以访问源文件双重保障
              deleteOriginalAssets: false,
            }),
          ],
        };
      },
    
  2. nginx

server {
    listen 80 default_server;
    listen [::]:80 default_server;
​
    root /var/www/web/mobile-maker/packages/front/dist;
​
    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;
​
    server_name _;
    # ......
    # 以下为gzip配置
​
    gzip on; # 开启gzip压缩
    gzip_min_length 4k; # 小于4k的文件不会被压缩,大于4k的文件才会去压缩
    gzip_buffers 16 8k; # 处理请求压缩的缓冲区数量和大小,比如8k为单位申请16倍内存空间;使用默认即可,不用修改
    gzip_http_version 1.1; # 早期版本http不支持,指定默认兼容,不用修改
    gzip_comp_level 2; # gzip 压缩级别,1-9,理论上数字越大压缩的越好,也越占用CPU时间。实际上超过2的再压缩,只能压缩一点点了,但是cpu确是有点浪费。因为2就够用了
    # 压缩的文件类型 MIME类型
    gzip_types text/plain application/x-javascript application/javascript text/javascript text/css application/xml application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/x-woff font/ttf;
    gzip_vary on; # 是否在http header中添加Vary: Accept-Encoding,一般情况下建议开启  
}

服务器实测

image.png

image.png

gzip开启成功,效果很明显,访问速度相比之前快了很多。

  • vendors体积:1739k => 561k
  • 请求时间:9.28s => 2.68s

相比之前白屏时间确实是缩短了很多,但是这个白屏时间还是过长的。

代码优化

当前:echarts按需引入、element-plus全引

需要将element-plus改成自动引入,是自动引入而不是以前按需引入的写法,这个真的方便很多,要改的代码很少。

npm install -D unplugin-vue-components unplugin-auto-import
​
// webpack配置里加入下面配置
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
​
plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
],

main.js 里注册使用element-plus的代码删掉即可。

代码优化结果

image.png

优化效果也是相当明显的,vendors减少了300多k。

服务器实测

image.png

快了将近一秒,整体来说提升还是很大的。

总结

个人服务器1M带宽,也算是模拟一种恶劣环境吧,企业服务器得百兆千兆,极端环境优化完后再放到企业服务器,何尝不是一种提升呢

1、分包,没效果(服务器带宽就这么多,分成多个包,也是这些包均分带宽速度)

2、CDN,应该是最优的解吧(上CDN需要备案的域名,实名加备案需要很长时间,备案过了可以试试。上了CDN后走得是CDN服务器,静态资源直接从CDN服务器取,跟自己服务器没啥太大关系了,速度是质变。)

3、GZIP,效果很明显,体积能优化缩小3倍左右

4、项目优化,有效果(第三方库按需加载剔除不必要的引入)

vendors加载:

  • 时间 - 从9.28秒到1.7秒
  • 体积 - 1739K到431K(gzip)

本人第一次做首屏优化,经验浅薄,有更好方案的大佬,望能分享!