Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

性能优化 #37

Open
lznbuild opened this issue Aug 18, 2020 · 0 comments
Open

性能优化 #37

lznbuild opened this issue Aug 18, 2020 · 0 comments

Comments

@lznbuild
Copy link
Owner

性能标准

  • 页面初载时,所有未压缩的 JavaScript 脚本大小:<=200KB;

  • 页面初载时,所有未压缩的 CSS 资源大小:<=100KB;

  • HTTP 协议下,请求资源数:<=6 个;

  • HTTP/2 协议下,请求资源数:<=20 个 ;

  • **90%**的代码利用率(也就是说,仅允许 10% 的未使用代码);

性能分析工具

  • Ctrl + Shift + P 搜索Coverage调出查看代码利用率的工具。

  • Ctrl + shift + p 搜索选中 show frames meter 实时查看fps,GPU内存。

  • Shift+Esc 查看浏览器内存管理器

  • Page Speed 或者lightHouse 做整体项目性能分析

  • 如果想要上报一些性能有关的数据,建议查看 window.performance 相关api

  • chrome中的performance + network + memory 部分工具

chrome 工具中的memory Heap snapshot。其实就是当前页面的js对象及其相关的DOM节点的内存分布情况。
可以在内存泄露前生成一份堆快照,再在内存泄露后生成一份堆快照。通过对比的方式,找出两份堆快照存在的内存泄露点。最好是在一次操作后分析,以便分析出问题。

网上常见的性能优化方案,基本可以分为3类

  • 对于网络加载资源的优化。(控制数量和体积,合理利用http缓存,资源预加载)
  • 渲染时的优化。(不管是框架的vdom,还是原生js的fragment文档碎片,函数节流防抖等等一系列的优化方式,归根结底都是为了避免较多次的回流重绘)
  • 对内存的使用情况。(避免占用太多内存)

好了,性能优化说完了。。。。

前两条,资源加载和渲染,可以一起说,这就避不开一个问题,就是从输入URL到页面加载完成,发生了什么,搞明白这个问题,才能有针对的优化。

这里只列出简易说明,详细请看 这里

  • DNS,将url解析为对应的Ip地址(DNS缓存,DNS prefetch预读)
  • 通过Ip地址找对应的服务器
  • 跟服务器建立Tcp连接(每次握手急死人,长连接,http缓存,http2.0的升级)
  • 向服务端发送请求(减少请求数量,减小请求体积,服务器越远,一次请求就越慢,部署时就把静态资源放在离我们更近的 CDN 上)
  • 服务端处理完请求把响应数据放在HTTP响应里返回给客户端
  • 关闭TCP连接
  • 浏览器拿到响应数据开始走渲染的流程。(对应渲染时优化)

这几个过程中可以进行优化的方向在括号中都已说明。
下面说下具体怎么做。

link标签有rel值,可以帮开发者做资源预加载。

rel值:

  • rel=preload
    做资源的预加载,加载后并不执行。

  • rel=dns-prefetch

    浏览器会对某个域名预先进行 DNS 解析并缓存。这样,当浏览器在请求同域名资源的时候,能省去从域名查询 IP 的过程,从而减少时间损耗

    <meta http-equiv="x-dns-prefetch-control" content="on">  
    <link rel="dns-prefetch" href="xxx">
  • rel=preconnect。

    让浏览器在一个 HTTP 请求正式发给服务器前预先执行一些操作,这包括 DNS 解析、TLS 协商、TCP 握手,通过消除往返延迟来为用户节省时间。

  • prefetch / preload。

    两个值都是让浏览器预先下载并缓存某个资源,但不同的是,prefetch 可能会在浏览器忙时被忽略,而 preload 则是强制浏览器在不阻塞document的onload事件的情况下请求资源。使用 preload 和 prefetch 的逻辑可能不是写到一起,但一旦发生对用一资源 preload 或 prefetch 的话,会带来双倍的网络请求。

    prefetch可以用来加载关键的脚本,字体,主要图片等。

    preload 加载资源一般是当前页面需要的,prefetch 一般是其它页面有可能用到的资源。

    对跨域的文件进行 preload 的时候,我们必须加上 crossorigin 属性

    <link rel="preload" as="font" crossorigin href="https://xx/font_zck90zmlh7hf47vi.woff">
  • rel = prerender

    浏览器不仅会加载资源,还会解析执行页面,进行预渲染。

<link rel="dns-prefetch" href="//g.alicdn.com"> // 慎用,浏览器自己有自己的一套域解析方法 
<link rel="preload" href="/path/to/style.css" as="style">

使用http响应头的Link字段创建

Link: <https:/ /example.com/other/styles.css>; rel=preload; as=style

关于资源的预加载,要注意一点。加载资源可能会加长首屏渲染的时间,请谨慎使用。

关于网络请求这部分直接用http缓存就好,详情查看 这里

CDN

1.缩减用户和服务器的距离,提升加载效率

2.浏览器对一个域名的并发请求有限制,所以用cdn域名专门加载静态资源

比如项目中用到的echarts等第三方包,可以用cdn引入。

图片的优化

加载资源如果是图片的话,可针对图片进行优化。

