从浏览器渲染原理说一说如何实现高效的动画

写在前面在平时的工作中,页面的动画效果是很常见的需求。那么,怎么样实现一个高效的动画呢? 本文首发于公众号:符合预期的CoyPan注,本文谈到的浏览器,均为基于Chromium的现代浏览器。 页面渲染原理 一个页面展示在用户面前,简单来说,会经历以上5个步骤。我们可以把上面这个图称为像素管道。 Javascript: 执行js逻辑,修改DOM,修改CSS等。Style:计算样式。Layout:在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。这个步骤,就是我们常说的重排。Paint: 绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。这个步骤,就是我们常说的重绘。Composite:渲染层合并,由上一步可知,对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。在浏览器中,页面的渲染由浏览器的渲染进程完成,而渲染进程中,包含了主线程,worker线程,Compositer线程,Raster线程。上述像素管道的5个过程中,前4个过程,都由主线程完成,最后一个步骤,主要由Raster线程、Compositer线程完成。 JavaScript、Style、Layout像素管道中的前三个步骤,大家都很熟悉了。JavaScript、Style两个步骤,一图以蔽之: 接着是Layout,浏览器遍历render tree的每一个节点,计算其确切大小和位置。最终形成一个Layout Tree。 Paint在Paint之前,浏览器会根据Layout Tree,确定需要绘制的对象的层级,我们可以把这个层级叫做渲染层,最终生成Layer Tree。这个阶段被称作:Update Layer Tree 在Paint这个阶段,浏览器会根据Layer Tree,生成Paint Records。 Paint Records就是描述先画什么,再画什么的记录,跟我们写canvas代码时很像。Paint Records是根据渲染层划分的的。来看一个Paint Records的实例: 尽管生成了Pain Records,真正的绘制并不在Paint这个阶段完成的,而是在Composite阶段由Raster线程完成的。 Composite经过之前的几个步骤,浏览器主线程已经将页面的内容分成了若干渲染层。为了提升性能,某些特定的渲染层,会被提升为合成层。我们可以通过下面两个css属性,将某个元素强制提升为合成层: will-change: transform;// 或者transform: translateZ(0);注:提升为合成层的条件比较复杂,这里就不一一展开了。可以参考这篇文章:http://taobaofed.org/blog/201... 主线程在处理完所有的所有的数据后,会把数据提交到Compositer线程。Composite线程会利用Raster线程来做光栅化处理,并将处理好的内容存入内存中。随着Composite线程完成渲染层合成操作,扔给GPU,页面最终被渲染到屏幕上。 可以通过Chrome开发者工具中的Layer来查看合成层: 其他运行方式的像素管道上文中的像素管道共有5个步骤。不一定每帧都总是会经过管道每个部分的处理。实际上,不管是使用 JavaScript、CSS 还是网络动画,在实现视觉变化时,管道针对指定帧的运行还有其他两种方式: 第一种就是我们所说的页面没有进行重排,只进行了重绘;第二种就是页面既没有进行重排,也没有进行重绘。 最后这种运行方式的开销最小,适合于页面上的动画效果。 实现动画效果不考虑canvas等,有三种常见的方式来实现页面上的动效, 完全不用css3相关属性,仅使用setTimeout, setInterval, requestAnimationFrame,通过js修改DOM的样式来实现动画。使用纯css3来实现动画。js与css3相结合来实现动画。一般情况下,使用第一种方式的时候,虽然有的动画效果在进行过程中不会触发像素管道中的Layout,但是Paint往往是避免不了的。而使用css3来实现动画时,我们可以跳过Layout和Paint步骤。 下面,来看看三种实现方式下,浏览器的处理过程。 完全由JS驱动的动画代码如下: <html> <head> <style type="text/css"> #test2 { margin-top: 100px; width: 100px; height: 100px; position: relative; background-color: black; } </style> </head> <body> <p> 这是一段无用的文字,这是一段无用的文字,这是一段无用的文字,这是一段无用的文字,这是一段无用的文字,这是一段无用的文字 </p> <div id="test2"></div> <script type="text/javascript"> window.onload = function() { const el = document.getElementById('test2'); let left = 0; const startTimeStamp = Date.now(); const fn = function() { left += 2; if(Date.now() - startTimeStamp > 2000) { return; } el.style.left = left + 'px'; return window.requestAnimationFrame(fn); } window.requestAnimationFrame(fn) } </script> </body></html>选取动画过程中的一帧,浏览器的处理过程如下: ...

July 9, 2019 · 2 min · jiezi

浏览器组成和各引擎工作原理

1.浏览器的主要构成部分1.用户界面2.浏览器引擎(负责窗口管理、Tab进程管理等)3.渲染引擎(有叫内核,负责HTML解析、页面渲染)4.JS引擎(JS解释器,如Chrome和Nodejs采用的V8) 这里面最核心的就是渲染引擎和JS引擎,后面会详细介绍这两个引擎的相关内容。 常见浏览器的渲染引擎和JS引擎如下: 浏览器渲染引擎JS引擎IETridentChakraEdgeEdgeHTMLChakraFirefoxGeckoSpiderMonkeyChromeWebkit -> BlinkV8(著名的)SafriWebkitJavascriptcoreOperaPresto->BlinkCarakan注:新版本的Chrome采用的渲染引擎是Blink,Blink是由谷歌团队从Webkit衍生开发出来的引擎,主要有应用到Chrome和Opera浏览器。 2.从进程和线程的角度来理解浏览器工作1)进程和线程进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)线程是cpu调度的最小单位(线程是建立在进程的基础上的一个程序运行单位,一个进程中可以有多个线程)进程可以类比为工厂,线程就是工厂里面的工人,一个工厂可以包含一个或者多个工人,工人之间可以相互协作,并且共享工作空间 2)浏览器的多进程架构现代的浏览器采用的都是多进程架构,主要包含以下三种进程: 1.Browser进程 浏览器的主线程,主要负责浏览器的页面管理、书签、前进后退、资源下载管理等,整个浏览器应用程序只有一个,对应上述浏览器组成中的浏览器引擎。 2.渲染进程 内核进程、负责页面渲染、JS执行,对应的是上述的渲染引擎和JS引擎,一个浏览器可以包含多个渲染进程,每个Tab窗口页对应一个渲染进程 3.GPU进程 负责GPU渲染,整个浏览器应用程序只有一个 4.插件进程 浏览器安装的插件(扩展程序),每个插件会创建一个进程 当打开上面两个Tab时,Chrome任务管理器截图:主要包括 1个浏览器进程1个GPU进程1个网络进程2个渲染进程(对应一个Tab一个进程)4个扩展程序进程 MAC的活动监视器中的chorme进程,可以看到所有的渲染、扩展、GPU、网络进程都统一显示为Google Chrome Helper。 这种多进程浏览器架构主要有如下优势: 1.避免单个页面奔溃影响整个浏览器2.避免第三方插件奔溃影响整个浏览器3.充分利用多核优势3)浏览器的渲染进程浏览器有多个渲染进程、一个Tab页面一个(相同的Tab页面可能会被合并)一个渲染进程包含多个线程一个渲染进程主要包括如下线程: 1.GUI线程(主要负责解析HTML、CSS和渲染页面) 2.JS引擎线程(负责解析和执行JS代码) 3.事件线程(控制事件循环) 4.定时器线程(处理定时器相关逻辑) 5.异步请求线程(发起Ajax时会生成该线程) 线程规则: 1.GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,页面的更新操作会等到JS引擎空闲时执行,涉及任务和微任务相关知识 2.一个渲染进程同时只有一个JS解析线程在运行 3.JS引擎线程不停的处理事件线程推送到事件队列中的任务 4.定时器和异步请求最终生成的回调事件也有事件线程来控制和管理 了解了浏览器的渲染进程之后我们再来看看JS引擎。 4)从事件循环的角度来理解JS引擎的工作过程在理解什么是事件循环之前,我们先来了解下同步和异步的概念 1.同步和异步同步是代码执行后就可以获得想要的结果,异步是指代码执行之后不能立即获得结果, 你打电话问书店老板有没有《Javascript权威指南》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来返回结果。 2.同步任务和异步任务同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而是由事件线程调度,在满足执行条件的时候放到事件队列中,等待主线程(JS线程)的执行。 3.为什么要有事件循环的概念JS包含有异步操作(如:Ajax、定时器),这些异步操作完成之后需要通知JS引擎来处理异步操作的返回,如Ajax的Callback。这些异步操作什么时候完成是不确定的,所以需要有一个事件队列,事件线程将已经完成的异步操作的回调任务加载到事件队列中,JS引擎在执行完当前的同步任务之后循环从事件队列中取事件执行。 异步任务:setTImeout、setInterval、Promise、process.nextTick(Node.js)、Ajax 同步任务:除以上异步任务 同步任务和异步任务的执行流程如下: 异步任务统一有事件线程管理,当异步任务完成的时候会被放入到事件队列中,JS在顺序执行完当前的代码之后会从事件队列中读取任务,再重复整个流程,判断该任务是同步还是异步。 4.异步任务的优先级如果按照上述的简化理解,所有异步任务都按照满足执行条件的顺序放到事件队列中,世界很和平,先来先到,但是在ES6当中,引入了microtask的概念,microtask会在当前的任务执行完成之后立即执行。因为我们将异步任务分为task和microtask,我们又称为宏任务和微任务。 task:setTImeout、setInterval、ajax microtask:MutationObserve、promise、process.nextTick(Node.js) 这样子加了优先级的话JS的执行又会变得再复杂一点,如下图所示,异步任务执行完成之后会判断他是task还是microtask,再分别加到不同的时间队列中,JS当前任务执行完成之后优先清空当前的microtask队列,而且在每次执行完宏任务的时候都会去清空微任务。 示例: 运行如下示例,就可以验证上述执行流程 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body> <div class="outer"> <div class="inner"></div> </div> <script> // Let's get hold of those elements var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the // outer element new MutationObserver(function() { console.log('mutate'); }).observe(outer, { attributes: true }); // Here's a click listener… function onClick() { console.log('click'); setTimeout(function() { console.log('timeout'); }, 0); Promise.resolve().then(function() { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } // …which we'll attach to both elements inner.addEventListener('click', onClick); outer.addEventListener('click', onClick); </script></body></html>5.UI渲染线程什么时候工作UI渲染线程会在当前的Task执行完成之后,下一个Task执行之前执行,微任务会优先于UI渲染线程,这就意味着我们使用微任务更新的DOM能更快的被渲染出来。另外Vue.js最新版本数据变更的时候采用的是promise和MutationObserver创建微任务:https://github.com/vuejs/vue/... ...

May 10, 2019 · 2 min · jiezi

Web 应用安全性: 浏览器是如何工作的

这本系列的第一篇,先解释浏览器的功能以及执行方式。由于大多数客户将通过浏览器与 web 应用程序进行交互,因此必须了解这些出色程序的基础知识。浏览器是一个渲染引擎,它的工作是下载一个web页面,并以人类能够理解的方式渲染它。虽然这几乎是一种过于简单的过分简化,但我们现在需要知道的全部内容。用户在浏览器栏中输入一个地址。浏览器从该 URL 下载“文档”并渲染它。你可能习惯使用 Chrome,Firefox,Edge或Safari等流行的浏览器之一,但这并不意味着没有不同的浏览器。例如,lynx 是一种轻量级的、基于文本的浏览器,可以在命令行中工作。lynx 的核心原理与其他“主流”浏览器的原理完全相同。用户输入 web 地址(URL),浏览器获取文档并呈现它——唯一的区别是 lynx 不使用可视化渲染引擎,而是使用基于文本的界面,这使得像谷歌这样的网站看起来像这样:我们大致了解浏览器的功能,但是让我们仔细看看这些机智的应用程序为我们所做的步骤。浏览器做了什么?长话短说,浏览器的工作主要包括:DNS 解析HTTP 交换渲染重复以下步骤DNS 解析这个过程确保一旦用户输入 URL,浏览器就知道它必须连接到哪个服务器。浏览器联系 DNS 服务器,发现google.com 翻译成 216.58.207.110,这是一个浏览器可以连接的 IP 地址。HTTP 交换一旦浏览器确定了哪个服务器将为我们的请求提供服务,它将启动与它的 TCP 连接并开始 HTTP 交换。 这只是浏览器与服务器通信所需内容以及服务器回复的一种方式。HTTP 只是用于在 Web 上进行通信协议的名称,而浏览器一般通过 HTTP 与服务器进行通信。 HTTP 交换涉及客户端(我们的浏览器)发送请求,服务器回复响应。例如,当浏览器成功连接到 google.com 背后的服务器后,它将发送一个如下所示的请求:GET / HTTP/1.1Host: google.comAccept: /让我们一行一行地把请求分解:GET / HTTP/1.1:在第一行中,并补充说其余请求将遵循 HTTP/1.1 协议(它也可以使用1.0或2)Host: google.com:这是 HTTP/1.1 中唯一必须的 HTTP 报头。因为服务器可能服务多个域(google.com, google.co.uk) 。这里的客户端提到请求是针对特定的主机的。Accept: /:一个可选的标头,其中浏览器告诉服务器接受任何类型的响应。服务器可以拥有 JSON、XM L或HTML 格式的可用资源,因此它可以选择自己喜欢的格式。作为客户端的浏览器发送请求之后,就轮到服务器进行响应了,这是响应的格式如下:HTTP/1.1 200 OKCache-Control: private, max-age=0Content-Type: text/html; charset=ISO-8859-1Server: gwsX-XSS-Protection: 1; mode=blockX-Frame-Options: SAMEORIGINSet-Cookie: NID=1234; expires=Fri, 18-Jan-2019 18:25:04 GMT; path=/; domain=.google.com; HttpOnly<!doctype html><html">……</html>哇,有很多信息需要消化。服务器让我们知道请求是成功的(200 OK),并向响应中添加一些头部信息,例如,它告知哪个服务器处理了我们的请求(Server:gws),该响应的 X-XSS-Protection 策略是什么,等等。现在,你不需要理解响应中的每一行,在本系列后面的文章中,我们将介绍 HTTP 协议及其头部等内容。现在,你只需要了解客户端和服务器正在交换信息,并且它们是通过 HTTP 进行交换的。渲染<!doctype html><html">……</html>在响应的主体中,服务器根据 Content-Type 头包括响应类型来表示。 在我们的例子中,内容类型设置为 text/ html,因此我们期待响应中的 HTML 标记 - 这正是我们在正文中找到的。这才是浏览器真正的亮点所在。它解析 HTML,加载标记中包含的额外资源(例如,可能需要获取JavaScript文件或CSS文档),并尽快将它们呈现给用户。最终的结果是普通人能够理解的:如果想要更详细地了解当我们在浏览器地址栏中按回车键时会发生什么,建议阅读“What happens when…”,这是一个非常精细的尝试来解释该过程背后的机制。由于这是一个关注安全性的系列文章,从刚刚了解到的内容可以提到提示:攻击者可以轻松地利用 HTTP 交换和渲染部分中的漏洞谋生。漏洞和恶意用户也潜伏在其他地方,但是这些级别上更好的安全方法已经允许你在改进安全性方面取得进展。供应商4 个最流行的浏览器属于不同的公司:谷歌的 ChromeMozilla 的火狐苹果的 Safari微软的 Edge除了为了增加市场渗透率而相互竞争之外,供应商也为了提高 web 标准而相互合作,这是对浏览器的一种“最低要求”。W3C是标准开发的主体,但是浏览器开发自己的特性并最终成为 web 标准的情况并不少见,安全性也不例外。例如,Chrome 51 引入了 SameSite cookie,该功能允许 Web 应用程序摆脱称为 CSRF 的特定类型的漏洞(稍后将详细介绍)。其他供应商认为这是一个好主意,并纷纷效仿,导致 SameSite 成为 web 标准:到目前为止,Safari 是唯一没有 SameSite cookie 支持的主流浏览器。这告诉我们两件事:Safari似乎并不关心用户的安全性(开玩笑:Safari 12中将提供SameSite cookie,这可能在你阅读本文时已经发布)修补一个浏览器上的漏洞并不意味着所有用户都是安全的第一点是对 Safari 的一次尝试(正如我提到的,开玩笑的!),而第二点非常重要。在开发web应用程序时,我们不仅需要确保它们在不同的浏览器中看起来是相同的,还需要确保我们的用户在不同的平台上受到相同的保护。你的网络安全策略应根据浏览器供应商允许我们执行的操作而有所不同。 如今,大多数浏览器都支持相同的功能集,并且很少偏离其常见的路线图,但是上面的实例仍然会发生,这是我们在定义安全策略时需要考虑的事情。在我们的例子中,如果我们决定只通过 SameSite cookie 来减轻 CSRF 攻击,那么我们应该意识到我们正在将 Safari 用户置于危险之中。我们的用户也应该知道这一点。最后但并非最不重要,你应该记住,你可以决定是否支持浏览器版本:支持每一个浏览器版本将是不切实际的(想想 Internet Explorer 6)。虽然确保最近几个版本的主流浏览器的支持通常是一个好的决定,但是如果你不打算在特定的平台上提供保护,一般建议让你的用户知道。专业提示:你不应该鼓励你的用户使用过时的浏览器,或积极支持他们。尽管你可能已经采取了所有必要的预防措施,但是其他web开发人员可能没有。鼓励用户使用主流浏览器支持的最新版本。供应商还是标准bug?普通用户通过第三方客户端(浏览器)访问我们的应用程序这一事实增加了另一层次的间接性:浏览器本身可能存在安全漏洞。‘供应商通常会向能够发现浏览器自身漏洞的安全研究人员提供奖励(即 bug奖金)。这些bug与你的实现无关,而是与浏览器本身处理安全性的方式有关。例如,Chrome 奖励计划可让安全工程师与 Chrome 安全团队联系,报告他们发现的漏洞。 如果确认了这些漏洞,则会发布补丁,通常会向公众发布安全建议通知,研究人员会从该计划中获得(通常是财务上的)奖励。像谷歌这样的公司在他们的Bug赏金项目中投入了相对较多的资金,这使得他们能够通过承诺在发现应用程序的任何问题时获得经济利益来吸引研究人员。在一个漏洞赏金计划中,每个人都是赢家:供应商设法提高其软件的安全性,研究人员也因此获得报酬。我们将在后面讨论这些程序,因为我相信Bug赏金计划应该在安全领域有自己的一节。Jake Archibald 是谷歌的一名开发人员,他最近发现了一个影响多个浏览器的漏洞。他在一篇有趣的博客文章中记录了他的努力,他如何接触不同的供应商,以及他们的反应,建议你阅读 这篇文章。开发人员的浏览器到目前为止,我们应该理解一个非常简单但相当重要的概念:浏览器只是为普通网络冲浪者构建的 HTTP 客户端。它们肯定比平台的纯HTTP客户端更强大(例如,考虑NodeJS的require(‘HTTP’)),但归根结底,它们“只是”更简单的 HTTP客户端的自然演化。作为开发人员,我们选择的HTTP客户机可能是 Daniel Stenberg 的 cURL,他是 web 开发人员每天使用的最流行的软件程序之一。它允许我们通过从命令行发送 HTTP 请求来实时执行 HTTP 交换:$ curl -I localhost:8080HTTP/1.1 200 OKserver: ecstatic-2.2.1Content-Type: text/htmletag: “23724049-4096-“2018-07-20T11:20:35.526Z"“last-modified: Fri, 20 Jul 2018 11:20:35 GMTcache-control: max-age=3600Date: Fri, 20 Jul 2018 11:21:02 GMTConnection: keep-alive在上面的示例中,我们在 localhost:8080/ 上请求了文档,本地服务器成功响应。在这里,我们没有将响应的主体显示在命令行,而是使用了 -I 标志,它告诉 cURL 我们只对响应头感兴趣。更进一步,我们可以指示 cURL 显示更多的信息,包括它执行的实际请求,以便更好地查看整个HTTP交换。需要使用的选项是-v(详细):$ curl -I -v localhost:8080 Rebuilt URL to: localhost:8080/ Trying 127.0.0.1…* Connected to localhost (127.0.0.1) port 8080 (#0)> HEAD / HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.47.0> Accept: />< HTTP/1.1 200 OKHTTP/1.1 200 OK< server: ecstatic-2.2.1server: ecstatic-2.2.1< Content-Type: text/htmlContent-Type: text/html< etag: “23724049-4096-“2018-07-20T11:20:35.526Z"“etag: “23724049-4096-“2018-07-20T11:20:35.526Z”"< last-modified: Fri, 20 Jul 2018 11:20:35 GMTlast-modified: Fri, 20 Jul 2018 11:20:35 GMT< cache-control: max-age=3600cache-control: max-age=3600< Date: Fri, 20 Jul 2018 11:25:55 GMTDate: Fri, 20 Jul 2018 11:25:55 GMT< Connection: keep-aliveConnection: keep-alive<* Connection #0 to host localhost left intact主流浏览器通过它们的 DevTools 可以获得几乎相同的信息。正如我们所见,浏览器只不过是精心设计的HTTP客户端。 当然,他们添加了大量的功能(想到凭据管理,书签,历史等),但事实是,它们是作为人类的 HTTP 客户端而诞生的。 这很重要,因为在大多数情况下,不需要使用浏览器来测试Web应用程序的安全性,因为你可以简单的通过 curl 命令来查看响应信息。进入 HTTP 协议正如我们所提到的,HTTP交换和渲染阶段是我们主要要涉及的阶段,因为它们为恶意用户提供了最大数量的攻击媒介。在下一篇文章中,我们将深入研究HTTP协议,并尝试了解为了保护HTTP交换,我们应该采取哪些措施。原文:https://medium.freecodecamp.o…你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

March 4, 2019 · 2 min · jiezi

【笔记】重学前端-winter

本文为:winter 发布在极客时间 【重学前端】系列课程的的笔记和总结支持正版哦: https://time.geekbang.org/col…导语如果深入进去了解,你会发现,表面上看他们可能是一时忘记了,或者之前没注意但实际上是他们对于前端的知识体系和底层原理没有真正系统地理解前端学习方法:一是立足标准,系统性总结和整理前端知识,建立自己的认知和方法论二是放眼团队,从业务和工程角度思考前端团队的价值和发展需要第一个方法:建立知识架构知识的“目录”或者索引:把零散的知识组织起来,也能够帮助我们发现一些知识上的盲区,面试时,定位到知识架构中的位置,相关点讲出第二个方法:追本溯源看这个出现的背景和原因,提出他是为了解决什么或者其底层是什么,其中还可以发掘一些趣闻,便于记忆课程目标:把无法通过查阅解决的原理和背景讲清楚不方便查阅和记忆的内容整理好前端知识体系:用一定的词法和语法,表达一定语义,从而操作运行时数据结构类型:JavaScript 的类型系统就是它的 7 种基本类型和 7 种语言类型实例:内置对象部分算法:JavaScript 的执行过程文档元信息:通常是出现在 head 标签中的元素,包含了描述文档自身的一些信息语义相关:扩展了纯文本,表达文章结构、不同语言要素的标签;链接:提供到文档内和文档外的链接替换型标签:引入声音、图片、视频等外部元素替换自身的一类标签…表单:用于填写和提交信息的一类标签;表格:表头、表尾、单元格等表格的结构。把 HTML 当作一门语言来了解下:语法和语言机制补充标准:ARIA,它是 HTML 的扩展,在可访问性领域,它有至关重要的作用1.HTML语义定义:语义类标签则是纯文字的补充,eg:标题、自然段、章节、列表,这些内容都是纯文字无法表达的建议:只靠 div 和 span 就能走天下了:用于描述“软件界面”多过于“富文本”好处:1.无css时,清晰的目录结构2.SEO3.读屏软件,根据文章可以自动生成目录 eg:阅读视图功能使用场景:1.作为自然语言和纯文本的补充,用来表达一定的结构或者消除歧义 eg: ruby(注释)/em(重音)2.文章标题摘要hgroup 标签:在 hgroup 中的 h1-h6 被视为同一标题的不同组成部分<hgroup><h1>JavaScript 对象 </h1><h2> 我们需要模拟类吗?</h2></hgroup><p>balah balah</p>……不加hgroupsection 的嵌套会使得其中的 h1-h6 下降一级,HTML5之后,只需要 section 和 h1 就足以形成文档的树形结构<section> <h1>HTML 语义 </h1> <p>balah balah balah balah</p> <section> <h1> 弱语义 </h1> <p>balah balah</p> </section> <section> <h1> 结构性元素 </h1> <p>balah balah</p> </section>……</section>3.适合机器阅读的整体结构“阅读模式”,以及各种非浏览器终端的出现<body> <header> <nav> …… </nav> </header> <aside> <nav> …… </nav> </aside> <section>……</section> <section>……</section> <section>……</section> <footer> <address>……</address> </footer></body>article,报纸的多文章结构适合用 article 来组织,article为独立性质文章,article与body有相似结构,也可包含header/footerheader,如其名,通常出现在前部,表示导航或者介绍性的内容footer,通常出现在尾部,包含一些作者信息、相关链接、版权信息aside 表示跟文章主体不那么相关的部分,它可能包含导航、广告等工具性质的内容2.HTML语义:如何运用语义类标签来呈现Wiki网页?实现如下wiki网页需要的标签:https://en.wikipedia.org/wiki…aside: 左侧侧边栏/导航性质的工具内容article: 独立为文章主体abbr 标签表示缩写hr 表示故事走向的转变或者话题的转变strong 重要,黑体blockquote, q, cite: blockquote 表示段落级引述内容,q 表示行内的引述内容,cite 表示引述的作品名。time 机器阅读方便figure, figcaption 表示与主文章相关的图像、照片等流内容dfn 被包裹的名词pre 不需要浏览器帮我们做自动换行,不需要浏览器进行排版。samp 计算机程序的示例输出code 代码除上面用到的标签之外,还需要用到:3.JavaScript类型:关于类型,有哪些你不知道的细节? ...

February 21, 2019 · 1 min · jiezi

掌握浏览器重绘(reflow)重排(repaint)-前端进阶

很多人都知道要减少浏览器的重排和重绘,但对其中的具体原理以及如何具体操作并不是很了解,当突然提起这个话题的时候,还是会一脸懵逼。希望大家可以耐着性子阅读本文,仔细琢磨,彻底掌握这个知识点!博客、前端积累文档、公众号、GitHub网页生成过程:HTML被HTML解析器解析成DOM 树css则被css解析器解析成CSSOM 树结合DOM树和CSSOM树,生成一棵渲染树(Render Tree)生成布局(flow),即将所有渲染树的所有节点进行平面合成将布局绘制(paint)在屏幕上第四步和第五步是最耗时的部分,这两步合起来,就是我们通常所说的渲染。网上找了一张图片,我加了注释会更直观一些:渲染:网页生成的时候,至少会渲染一次。在用户访问的过程中,还会不断重新渲染重新渲染需要重复之前的第四步(重新生成布局)+第五步(重新绘制)或者只有第五个步(重新绘制)。重排比重绘大:大,在这个语境里的意思是:谁能影响谁?重绘:某些元素的外观被改变,例如:元素的填充颜色重排:重新生成布局,重新排列元素。就如上面的概念一样,单单改变元素的外观,肯定不会引起网页重新生成布局,但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分。比如改变元素高度,这个元素乃至周边dom都需要重新绘制。也就是说:“重绘"不一定会出现"重排”,“重排"必然会出现"重绘"重排(reflow):概念:当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。重排也叫回流,重排的过程以下面这种理解方式更清晰一些:回流就好比向河里(文档流)扔了一块石头(dom变化),激起涟漪,然后引起周边水流受到波及,所以叫做回流常见引起重排属性和方法任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发重排,下面列一些栗子:添加或者删除可见的DOM元素;元素尺寸改变——边距、填充、边框、宽度和高度内容变化,比如用户在input框中输入文字浏览器窗口尺寸改变——resize事件发生时计算 offsetWidth 和 offsetHeight 属性设置 style 属性的值常见引起重排属性和方法 widthheightmarginpaddingdisplayborderpositionoverflowclientWidthclientHeightclientTopclientLeftoffsetWudthoffsetHeightoffsetTopoffsetLeftscrollWidthscrollHeightscrollTopscrollLeftscrollIntoView()scrollTo()getComputedStyle()getBoundingClientRect()scrollIntoViewIfNeeded()重排影响的范围:由于浏览器渲染界面是基于流失布局模型的,所以触发重排时会对周围DOM重新排列,影响的范围有两种:全局范围:从根节点html开始对整个渲染树进行重新布局。局部范围:对渲染树的某部分或某一个渲染对象进行重新布局全局范围重排:<body> <div class=“hello”> <h4>hello</h4> <p><strong>Name:</strong>BDing</p> <h5>male</h5> <ol> <li>coding</li> <li>loving</li> </ol> </div></body>当p节点上发生reflow时,hello和body也会重新渲染,甚至h5和ol都会收到影响。局部范围重排:用局部布局来解释这种现象:把一个dom的宽高之类的几何信息定死,然后在dom内部触发重排,就只会重新渲染该dom内部的元素,而不会影响到外界。尽可能的减少重排的次数、重排范围:重排需要更新渲染树,性能花销非常大:它们的代价是高昂的,会破坏用户体验,并且让UI展示非常迟缓,我们需要尽可能的减少触发重排的次数。重排的性能花销跟渲染树有多少节点需要重新构建有关系:所以我们应该尽量以局部布局的形式组织html结构,尽可能小的影响重排的范围。而不是像全局范围的示例代码一样一溜的堆砌标签,随便一个元素触发重排都会导致全局范围的重排。重绘(Repaints):概念:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。常见的引起重绘的属性: colorborder-stylevisibilitybackgroundtext-decorationbackground-imagebackground-positionbackground-repeat outline-coloroutlineoutline-styleborder-radiusoutline-widthbox-shadowbackground-size浏览器的渲染队列:思考以下代码将会触发几次渲染?div.style.left = ‘10px’;div.style.top = ‘10px’;div.style.width = ‘20px’;div.style.height = ‘20px’;根据我们上文的定义,这段代码理论上会触发4次重排+重绘,因为每一次都改变了元素的几何属性,实际上最后只触发了一次重排,这都得益于浏览器的渲染队列机制:当我们修改了元素的几何属性,导致浏览器触发重排或重绘时。它会把该操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行这些操作。强制刷新队列:div.style.left = ‘10px’;console.log(div.offsetLeft);div.style.top = ‘10px’;console.log(div.offsetTop);div.style.width = ‘20px’;console.log(div.offsetWidth);div.style.height = ‘20px’;console.log(div.offsetHeight);这段代码会触发4次重排+重绘,因为在console中你请求的这几个样式信息,无论何时浏览器都会立即执行渲染队列的任务,即使该值与你操作中修改的值没关联。因为队列中,可能会有影响到这些值的操作,为了给我们最精确的值,浏览器会立即重排+重绘。强制刷新队列的style样式请求:offsetTop, offsetLeft, offsetWidth, offsetHeightscrollTop, scrollLeft, scrollWidth, scrollHeightclientTop, clientLeft, clientWidth, clientHeightgetComputedStyle(), 或者 IE的 currentStyle我们在开发中,应该谨慎的使用这些style请求,注意上下文关系,避免一行代码一个重排,这对性能是个巨大的消耗重排优化建议就像上文提到的我们要尽可能的减少重排次数、重排范围,这样说很泛,下面是一些行之有效的建议,大家可以参考一下。1. 分离读写操作div.style.left = ‘10px’;div.style.top = ‘10px’;div.style.width = ‘20px’;div.style.height = ‘20px’;console.log(div.offsetLeft);console.log(div.offsetTop);console.log(div.offsetWidth);console.log(div.offsetHeight);还是上面触发4次重排+重绘的代码,这次只触发了一次重排:在第一个console的时候,浏览器把之前上面四个写操作的渲染队列都给清空了。剩下的console,因为渲染队列本来就是空的,所以并没有触发重排,仅仅拿值而已。2. 样式集中改变div.style.left = ‘10px’;div.style.top = ‘10px’;div.style.width = ‘20px’;div.style.height = ‘20px’;虽然现在大部分浏览器有渲染队列优化,不排除有些浏览器以及老版本的浏览器效率仍然低下:建议通过改变class或者csstext属性集中改变样式// badvar left = 10;var top = 10;el.style.left = left + “px”;el.style.top = top + “px”;// good el.className += " theclassname”;// goodel.style.cssText += “; left: " + left + “px; top: " + top + “px;";3. 缓存布局信息// bad 强制刷新 触发两次重排div.style.left = div.offsetLeft + 1 + ‘px’;div.style.top = div.offsetTop + 1 + ‘px’;// good 缓存布局信息 相当于读写分离var curLeft = div.offsetLeft;var curTop = div.offsetTop;div.style.left = curLeft + 1 + ‘px’;div.style.top = curTop + 1 + ‘px’;4. 离线改变dom隐藏要操作的dom在要操作dom之前,通过display隐藏dom,当操作完成之后,才将元素的display属性为可见,因为不可见的元素不会触发重排和重绘。dom.display = ’none’// 修改dom样式dom.display = ‘block’通过使用DocumentFragment创建一个dom碎片,在它上面批量操作dom,操作完成之后,再添加到文档中,这样只会触发一次重排。复制节点,在副本上工作,然后替换它!5. position属性为absolute或fixedposition属性为absolute或fixed的元素,重排开销比较小,不用考虑它对其他元素的影响6. 优化动画可以把动画效果应用到position属性为absolute或fixed的元素上,这样对其他元素影响较小动画效果还应牺牲一些平滑,来换取速度,这中间的度自己衡量:比如实现一个动画,以1个像素为单位移动这样最平滑,但是reflow就会过于频繁,大量消耗CPU资源,如果以3个像素为单位移动则会好很多。启用GPPU加速此部分来自优化CSS重排重绘与浏览器性能GPU(图像加速器):GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成,因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率。GPU 加速通常包括以下几个部分:Canvas2D,布局合成, CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和视频(video)。/* * 根据上面的结论 * 将 2d transform 换成 3d * 就可以强制开启 GPU 加速 * 提高动画性能 */div { transform: translate3d(10px, 10px, 0);}结语重排也是导致DOM脚本执行效率低的关键因素之一,重排与重绘作为大厂经常出现的面试题,并且涉及的性能优化,这是前端必须掌握的基本概念/技能之一(敲黑板!)。重排会不断触发这是不可避免的,但我们在开发时,应尽量按照文中的建议来组织代码,这种优化,需要平时有意识的去做,一点一滴的去做,希望大家重视一下。希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。博客、前端积累文档、公众号、GitHub以上2018.12.17参考资料:网页性能管理详解优化CSS重排重绘与浏览器性能 ...

December 24, 2018 · 1 min · jiezi