electron性能优化系列二(启动优化)

810 阅读4分钟

背景

最近在做 electron 首次加载优化,本文主要是记录下优化的内容。

在优化之前,我们先看一下目前性能的瓶颈是在哪里,优化前的流程如下图(业务原因需要热更新):
app启动->loadURL->show window 在使用了缓存的情况下,多次测试均值大概如下:

1、app启动->loadURL,主要进行了 版本检查/连接数据库/各种校验 等,耗时 380ms
2、loadURL->show window,因为业务原因(需要热更新,不直接读取本地文件),所以需要请求静态资源,耗时 920ms

这里主要分两步进行优化:
1、客户端启动到 loadURl 过程中的其他操作尽量全部延迟到loadURL后执行;
2、提高 loadURL 的FCP/LCP;

第1步主要是业务逻辑的处理,把所有的步骤延迟执行后(主要是业务逻辑内容,这里不再详细描述),处理时间优化为 105ms 左右(应用启动和实例化 BrowserWindow 对象的时间)。
本文主要是来说一下如何提高页面的FCP/LCP。

注:为了尽量保证环境一致,内容都在限制网速下进行(chrome Fast 3G)

分析渲染过程

当前项目渲染流程如下(Performance insights): (不使用缓存) image.png (请求html资源->初始化渲染->请求css/js资源->解析css重新渲染->执行js重新渲染)

(使用缓存) image.png

可以看到主要阻塞的地方在js/css资源,所以需要分包:
1、每次更新时,没有修改过内容的文件不修改,尽量利用缓存;
2、将首屏渲染不需要的内容延迟加载;

分包

到底是根据什么来分包呢?根据我的目的分包是有两个维度:
1、在首屏(在当前项目中是登录页)只请求加载登录页相关逻辑代码,延迟加载其他代码;
2、对于基本不变的第三方包(node_modules)单独打包,利于缓存;
主要用到了两个配置,vue3的 异步组件 以及rollup的 manualChunks:

// manualChunks 配置
rollupOptions: {
    output: {
    manualChunks: (id) => {
            const _nodeModules = /node_modules/

            if(_nodeModules.test(id)) {
            return 'vener'
            }
        }
    }
}
// 异步组件配置
{
    path: '/',
    name: 'Home',
    component: defineAsyncComponent(() => import('@pages/home.vue'))
}

优化结果如下:
(不使用缓存) image.png (使用缓存) image.png

可以看到静态资源分布比较均衡,但FCP/LCP等指标优化的并不多(这是由于本地下行带宽限制导致的,带宽就这么多,并发或者串行并不影响总体下载时间).

注:有些库需要注意按需引入,e.g. lodash/element-plus/vant 等需要改为按需引入避免打包文件过大

体验优化

其实可以看到在网络不好的情况下如果没有缓存,加载时间还是比较长,为了避免白屏时间过程,可以用骨架屏先做一个渲染,提高用户体验.
可以使用一些骨架屏生成的工具, e.g. awesome-skeleton

总结

在限制了宽带下行速度(fast 3G)后,进行投入非常小的分包,就可以得到非常不错的效果:
1、在没有使用缓存的情况下,FCP由 6.8s 优化到 4.97s;
2、在有缓存的情况下,FCP由 0.92s 到 0.81s;
3、首屏js资源由 752kb 到 526kb(其中423kb是基本不变化的vender包,其实这里还可以继续优化);
而且最大化了缓存的利用效果.

问题记录

1、到底怎么评估 首屏加载 时间?
看了很多分析介绍,其实对当前项目来说说用 TTI/FCP/LCP/onloaded 等都可以,评估的原因在于需要优化对比,只要有明确的指标就可以了。

2、可用 rollup-plugin-visualizer 插件分析打包情况;

3、是否应该将 骨架屏 放到 link 和 script 标签 之前,避免阻塞骨架屏的渲染?
目前 vite 的打包,是将script标签放到head中(以前的做法一般会将script标签放到body最下方),这样会阻塞 body 的渲染,我翻了一下相关的issue,该特性提出的主要原因是:
1、避免html内容过大阻塞script标签的下载;
2、link preload不会阻塞body内容渲染;
该特性其实还挺多争议的,做了一些测试,link type='stylesheet' 仍会阻塞body内容(即我加入的骨架屏)渲染,所以暂时需要手动将标签改到body内容下方:

// 自己通过 vite 插件的方式实现替换
// 只做对当前项目的匹配,考虑的情景不多,后续如果有需求可以考虑发布一个完善的插件
function vitePluginInjectStyleSheet() {
    return {
        name: 'vite-plugin-inject-styleSheet',
        transformIndexHtml(html, ctx) {
            const linkStyleSheetReg = /<link rel="stylesheet".+?>/g
            const matchContent = html.match(linkStyleSheetReg)
            const position = /(?=<\/body>)/

            html = html.replaceAll(linkStyleSheetReg, '')
            html = html.replace(position, matchContent.join('\n'))

            return html
        }
    }
}

参考文档

1、指标: web.dev/metrics/