浏览器原理及性能优化

1. 影响页面性能的因素
  • HTTP 请求
  • 复杂的页面逻辑
  • 重度的DOM操作
  • 服务端响应
  • 大量的数据
2. 优化网页性能
  • 资源压缩与合并(代码打包)
  • 异步加载
  • CDN
  • DNS 预解析
  • 缓存

一、浏览器

1.1 主要作用

浏览器的主要功能就是向服务器发出请求,在浏览器窗口中展示选择的网络资源。这些网络资源包括以下内容:

  • HTML
  • CSS
  • JavaScript的
  • 媒体(图片,视频等)

也可以分为HTML文档(HTML/CSS/JS)、PDF、图片、视频和其他类型

1.2 组成结构

在这里插入图片描述

  1. 用户界面(User Interface) - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。

  2. 浏览器引擎(Browser engine) - 在用户界面和渲染引擎之间传送指令。

  3. 渲染引擎(Rendering engine) - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。

    也可以叫呈现引擎(Rendering Engine)或者布局引擎(Layout Engine)

    浏览器渲染引擎(开发语言)脚本引擎(开发语言)
    ChromeBlink (c++)V8 (c++)
    OperaBlink (c++)V8 (c++)
    SafariWebkit (c++)JavaScript Core (nitro)
    FireFoxGecko (c++)SpiderMonkey (c/c++)
    EdgeEdgeHTML (c++)Chakra JavaScript Engine (c++)
    IETrident (c++)Chakra JScript Engine (c++)
  4. 网络(Networking) - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。

  5. 用户界面后端(UI Backend) - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。

  6. JavaScript 解释器(JavaScript Interpreter)。用于解析和执行 JavaScript 代码,如 V8 引擎。

    JS引擎线程负责解析Javascript脚本,运行代码。

    JS引擎一直等待任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中只有一个JS线程在运行

  7. 数据存储(Data Persistence)。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

1.3 多进程
  • 浏览器是多进程的
  • 浏览器之所以能够运行,是因为系统给它的进程分配了资源(cpu、内存)
  • 简单点理解,每打开一个Tab页,就相当于创建了一个独立的浏览器进程

浏览器里面的进程:

  1. Browser进程:浏览器的主进程(负责协调、主控),只有一个。作用有

    • 负责浏览器界面显示,与用户交互。如前进,后退等
    • 负责各个页面的管理,创建和销毁其他进程
    • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
    • 网络资源的管理,下载等
  2. 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建

  3. GPU进程:最多一个,用于3D绘制等

  4. 渲染进程(浏览器内核)(Renderer进程,内部是多线程的)

    • 默认每个Tab页面一个进程,互不影响
    • 主要作用为页面渲染,脚本执行,事件处理等

渲染进程是多线程的:

  1. GUI渲染线程

    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
  2. js引擎线程

    • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
    • JS引擎线程负责解析Javascript脚本,运行代码。
    • JS引擎一直等待任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中只有一个JS线程在运行
    • 同样注意,GUI渲染线程与JS引擎线程是互斥的。所以如果JS执行的时间过长,要放在body下面,否则就会导致页面渲染加载阻塞。
  3. 事件触发线程

    • 管理着事件队列
    • 监听事件,符合条件时把回调函数放入事件队列中
  4. 定时触发器线程

    • setInterval与setTimeout在此线程中计时完毕后,把回调函数放入事件队列中
    • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确),因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
    • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
  5. 异步http请求线程

    • 检测到XHR对象状态变化时,将回调函数放入事件队列中
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

总结一下:

  • css加载不会阻塞DOM树解析(异步加载时DOM照常构建),但会阻塞render树渲染(渲染时需等css加载完毕,因为render树需要css信息)
  • Javascript 阻塞 DOM 解析
1.4 渲染机制
  • DOM 树与 CSSOM 树合并后形成渲染树
  • 渲染树只包含渲染网页所需的节点
  • 布局计算每个对象的精确位置和大小
  • 最后一步是绘制,使用最终渲染树将像素渲染到屏幕上

在这里插入图片描述

1.5 重排(回流)与重绘
  1. 重绘(repaint)
    元素外观的改变所触发的浏览器行为,例如改变vidibility、outline、background等属性,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观,重绘不会带来重新布局

  2. 重排(回流 reflow)
    渲染树需要重新布局,例如:在body最前面插入一个元素, dom元素的结构变化 ,调整浏览器窗口大小等

例子:

var bstyle = document.body.style; // cache

bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; //  再一次的 reflow 和 repaint

bstyle.color = "blue"; // repaint
bstyle.backgroundColor = "#fad"; // repaint

bstyle.fontSize = "2em"; // reflow, repaint

// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

减少重绘与重排:

  • js尽量少访问dom节点和css 属性,尽量不要过多的频繁的去增加,修改,删除元素,因为这可能会频繁的导致页面reflow,可以先把该dom节点抽离到内存中进行复杂的操作然后再display到页面上。(虚拟DOM)

  • 减少不必要的 DOM 层级(DOM depth)。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面。

  • 不要通过父级来改变子元素样式,最好直接改变子元素样式,改变子元素样式尽可能不要影响父元素和兄弟元素的大小和尺寸

  • 尽量通过class来设计元素样式,切忌用style 多次操作单个属性

  • 尽可能的为产生动画的 HTML 元素使用 fixedabsoluteposition ,那么修改他们的 CSS 是不会 Reflow 的。

  • img标签要设置高宽,以减少重绘重排

  • 避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU

总结:
Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。


二、页面加载缓慢的原因
2.1 浏览器部分
  • 网络层面

    1. 过多的HTTP请求

      打开一个网页的时候,后台程序的响应并不所需太多时间,等待的时间主要花费在下载网页元素上了,即HTML、CSS、JavaScript、Flash、图片等。据统计,每增加一个元素,网页载入的时间就会增加25-40毫秒(具体取决于用户的带宽情况)。

    2. 资源访问带宽小

      两方面,一方面是客户端的带宽,一方面是服务器端的带宽。

    3. 网页元素(图片、视频、样式)太大

  • 浏览器渲染层面

    1. 渲染阻塞:

      浏览器想要渲染一个页面就必须先构建出DOM树与CSSOM树,如果HTMLCSS文件结构非常庞大与复杂,这显然会给页面加载速度带来严重影响。

      所谓渲染阻塞资源,即是对该资源发送请求后还需要先构建对应的DOM树或CSSOM树,这种行为显然会延迟渲染操作的开始时间。

      JS阻塞与CSS阻塞:

      HTML、CSS、JavaScript都是会对渲染产生阻塞的资源,HTML是必需的(没有DOM还谈何渲染),但还可以从CSS与JavaScript着手优化,尽可能地减少阻塞的产生。

    2. 重复渲染

    3. DNS解析

  • 服务端层面

    1. 硬件配置低:这个是双向的
    2. 服务器软件,比如防火墙、内网策略等
    3. 未对Nginx这类web服务器进行配置优化
    4. CPU占满、数据库未优化
    5. 代码问题,代码效率,代码性能
    6. 包含了过多的分析类工具
2.2 代码部分
  • 构建层面

    未对代码进行打包、压缩、兼容性优化。

    未合并重复的请求、代码。

  • 编码层面

    没有良好的编码习惯,错误的编排JS与CSS

    for循环、迭代、同步、重定向、阻塞请求

    未删除重复、无用的代码

    未对逻辑业务复杂的代码进行重构,了解设计模式,对业务进行疏理

  • 机制(SSR,英文Server Side Render:服务器端渲染)

    未加入Async异步机制

    未思考页面加载、用户体验

  • 规范

    CSS规范

    HTML规范/HTML5规范

    Airbnb代码规范等。


三、优化方式
3.1 减少http请求
  • 合并js文件/合并css文件
  • 雪碧图的使用(css sprite)
  • 使用base64(肉联图片)表示简单的图片
3.2 使用内容传送网络CDN

用于分发传送内容的负载的服务器网络。从本质上讲,网站的副本存储在多个地理位置不同的数据中心,以便用户可以更快,更可靠地访问网站

3.3 避免空src或者是href值

空的src和href都会导致多余的HTTP请求,虽然不影响加载时间,但是会对服务器产生不必要的流量和压力。浏览器仍然会向服务器发起一个 HTTP 请求

src 产生请求的后果不容小憩:

  • 给服务器造成意外的流量负担,尤其时日 PV 较大时
  • 浪费服务器计算资源
  • 可能产生报错
3.4 gzip 压缩

启用 gzip 压缩可大幅缩减所传输的响应的大小(最多可缩减 90%),从而显著缩短下载相应资源所需的时间、减少客户端的流量消耗并加快网页的首次呈现速度

从HTTP / 1.1开始,Web客户端表示支持使用HTTP请求中的Accept-Encoding标头进行压缩

Accept-Encoding:gzip,deflate
3.5 CSS放在顶部,JS放在底部
  • 将内联样式块和<link>元素从页面<body>移动到页面<head>中。

    HTML 4.01规范(第12.3节)规定,始终把使用<link>标签的外部样式表放在<head>部分里,还要确保您指定的样式有正确的顺序。

  • <style>区块放在<head>部分里。

  • 使用css媒体类型,让CSS资源只在特定条件下使用

<!-- 没有使用媒体查询,这个css资源会阻塞渲染  -->
<link href="style.css"    rel="stylesheet">
<!-- all是默认类型,它和不设置媒体查询的效果是一样的 -->
<link href="style.css"    rel="stylesheet" media="all">
<!-- 动态媒体查询, 将在网页加载时计算。
根据网页加载时设备的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。-->
<link href="portrait.css" rel="stylesheet" media="orientation:portrait">
<!-- 只在打印网页时应用,因此网页首次在浏览器中加载时,它不会阻塞渲染。 -->
<link href="print.css"    rel="stylesheet" media="print">
3.6 减少DNS查找

用户访问网站的过程如下:

  1. 在地址栏输入网站地址,如www.example.com;

  2. 本地DNS得到这个请求,查询本地DNS缓存,如果有这条记录,则直接返回对应的IP;否则,请求网络上的DNS服务器,得到相应的IP,返回给客户机,并缓存这条记录;

  3. 浏览器向得到的IP发起建立连接请求,得到响应后建立连接,请求数据;

  4. Server端计算所需数据,并返回给client端;

  5. client端,即浏览器,解析数据并显示在浏览器窗口中,至此,请求完成。

缩短DNS解析的 方法可以通过延长DNS缓存的时间选用更快的DNS Server减少域名总数(例如原来有5个img server,分别为img1.xxx.com至img5.xxx.com,则现在可以减少到3个)等等

3.7 压缩资源

通过对外部资源进行压缩可以大幅度地减少浏览器需要下载的资源量,它会减少关键路径长度与关键字节,使页面的加载速度变得更快

3.8 避免3xx/4xx

3xx
重定向相关的HTTP响应代码,意思是用户的原始请求(例如请求A)被重定向到其他的请求(例如请求B)

每次页面重定向到另一个页面时,您的访问者都会面临等待HTTP请求 - 响应周期完成的额外时间。例如,如果移动重定向模式如下所示:

example.com - > www.example.com - > m.example.com - > m.example.com/home,这两个额外重定向中的每一个都会使您的页面成为可能加载速度慢。

常见的优化办法:

  • 最浪费的重定向经常发生、而且很容易被忽略:URL 末尾应该添加 / 但未添加。比如,访问 http://astrology.yahoo.com/astrology 将被 301 重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)。如果使用 Apache,可以通过 Aliasmod_rewriteDirectorySlash 解决这个问题。

  • 网站域名变更:CNAME 结合 Aliasmod_rewrite 或者其他服务器类似功能实现跳转。

  • 在定义链接地址的href属性的时候,尽量使用最完整的、直接的地址。例如:
    使用 www.cnblogs.com 而不是cnblogs.com
    使用cn.bing.com而不是bing.com
    使用www.google.com.hk而不是google.com
    使用www.mysite.com/products/而不是www.mysite.com/products

避免404浏览器找不到资源的情况发生

发出HTTP请求并获得无用的响应(即404 Not Found)是完全没必要的。特别糟糕的是当外部JavaScript的链接错误并且结果是404时。首先,此下载将阻止并行下载。接下来,浏览器可能会尝试解析404响应主体,就像它是JavaScript代码一样,这样就带来的性能的浪费

看不到的影响:

  • 例如请求favicon.ico文件,或者请求了某个不存在的脚本文件、样式表、图片文件,页面还是会按照正常的方式进行呈现。
  • 丢失的脚本文件、样式表、图片文件,会导致页面的某些行为、界面效果出现异常(也可能不是很明显)
  • 最大的问题可能是性能方面的影响。尤其是如果请求一个不存在的脚本文件,因为浏览器在请求脚本文件的时候,即便是返回404,它也会尝试去按照Javascript的方式解析响应中的内容。这无疑会增加很多处理的时间,而因为该文件不存在,所以这些都是无用功。

看得到的影响:

  • 如果用户请求的某个页面不存在,那么他将收到明确的回应
  • 默认情况下,他将收到一个标准的错误页面(请注意:不少用户会被这个页面吓到)

常见的优化办法:

404 意味着Not Found,意思是说未找到资源。既然如此,那么至少会有两种原因导致404错误:

  • 该资源按理说是要有,但我们没有提供。用户按照正常的方式来请求,所以资源找不到。
    • 为网站提供favicon.ico这种经常可能会被忽略的资源
    • 使用一些检查工具:比如Link checker
  • 该资源本来就不存在,用户按照不正常的方式来请求,当然还是找不到。
    • 避免用户收藏绝对地址,给后期更新带来隐患。可以使用地址Rewrite来重写,或者在设计阶段定义一些灵活友好的地址
    • 使用Routing技术,配置路由规则。
3.9 AJAX 优化
  1. POST的请求,是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200。(这里可以优化的是,服务器端对数据进行缓存,以便提高处理速度)
  2. GET的请求,是可以(并且默认)在客户端进行缓存的,除非指定了不同的地址,否则同一地址的AJAX请求,不会重复再服务器执行,而是返回304。

有的时候可能希望GET请求不被缓存,有几种做法来达到这样的目的:

  1. 每次调用的时候,请求不同的地址(可以在原始地址后面添加一个随机的号码)
  2. 如果你所使用的是jquery的话,则可以考虑禁用AJAX的缓存
$.ajaxSetup({ cache: false });

axios中

var config = {	headers: {'Content-Type': 'application/json','Cache-Control' : 'no-cache'}};
 
axios.get('/post',config)
3.10 减小 Cookie 大小

减小cookie的大小,因为在发请求时浏览器会将cookie信息发送到server端,所以应该只在cookie中存必要的信息且越长度越小越好。在 写cookie的时候要记得给cookie设置一个合理的过期时间及域

3.11 利用缓存

利用浏览器缓存 ,为链接或者资源,添加Expires或Cache-Control头

  • 对于静态组件:通过设置远期未来Expires标头实现“永不过期”策略
  • 对于动态组件:使用适当的Cache-Control标头来帮助浏览器处理条件请求
3.12 缩短服务器响应时间
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值