使用base64的编码方式,对这个图片进行 Base64 编码,浏览器原来是可以理解这个字符串的,它自动就将这个字符串解码为了一个图片,而不需再去发送 HTTP 请求。用于小图
Base64 编码后,图片大小会膨胀为原文件的 4/3(这是由 Base64 的编码原理决定的)

jpeg,jpg, 体积小,加载快,不支持透明,用与大图,有损压缩,不好处理矢量图,颜色对比强烈的图

png 无损压缩,高保真,体积大,用于logo,图标,透明图

svg 体积小,灵活,矢量图不失真,可编程,渲染成本高

webp 体积和质量相对最平衡的一种图片类型

雪碧图 减少HTTP请求

关于图片除了使用合理文件类型,雪碧图,还可以通过image-webpack-loader压缩,减少体积。

服务端渲染

除了客户端渲染之外,还有另一种方案--服务端渲染。

客户端渲染模式下,服务端会把渲染需要的静态文件发送给客户端,客户端加载过来之后,自己在浏览器里跑一遍 JS,根据 JS 的运行结果,生成相应的 DOM。

服务端渲染的模式下,当用户第一次请求页面时,由服务器把需要的组件或页面渲染成 HTML 字符串,然后把它返回给客户端。客户端拿到手的,是可以直接渲染然后呈现给用户的 HTML 内容,不需要为了生成 DOM 内容自己再去跑一遍 JS 代码

搜索引擎只会查找现成的内容,不会帮你跑 JS 代码。

img中的alt属性是搜索引擎判断图片与文字是否相关的重要依据,优化搜索引擎.

服务端渲染本质上是本该浏览器做的事情,分担一部分给服务器去做。这样当资源抵达浏览器时,它呈现的速度就快了。乍一看好像很合理:浏览器性能毕竟有限,服务器多牛逼!能者多劳,就该让服务器多干点活!

但仔细想想,在这个网民遍地的时代,几乎有多少个用户就有多少台浏览器。用户拥有的浏览器总量多到数不清,那么一个公司的服务器又有多少台呢?我们把这么多台浏览器的渲染压力集中起来,分散给相比之下数量并不多的服务器,服务器肯定是承受不住的,所以需要合理使用服务端渲染。

解析资源

浏览器拿到资源后,进行解析资源的过程。

我们知道,只有当我们开始解析 HTML 后、解析到 link 标签或者 style 标签时,CSS 才登场,CSSOM 的构建才开始。很多时候,DOM 不得不等待 CSSOM。因此我们可以这样总结:

CSS 是阻塞渲染的资源。需要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。

这个“把 CSS 往前放”的动作,对很多同学来说已经内化为一种编码习惯。那么现在我们还应该知道,这个“习惯”不是空穴来风,它是由 CSS 的特性决定的。

当渲染引擎解析 HTML 遇到 script 标签引入文件时,会立即进行一次渲染。所以这也就是为什么构建工具会把编译好的引用 JavaScript 代码的 script 标签放入到 body 标签底部,因为当渲染引擎执行到 body 底部时会先将已解析的内容渲染出来,然后再去请求相应的 JavaScript 文件。如果是内联脚本(即不通过 src 属性引用外部脚本文件直接在 HTML 编写 JavaScript 代码的形式),渲染引擎则不会渲染。

以上就说明了两点,css引入放开头,js引入放结尾。
js文件可以通过async,defer加载,避免阻塞dom树的解析。

关于渲染时的回流重绘

浏览器自己清楚,如果每次 DOM 操作都即时地反馈一次回流或重绘,那么性能上来说是扛不住的。于是它自己缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气出队。

当要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,就要注意了!

这些值有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流。

除此之外,当我们调用了 getComputedStyle 方法,或者 IE 里的 currentStyle 时,也会触发回流。原理是一样的,都为求一个“即时性”和“准确性”。

对于回流重绘,只能想办法减少次数。

具体的性能优化的方案

  • index.html模板中放入一个静态的loading组件或者骨架屏
 new HtmlWebpackPlugin({
      filename: 'xxxx.html',
      template: 'template.html',
      loading: loading // loading组件
    })
  • 不变的框架代码做缓存,只发布业务代码。
    react,react-dom等依赖,webpack打包的时候单独抽离。

  • 不是每个设备都需要polyfill,使用polyfill.io这样的动态polyfill,保证在需要时引入

  • 代码分割 SplitChunksPlugin,基于路由做分包,react.lazy或者bundle-loader。

  • 不是所有的设备都需要babel编译到es5的代码

<script type="module" src="main.js"></script>
<script nomodule src="main.es5.js"></script>

支持type=module的浏览器,必然支持async/await promise, class 箭头函数 等等
而不支持 <script type="module"> 的老旧浏览器,会因为无法识别这个标签,而不去加载 ES2015+ 的代码。另外老旧的浏览器同样无法识别 nomodule 熟悉,会自动忽略它,从而加载 ES5 标准的代码。

至此,加载资源和渲染说完了。

哪些情况容易导致内存泄露

  1. 全局变量太多
  2. 定时器未清除,dom2级事件没有remove
  3. 闭包太多
  4. 对于占内存大的变量,不要等到垃圾回收,开发者手动清除。(比如:大量dom获取,blob对象等)

https://zhuanlan.zhihu.com/p/62047452

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant