electron启动优化-页面白屏

公司的一个桌面应用,electron开发,目前是自己在负责. 优化方案前前后后花了两周多. 优化级别对标的是vscode, 都说vscode用了很多黑魔法, 那到底黑魔法是指什么呢, 今天先带着大家扒一扒他的首页...

vscode首页performace性能指标

从chrome的performace控制台入手,打开控制台如下

DCL / FP

从上图时间轴中Screenshots可看到大概58ms左右骨架屏显示出来,这个时间点对应的是DCL/FP,即DomContextLoad事件触发并且开始第一次绘制. 所以我们项目的目标就是DCL&FP优化到50-70ms

先简单说下上图五颜六色的意义. 蓝色:Parse Html 所有的蓝色(无论是cpu轴还是main线程中的蓝色)都代表了html的解析. 黄色: Evluate Script 代表js的执行 紫色: Recalculate Style 样式计算

放大这段时间轴,我们看看这期间发生了什么:

  1. HTML Parser: 在解析期间可以看出在蓝色中间是加载了wokbench.js, js阻塞了ParseHTML. 这段js加载和执行后继续html的解析. 中间这段js大概阻塞了20多ms.
  2. Recalculate Style 样式计算
  3. 之后相继触发了DCL和FP

源码分析workbench.html

D:\Microsoft VS Code\resources\app\out\vs\code\electron-sandbox\workbench\workbench.html

html 复制代码
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta
			http-equiv="Content-Security-Policy"
			content="
				default-src
					'none'
				;
				img-src
					'self'
					data:
					blob:
					vscode-remote-resource:
					vscode-managed-remote-resource:
					https:
				;
				media-src
					'self'
				;
				frame-src
					'self'
					vscode-webview:
				;
				script-src
					'self'
					'unsafe-eval'
					blob:
				;
				style-src
					'self'
					'unsafe-inline'
				;
				connect-src
					'self'
					https:
					ws:
				;
				font-src
					'self'
					vscode-remote-resource:
					vscode-managed-remote-resource:
					https://*.vscode-unpkg.net
				;
				require-trusted-types-for
					'script'
				;
				trusted-types
					amdLoader
					cellRendererEditorText
					defaultWorkerFactory
					diffEditorWidget
					diffReview
					domLineBreaksComputer
					dompurify
					editorGhostText
					editorViewLayer
					notebookRenderer
					stickyScrollViewLayer
					tokenizeToString
				;
		"/>
	</head>

	<body aria-label="">
	</body>

	<!-- Startup (do not modify order of script tags!) -->
	<script src="workbench.js"></script>
</html>

总结: 可以看出head里面竟然没有link css. 而 只有一个script 是放在body下面.

问题1. 为什么head里面不写link css

问题2. 为什么script放在body后面, 而不用defer或async

针对第一个问题,我的理解是 css的加载和解析虽然不影响HTML解析,但script的加载会阻塞HTML解析,而CSS解析会阻塞script的执行(不影响scrpt的解析).所以css间接地阻塞了HTML解析.

为什么要这样设计呢? 其实是因为浏览器遇到script标签时会同步加载并执行对应的js文件, 但它并不知道js中的代码会干些什么,js可以去改动DOM,也可以获取/改变css样式。js要获取正确的样式就必须等css加载完

还有一个问题为什么不用async和defer呢

script对页面加载的影响

async vs defer vs module

参考mdn

async vs defer

  1. 共同点是 script的加载都是异步的,不会阻塞HTML的解析.
  2. 区别是,他们的执行是否会中断HTML的解析
    • async是下载结束立即执行, 此时如果HTML解析没有结束,那么就阻塞了HTML的解析,还有一种情况,如果下载比较慢可能script执行的时候,HTML已经解析结束
    • defer是等HTML解析完全结束后再执行
    • defer是按出现的位置顺序执行,async是谁先下载好谁先执行

这里提一下mdn对DOMContentLoaded事件的定义

当 HTML 文档完全解析,且所有deferred脚本(<script defer src="..."><script type="module">)下载和执行完毕后,会触发 DOMContentLoaded 事件。它不会等待images, subframes,和 async scripts 的加载。

DOMContentLoaded 不会等待stylesheets的加载,但deferred script 会等待样式表,而且 DOMContentLoaded 事件排在deferred script之后。此外,不是deferred 或async的脚本(如 <script>)也会等stylesheets的加载。

再提一下<script type="module">,他和defer,async的区别呢

所以defer属性对模块脚本也不会生效------它们默认是 defer 的。

其次如果同时设置defer和async,async生效.

script写在body标签下还推荐吗

先说答案, 推荐. 所vscode页面上看,就是script写在body下面

为了解释这个原因,先说说浏览器解析html的几个特性

  1. 浏览器解析HTML时,虽然会一行一行向下解析,但是它会预先加载具有引用标记的外部资源(例如带有src标记的script、link标签),而在解析到此标签时,则无需再去加载,直接运行,以此提高运行效率。
  2. 解析HTML时,如果遇到了script标签,会先渲染一次这个script标签之前的DOM,然后再去加载和执行js。因为js是可以操作DOM的,如果浏览器不先去渲染一次,那么js获取的DOM就会是null。所以script放在body后面会强制html的渲染.此时会先触发FP事件. 让页面更快地让用户看到

综上: body下的script就是defer的区别就是defer会等浏览器全部解析完才会执行.

stackoverflow.com/questions/1...
stackoverflow.com/questions/4...

link标签对script的影响

看vscode首页的源码,是没有写link标签的, why

link标签会阻塞页面的渲染

  1. html和css的解析虽然互不影响, 即dom和css树的构建互不影响, 但合成渲染树的时候需要等待dom树和css树都构建完成. 所以css的解析(包括link外部样式表和内部样式表)会阻塞页面的渲染 这是为了确保页面在渲染时应用了正确的样式, 避免出现无内容闪烁,FOUC ,Flash of Unstyled Content
  2. link和script也有一个相同的特性, 就是浏览遇到link标签的时候会立即把之前已解析的内容渲染出来. link不会阻塞前面内容渲染,只会影响后面的内容
  • 注意: 页面head是中的scipt和link是不会有页面提前渲染的特性. 因为默认head是没有布局的

link会阻塞script的执行

link不会阻塞script的加载,但会阻塞script的执行, script又会阻塞页面的渲染

可能是这种考虑,vscode没有用link标签把

vscode在DCL/FP之前发生了什么

从上面的截图可以看到,在DCL/FP之前,页面的背景色竟然已经渲染好了. 这里是针对Chrome做了什么优化吗, 其实没有什么. 是因为electron在创建窗口的时候可以设置一个背景色, 所以vscode中我们看不见白屏

www.electronjs.org/docs/latest...
www.electronjs.org/docs/latest...

理论验证

如何验证上面的一些结论

自己写一个node 服务器, 设置js和css的请求时间.然后打开浏览器的性能分析即可验证

server.js

js 复制代码
const http = require("http");
const fs = require("fs");
const path = require("path");

const hostname = "127.0.0.1";
const port = 80;

const server = http.createServer((req, res) => {
  console.log(req.url);
  if (req.url === "/index.js") {
    setTimeout(() => {
      const stream = fs.createReadStream(path.join(__dirname, "index.js"));
      stream.pipe(res);
    }, 1000);
    return;
  } else if (req.url === "/style.css") {
    setTimeout(() => {
      const styleStream = fs.createReadStream(path.join(__dirname, "style.css"));
      styleStream.pipe(res);
    },2000);
    return;
  } else {
    fs.readFile(path.join(__dirname, "index.html"), (err, data) => {
      if (err) {
        res.statusCode = 500;
        res.setHeader("Content-Type", "text/plain");
        res.end("Error loading index.html");
      } else {
        res.statusCode = 200;
        res.setHeader("Content-Type", "text/html");
        res.end(data);
      }
    });
  }
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>script demo</title>
    <!-- <script src="./jquery-git.js"></script>  -->
    <!-- <script defer src="./jquery-git.js"> </script>  -->
    <!-- 251kb -->
</head>
<body>
    <div style="display: flex;width: 90dvw;height: 40dvh;background-color: bisque;">
    </div>
    <!-- <script  src="./index.js" > </script>  -->
    <link rel="stylesheet" href="style.css">

    <div style="display: flex;width: 90dvw;height: 40dvh;background-color:darkorange;" >
    </div>

</body>
</html>

index.js

js 复制代码
console.log('js start')
for (let index = 0; index < 1000000000; index++) {
	
}

console.log('js end')

style.css

js 复制代码
body{
    background-color: red;
    padding: 5px;
}

developer.chrome.com/docs/devtoo...

web.dev/articles/lc...

hondrytravis.com/docs/perfor...

juejin.cn/post/711254...

juejin.cn/post/689052...

相关推荐
kyriewen20 小时前
别再 console.log 了:5 个 Chrome DevTools 调试技巧,用过就回不去了
前端·javascript·面试
IT_陈寒1 天前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
To_OC1 天前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
DigitalOcean1 天前
Laravel 开发者已在 DigitalOcean 上开通超过 10 万台服务器
前端·laravel
星始流年1 天前
从 Tool 到 Skill——基于 LangChain 的服务端Skill实现
前端·langchain·agent
李惟1 天前
开源本地通信库,纯客户端 RPC,像聊天一样通信
前端
YAwu111 天前
深入解析 React 炫彩鼠标跟随标题组件:从坐标定位到动画性能
前端·react.js
GuWenyue1 天前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
OpenTiny社区1 天前
🎨 看完 GenUI SDK 源码我悟了!
前端·vue.js·github
叁两1 天前
前端转型AI Agent该如何学习?(前置篇)
前端·人工智能·node.js