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...

相关推荐
花海少爷6 分钟前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
Amd79410 分钟前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You19 分钟前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生30 分钟前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互
sinat_3842410933 分钟前
在有网络连接的机器上打包 electron 及其依赖项,在没有网络连接的机器上安装这些离线包
javascript·arcgis·electron
baiduopenmap1 小时前
百度世界2024精选公开课:基于地图智能体的导航出行AI应用创新实践
前端·人工智能·百度地图
loooseFish1 小时前
小程序webview我爱死你了 小程序webview和H5通讯
前端
小牛itbull1 小时前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
菜牙买菜1 小时前
让安卓也能玩出Element-Plus的表格效果
前端
请叫我欧皇i1 小时前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript