无题
Chrome DevTools 的 Performance 工具是性能分析和优化的利器,因为它可以记录每一段代码的耗时,进而分析出性能瓶颈,然后做针对性的优化。
这么强大的工具肯定是要好好掌握的,今天我们就来做一个性能优化的案例来快速上手 Performance 吧。
性能分析首先,我们准备这样一段代码:
123456789101112131415161718192021222324252627282930313233343536<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>worker performance optimization</title></head><body> <script> function a() { b(); } functio ...
无题
很多前端都喜欢用 console.log 调试,先不谈调试效率怎么样,首先 console.log 有个致命的问题:会导致内存泄漏。
为什么这么说呢?
用 Performance 和 Memory 工具分析下就知道了。
我们准备这样一段代码:
一个按钮,点击之后创建一个数组,执行一些计算。
很常见的逻辑。
我们最后加了一个 console.log 打印了下这个数组。
起个静态服务:
浏览器访问:
点击 performance 下的垃圾回收按钮,手动触发一次 GC:
勾选 Memory,然后开始录制,点击 3 次按钮,再执行一次 GC:
你会发现内存是这样的:
内存占用有三次增长,因为我们点击三次按钮的时候会创建 3 次大数组。
但是最后我们手动 GC 之后并没有回落下去,也就是这个大数组没有被回收。
按理来说,代码执行完,那用的内存就要被释放,然后再执行别的代码,结果这段代码执行完之后大数组依然占据着内存,这样别的代码再执行的时候可用内存就少了。
这就是发生了内存泄漏,也就是代码执行完了不释放内存的流氓行为。
有同学说,只是这么一点内存问题不大呀,反正可用内存还很多。
但如 ...
无题
上节通过 Performance 和 Memory 工具证明了打开 devtools 的时候 console.log 会有内存泄漏。
有 console.log 的时候,内存是这样的:
去掉之后是这样的:
我们得出结论,console.log 会导致内存泄漏。
这点没错。
但很多同学会有疑问,是不是因为打开 devtools 才有内存泄漏,不打开就不会呢?
不打开 devtools 怎么确定内存泄漏问题呢?
看下内存大小就知道了。
通过 performance.memory.totalJSHeapSize 是可以拿到堆内存大小的。
我们通过分析 console.log 的代码执行后的堆内存大小变化就行。
也就是这样:
123456789101112131415161718192021222324252627<!DOCTYPE html><html lang="en"><body> <button id="btn">点我</button> <div id=" ...
无题
用 Performance 工具分析网页的时候,你可能会看到 FP、DCL、FCP、L、LCP 这些东西:
勾选 web vitals 的话,还能看到具体的时间:
这是什么呢?
这叫做 web vitals,网页性能指标,这其中还有 3 个核心的,叫做 web core vitals。
我们分别来看下它们的含义:
Web VitalsTTFB (首字节到达)Time To First Byte,从开始加载网页到接收到第一个字节的网页内容之间的耗时,用来衡量网页加载体验。
可以通过 performance api 计算出来:
12const { responseStart, requestStart } = performance.timingconst TTFB = responseStart - requestStart
或者通过 PerformanceObserver api:
123456789101112new PerformanceObserver((entryList) => { const entries = entry ...
无题
大家应该接触过图层的概念,可能是在一些设计软件里面,其实网页里也有图层。
用 Performance 分析页面运行流程的时候,你可能会主线程发现这样的任务:
回流、重绘、绘制,然后再合并图层。
下面还有 Compositor 线程,专门用来合并图层:
说明网页前面的绘制是在绘制在不同的图层上的。
为什么一个网页要分为不同的图层呢?
页面中的不同部分,重绘频率是不一样的,比如 video、canvas、动画这种就要高频重绘,而且现代浏览器都支持通过 GPU 做计算来加速渲染(硬件加速),怎么综合高频重绘和低频重绘、CPU 渲染和 GPU 渲染呢?
答案就是分成不同的图层,每个图层单独做自己的绘制,最后由 Compositor 线程把它们合并到一起。
那什么样式会新建图层呢?
大家可能听过用 3D transform 会新建图层,用 will-change 会新建图层等等,但是是否真的新建了图层心里并没底。
其实这个也是有工具可以分析的,就是 Layers 工具。
在 more tools 里开启 Layers:
就可以看到所有的图层,就是被黑框框出的那些:
选中某一个图层后,会展 ...
无题
Chrome DevTools 有很多有用的小功能,这节就专门梳理一下这个:
reply XHR当你想重新发送一次 XHR 请求的时候,不用刷新页面,直接点击 replay XHR 即可:
请求定位到源码当你想知道某个请求是在哪里发的,可以打开 Network 面板,在每个网络请求的 initiator 部分可以看到发请求代码的调用栈,点击可以快速定位到对应代码。
元素定位到创建的源码当你想知道某个元素的创建流程,可以通过 Elements 面板选中某个元素,点击 Stack Trace,就会展示出元素创建流程的调用栈。这可以帮你理清前端框架的运行流程。
这个功能是实验性的,需要手动开启下:在 settings 的 expriments 功能里,勾选 “Capture node creation stacks”。
group by folder网页加载的文件默认是按照域名和目录组织的,找文件时一层层找起来比较麻烦。
这时候可以切换为平铺的,会按照 js、css、图片的顺序列出来,找某个文件就容易多了:
Network 自定义展示列Network 是可以修改展示的列的,比如我勾 ...
无题
上节我们知道了什么是调试、调试的原理,这节我们开始学习调试工具的使用。
首先从网页的 JS 调试开始。
我们以 React 项目为例,用 create-react-app 创建一个 react 项目:
1yarn create react-app test-react-debug
进入项目目录,执行 npm run start。
它会启动一个开发服务,然后浏览器访问 localhost:3000:
打开 Chrome DevTools,在 Sources 面板找到 src/index.js,打上个断点:
然后刷新就可以开始调试了:
代码会在断点处断住,右边会显示当前 local 作用域的变量,global 作用域的变量,还有调用栈 call stack。
上面有几个控制执行的按钮,分别是:
恢复执行
单步执行
进入函数调用
跳出函数调用
让断点失效
在异常处断住
可以控制代码的执行,可以看到每一步的调用栈和作用域的变量,那理清代码的逻辑,或者排查代码中的问题不就很容易了么?
其实调试网页的 JS,除了 Chrome DevTools 外,还有一种更好用的 ...
无题
上节我们学会了如何用 VSCode Debugger 调试网页的 JS,其实它还有很多有用的配置项,这节我们一起来过一遍:
首先,调试配置文件不用自己创建,可以直接点击 Debug 窗口的 create a launch.json file 快速创建:
launch/attach创建 Chrome Debug 配置有两种方式:launch 和 attach:
它们只是 request 的配置不同:
我们知道,调试就是把浏览器跑起来,访问目标网页,这时候会有一个 ws 的调试服务,我们用 frontend 的 ws 客户端连接上这个 ws 服务,就可以进行调试了。
VSCode 的 Debugger 会多一层适配器协议的转换,但是原理差不多。
launch 的意思是把 url 对应的网页跑起来,指定调试端口,然后 frontend 自动 attach 到这个端口。
但如果你已经有一个在调试模式跑的浏览器了,那直接连接上就行,这时候就直接 attach。
比如我们手动把 Chrome 跑起来,指定调试端口 remote-debugging-port 为 9222,指定用户 ...
无题
学习调试,sourcemap 是绕不开的概念,有了它才能直接调试源码。
这一节,我们就来探究下 sourcemap:
什么是 sourcemapsourcemap 是关联编译后的代码和源码的,通过一个个行列号的映射。
比如编译后代码的第 3 行第 4 列,对应着源码里的第 8 行第 5 列这种,这叫做一个 mapping。
sourcemap 的格式如下:
123456789{ version : 3, file: "out.js", sourceRoot : "", sources: ["foo.js", "bar.js"], names: ["a", "b"], mappings: "AAgBC,SAAQ,CAAEA;AAAEA", sourcesContent: ['const a = 1; console.log(a)', 'const b = 2; co ...
无题
上节我们学习了什么是 sourcemap,这还不够,webpack 对 sourcemap 做了很多封装。
想彻底掌握 sourcemap,还要搞懂 webpack 的 sourcemap 配置。
webpack 的 sourcemap 配置是比较麻烦的,比如这两个配置的区别:
eval-nosources-cheap-module-source-map
hidden-source-map
是不是分不清楚?
其实它是有规律的。
你把配置写错的时候,webpack 会提示你一个正则:
^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
这个就是配置的规律,是几种基础配置的组合。
搞懂了每一种基础配置,比如 eval、nosources、cheap、module,按照规律组合起来,也就搞懂了整体的配置。
那这每一种配置都是什么意思呢?
我们分别来看一下。
(可以用后面一节的项目代码来测试)
evaleval 的 api 是动态执行 JS 代码的。比如:
但有个问题,eval 的代码打不了断点。
怎 ...
