一. 如何理解HTML语义化?
对web、html语义化的理解:使用恰当语义的HTML标签和CSS,class等内容,让页面具有良好的语义和结构;从而方便人类和机器都可以快速理解网页内容。
其核心内容分为以下4点:
- 用正确的标签做正确的事情
- 页面结构化
- 无CSS的情况下也能进行网页阅读
- 方便浏览器,搜索引擎解析;有利于SEO
1. header
header代表网页或者内容块(section)的页眉,通常包含hgroup,h1-h6标签;
作为整个网页或者内容块的标题,可以包裹一节的目录部分,一个搜索框,,一个nav,相关logo等内容。
示例:
2. footer
footer代表网页或者内容块(section)的页脚,包含网页的基本信息如:文档创作的作者,文档的版权信息,使用条款的链接,联系信息等。
示例:
3. hgroup
hgroup代表网页或者内容块(section)的标题,文章主标题和副标题组合时可以使用hgroup包裹;
连续使用多个h1-h6时需要用hgroup包裹;
只是用单个h1-h6时不需要用hgroup包裹。
示例:
4. nav
nav标签用于页面的导航链接区域,规定只适用于页面的主要导航区域
5. aside
- aside在article中可以作为主要内容的附属信息,如与当前文章有关的资料,标签,名词解释
- aside在article外作为页面或者站点的全局附属信息部分,如侧边栏,可以包含其他组的导航,广告等
6. article
article代表一个在文档,页面或者网站自成一体的内容;可以包含header和footer
是特殊的section,比section有更明确的语义;当一段内容脱离语境后还是完整独立的,应该使用article;
应用场景:新闻博客文章,论坛帖子,用户评论等
示例:
7. ARIA
ARIA无障碍富互联网应用,目的是使有功能障碍的人群更容易访问web网页和web应用
属性role标识元素的作用
示例:
参考:如何理解HTML结构的语义化?
二. scrpit中defer和async的区别
defer和async属性用于异步加载外部的script资源,他们有如下的区别
| 类型 | 执行顺序 | 是否阻塞解析HTML | 与DOMContentLoaded执行顺序 |
|---|---|---|---|
<script> |
HTML中的排列顺序 | 阻塞 | 在DOMContentLoaded之前执行,算是解析HTML的一部分 |
<script async>``<script defer async> |
请求返回的顺序,谁先返回先执行谁 | 可能阻塞,请求返回后HTML没有解析完成则阻塞解析HTML | 在DOMContentLoaded之后执行,不算解析HTML的一部分 由此可见DOMContentLoaded触发不需要等待async脚本,样式图片加载 |
<script defer> |
HTML中的排列顺序 | 不阻塞,HTML解析完成后执行脚本 | 在DOMContentLoaded之前执行,算是解析HTML的一部分 |
1. script在中,没有添加任何属性
2. script在底部,没有添加任何属性
3. script在中,添加async标签;同时添加defer,async标签
4. script在中,添加defer标签

问题:DOMContentLoaded和load区别?
答:DOMContentLoaded是在DOM解析完成后执行
load是所有外部资源加载完成后执行,包括css,图片等
三. 从浏览器输入URL到请求返回发生了什么
1. 输入网址并解析
1)url的组成
URI只支持ASCII编码,所以引入URI编码将非ASCII字符和界定符转换为16进制字节值,并加上%
从左到右如图所示
| 协议(scheme) | 常见http,https,ftp。。。 |
|---|---|
| 主机(host) | 可以时域名或者IP地址 |
| 端口(port) | 不加默认80端口 |
| 路径(path) | 路径可以不包含文件名,访问默认文件,取决于服务器实现 |
| 查询参数(query) | 以?开头,key=value的形式 |
| 锚点(anchor) | 以#开头,网页内部的定位点,文件内部位置 |
2)浏览器缓存
强缓存
浏览器首先会判度请求是否命中了强缓存或者协商缓存。根据上次请求的响应头Cache-Control或Expires判断是否命中强缓存;根据If-None-Match和If-Modified-Since判断是否命中协商缓存
如果命中强缓存,浏览器不会真正发送请求,而是从浏览器缓存中(from disk cache或from memery cache)读取数据并返回状态码200(firefox是304);
强缓存的使用场景:公共js、css,图片。。。
以下是两个参数的对比
| 响应头 | 协议版本 | 优先级 | 格式 | 命中条件 | 响应头其他值 | 请求头 |
|---|---|---|---|---|---|---|
| Cache-Control | http1.1 | 高,如果两者同时存在忽略Expires | Cache-Control:max-age=3600 max-age的单位是秒 |
服务响应头中若有Cache-Control会自动添加Date属性,如果发送请求时间超过Date+maxAge则没有命中缓存 | 客户端是否缓存数据,数据过期时间由服务端决定 - no-cache或max-age=0:客户端缓存;不使用强缓存,验证协商缓存 - no-store:客户端不缓存;强缓存和协商缓存都不使用,直接向服务端请求 |
当客户端不想使用缓存数据的时候可以添加Cache-Control控制 - max-age=0:不使用强缓存,验证协商缓存 - no-cache:不使用强缓存和协商缓存,直接请求 |
其他值无效 |
| Expires | http1.0 | 低,现在基本不会使用。作为兼容可以保留 | Expires: Wed, 22 Nov 2019 08:41:00 GMT
(GMT格式的日期) | 服务响应头中若有Expires会自动添加Date属性,如果超过Expires的过期时间则没有命中缓存 | | |
下面是验证的情况:
请求命中了Cache-Control强缓存
请求命中了Expires强缓存
没有命中强缓存,因为Cache-Control含有max-age或s-max-age时会忽略Expires

协商缓存
如果没有命中强缓存,就可以由服务器决定是否使用缓存数据,称为协商缓存;协商缓存实际发送请求并由服务端决定是否使用缓存。
本次请求会根据上次请求的响应头ETag自动在请求头中添加If-None-Match,Last-Modified自动添加If-Modified-Since,如果命中缓存服务端返回304状态码
协商缓存的使用场景:HTML
| 第一次请求响应头 | 第二次请求请求头 | 版本 | 优先级 | 优点 |
|---|---|---|---|---|
| ETag | If-None-Modified | http1.1 | 高(由服务端决定) | 1. 针对周期更新,但文件内容不一定真正改变的情况,ETag能检查文件是否真的改变了 1. 提高控制细粒度,If-Modified-Since的单位是秒,低于秒级的文件改动可由ETag检查到 1. 某些服务器不能获取文件的修改时间 |
| Last-Modified | If-Modified-Since | http1.0 | 低(由服务端决定) |
第一次请求服务器返回ETagheader,第二次请求自动带上if-None-Match,命中了协商缓存

第一次请求服务器返回Last-Modifiedheader,第二次请求自动带上if-Modified-Since,命中了协商缓存
3) DNS解析
DNS(Domain Name System)域名系统。DNS域名解析就是将域名转换成IP地址。DNS解析使用UDP的连接方式
DNS解析流程是一个递归+迭代的过程;从客户端到(ISP的)本地DNS服务器的查询是递归的过程;从本地服务器到根/域服务器的查询是迭代的过程
- 客户端到本地DNS服务器的递归查询,在统称为DNS高速缓存中查询,流程如下:

- 如果从本地DNS服务器上查不到IP地址,就要本地DNS服务器从根/域服务器上迭代查询,如果在某个根/域服务器上查到直接返回给客户端并在DNS高速服务器中缓存;
迭代查询的流程如下:假设需要查询mail.goole.com的地址,本地DNS服务器首先向根服务器查询mail.google.com的IP地址,根服务器说:我不知道,但是我有com顶级域名服务器的地址,你去问他吧;本地DNS服务器继续向com域名服务器地址查询mail.google.com的IP地址,com服务器说,我不知道但我知道google.com的IP地址,你去问他吧。。。直到找到mail
.google.com的地址返回给本地DNS服务器,IP地址返回给客户端并在DNS高速服务器中缓存。流程如下所画
- DNS负载均衡
现在网站都有多个服务器,当一个网站访问量过大,如果所有的请求都访问同一个服务器,这个服务器可能会崩掉。
当一个网站有多个服务器时,每次应答DNS查询时可以解析成不同的IP地址,引导用户访问不同的服务器,从而达到负载均衡的目的。
应答的IP地址可以根据服务器负载情况,距离用户远景而定。
- DNS预解析
大型网站有很多资源需要,需要预加载的时候考虑使用DNS预解析。可以解析当前页面没有的跨域资源的DNS。
使用link标签<link rel="dns-prefetch" href="//google.com">,有如下几个注意点
- 必须是跨域资源,因为本站的IP已经被解析
- 考虑配合preconnect预先建立TCP/IP连接,TLS握手使用,进一步减少跨域请求的延迟
- 预解析的数量适当,否则适得其反

问题:大家都访问DNS根服务器,能承受的住吗?
答:有DNS缓存技术和DNS负载均衡,大多数查询都绕过了根服务器。
参考:是时候搞懂DNS了
2. TCP/IP连接
4)TCP/IP:三次握手
TCP/IP协议族是互联网普遍使用的4层网络模型,和OSI的七层网络模型对比如下:
| OSI | TCP/IP | 作用 |
|---|---|---|
| 应用层 | 应用层 | 为应用程序提供网络服务 |
| 表示层 | 数据格式化,加密,解密 | |
| 会话层 | 建立,维护,管理会话连接 | |
| 传输层 | 传输层 | 建立,维护,管理端到端连接 |
| 网络层 | 网络层 | IP寻址与路由控制 |
| 数据链路层 | 链路层 | 控制网络层与物理层之间的通信 |
| 物理层 | 比特流传输 |
TCP/IP连接的三次握手的流程如下:
- 第一次:客户端A发送SYN=1,产生随机数Seq Number=X(123456)的数据包发动到服务端B,B由控制位的SYN=1知道A需要建立连接。
- 第二次:B发送SYN=1,ACK=X+1,Seq Number=Y,随机数Seq Number=Y的数据包发送到A。这一步B确认了A的发送能力。此时B处于半连接状态SYN_RCVD
- 第三次:A收到后检查SYN==1,ACK==X+1是否正确。然后发送ACK=Y+1,Seq Number=Z的数据包给B,B收到后确认ACK和Seq Number后就代表连接完成。这里A确认了B的发送和接收能力,B确认了A的接收能力
TCP/IP断开连接的四次挥手:
TCP/IP的连接是全是双工的,允许任何一端优先发起断开连接的操作。如果主机A发起断开连接的数据包,意味着它不再发送信息,但可以接收信息,所以需要两个方向上分别断开连接。
流程如下:
- 第一次挥手:主机A发送FIN=1,Seq Number=X的断开请求;
- 第二次挥手:主机B接受到后,发送ACK=X+1。主机B知道主机A需要断开连接
- 第三次挥手:主机B发送FIN=1,Seq Number=Y的数据包
- 第四次挥手:主机A接受后,发送ACK=Y+1的数据包;主机B接收后确认连接断开
3. HTTP请求
5) HTTP
HTTP的特点如下:
- 灵活可拓展:报文只规定了基本的格式,可以请求各种类型的数据
- 可靠传输:基于TCP/IP传输报文
- 无状态:请求之间没有上下文信息,都是独立的
- 请求-应答机制:代理服务器也可以发送请求
- 明文传输:报文头部使用文本传输,带来安全隐患
队头阻塞:当http开启长连接,共用一个TCP连接,一次只能处理一个请求;如果当前请求处理时间过长,造成对头阻塞1. HTTP报文的结构
HTTP请求和响应的报文一致,都为起始行+请求头+空行+实体
请求报文:
响应报文: ```http HTTP/1.1 200 OK // 响应起始行组成:HTTP版本 + 状态码 + 响应短语GET /home HTTP/1.0 // 请求起始行组成:方法 + URI + HTTP版本Host: mcs.snssdk.com // 请求头:字段名不区分大小写Connection: keep-alive // 字段名不能有空格Content-Length: 1020 // 字段后紧跟:// 空行:分隔请求头和实体Hello World // 实体:空行后面全是实体内容,可以没有实体
Cache-Control: private, max-age=0, no-cache Content-Length: 43 Content-Type: image/gif
Hello World
<a name="ffLgj"></a>###### 2. 请求方法和作用| 请求方法 | 支持HTTP版本 | 作用 || --- | --- | --- || Get | 1.0,1.1 | 获取指定uri上的资源 || Post | 1.0,1.1 | 提交,上传数据 || Head | 1.0,1.1 | 获取响应头信息 || Put | 1.0(非标准支持),1.1 | 修改指定uri上的资源 || Delete | 1.0(非标准支持),1.1 | 删除指定资源上的数据 || Options | 1.1 | 列出可以对资源请求的方法,用于跨域 || Trace | 1.1 | 追踪请求-响应的传输路径 || Connect | 1.1 | 建立连接隧道,用于代理服务器 |<a name="ICXQc"></a>###### 3. 状态码| 状态码 | 功能 | 举例详情 || --- | --- | --- || 1xx | 告知请求的进度和状态 | 100 continue 继续<br />101 Switching Protocols 切换协议 || 2xx | 请求成功的状态码 | 200 OK 请求成功<br />204 No Content 请求成功没有响应体<br />206 Partial Content 分块返回,这知识部分内容 || 3xx | 重定向等,需要进一步操作完成请求 | 301 Moved Permanently 永久重定向<br />302 Found 临时重定向<br />304 Not Modified 未修改 请求命中协议缓存 || 4xx | 请求报文错误 | 400 Bad Request 请求语法错误<br />401 Unauthorized 要求用户身份认证<br />403 Forbidden 权限不够<br />404 Not Found 请求的资源服务器上不存在<br />405 Method Not Allowed 请求的方法不支持 || 5xx | 服务器错误 | 500 Internal Server Error 服务器内部发生错误<br />501 Not Implemented 不支持请求的功能<br />502 Bad Gateway 作为代理服务器向上游请求的服务无效 |参考:[状态码](https://www.runoob.com/http/http-status-codes.html)<br />问题:Get和Post的区别?<br />答:从5个角度回答这个问题1. 从**语义**的角度,Get用于获取资源,Post用于上传资源2. 从**参数**的角度,Get参数放在URL中,URL总长度限制为2048字符,Post参数放在请求体中,更加安全2. 从**编码**的角度:Get只能URL,只允许ASCII类型的字符,Post没有限制2. 从**缓存**的角度:Get默认被浏览器缓存,可以收藏,Post默认不会被缓存2. 从**幂等**的角度:Get是幂等的,Post不是(幂等:执行相同的操作,结果也相同问题:HTTP/1.0和HTTP/1.1的区别<br />答:1. **支持长连接**:支持长连接和请求流水线,在同一个TCP连接上传送多个请求和响应,默认`Connection: keep-alive`1. **缓存处理**:http1.1增加强缓存`Cache-Control`的和协商缓存的`ETag`和`If-None-Match`header支持1. **分块传输/断点续传**:增加状态码206(Partial Content)和header`range`,允许只请求部分资源,提供断点续传支持1. **host头处理**:增加`host`header以支持一个IP上有不同的服务<a name="KblRX"></a>##### 6) HTTPSHTTPS是将HTTP数据包通过SSL/TLS加密后传输,TLS是SSL的升级版,目前主流在用版本是TLS1.3,默认端口是443;使用**非对称加密+对称加密**的方式通信。HTTPS通信在传输层需要7次握手(TCP/IP 3次 <br />+ TLS 4次)<br />HTTPS的**优点**是:更加安全,增加中间人攻击的成本。**缺点**是加密耗费资源,响应时间增加,CA认证收费<br />**TLS四次握手的流程:**<br />**客户端**1. 客户端发送`Client Hello`消息,包含支持的协议版本,加密协议,压缩算法,客户端生成的随机数X**服务端**2. 服务端收到请求,发送`Server Hello`消息,包含选择的协议版本,加密协议,压缩算法,服务端生成的随机数Y;2. 发送`Certificate`消息,即证书链,包含支持的域名,有效日期,发行商信息发送`Server Key Exchange`消息,将公钥Pub,Certificate Signature签名给客户端<br />发送`Server Hello Done`消息,表明消息发送完成<br />**客户端**4. 客户端收到证书,校验证书合法性,如果不合法现实HTTPS警告信息4. 校验通过,发送`Client Key Exchange`消息,客户端随机生成key,使用证书内公钥Pub加密生成预主密钥发送给服务端4. 发送`Change Cipher Spec`消息,通知服务端后面的消息加密发送,会话密钥由**X+Y+预主密码**生成4. 发送`Finished`消息,包含加密后的握手信息**服务端**8. 服务端使用私钥Private得到随机key(会话密钥),发送`Server Key Exchange`消息,通知客户端后面消息使用加密发送8. 发送`Finished`消息,验证客户端的`Finished`消息并完成TLS握手<br />参考:[HTTPS详解](https://juejin.cn/post/6844904127420432391)<br />**证书合法性校验:**<br />数字证书包含以下信息:- 公钥- 持有者的信息- 证书认证机构的信息(CA)- 证书的数字签名(Certificate Signature)- 证书有效期- 其他信息CA签发证书的过程:1. CA机构将持有者的公钥,用途,颁发机构,有效日期打包,并使用在哈希算法生成哈希Hash11. 使用CA私钥生成数字签名Certificate Signature,这一步是CA对证书做了签名1. 将签名包含在文件证书中,形成数字证书证书验证过程:1. 客户端将从服务端收到的数字证书分解成明文Text和签名Sign1. 将明文Text使用同样的哈希算法生成Hash21. 使用操作系统或浏览器中的CA公钥解密签名Sign得到Hash31. 对比Hash2和Hash3,如果相同说明证书通过下图是证书验证的过程<br /><br />参考:[浏览器是如何验证HTTPS证书的合法性?](https://www.zhihu.com/question/37370216/answer/1914075935)<a name="M4fYp"></a>##### 7) HTTP2**特点:**1. 二进制分帧层(Binary Framing Layer)使用二进制传输代替原来的明文传输,一个HTTP消息被分解为一个或多个帧。这样头部也能压缩了。最小的传输单位是帧<br />2. 流,消息和帧(Stream,message and frames)1. 通信只需要一次TCP连接,可以传输任意数量的**双向流**1. 每一个流都有一个唯一ID和可选的优先级参数1. 一个HTTP消息由一个或多个帧组成1. 帧是传输的最小单位,帧可以在不同的流之间传输,通过帧头的标识符组装3. 请求和响应的多路复用(Request and response multiplexing)HTTP2将一个请求/响应消息分解为多个帧放在流中传输,和HTTP1.x一个TCP连接只允许同时传输一个消息不同,HTTP2允许在一个TCP连接中同时传输多个请求/响应的帧。有效的解决了队头阻塞和并行请求限制的问题<br />4. 流的优先级(Stream prioritization)流的优先级由它可选的依赖和权重决定;权重取值1-256。如果一个流中A,B依赖于另一个流D。那全部资源优先处理D,然后处理A,B。如果A权重12,B权重4。那A获得3/4资源处理,B获得1/4资源处理(不是100%保证)。<br />5. 同源请求只需要一次连接(One connection per origin)HTTP2的连接是持久的,因为一个连接允许多个流同时传输,所以同一个源的请求只需要一次TCP连接。减少了TCP,TLS连接次数;也就减少了网络上数据传输量<br />参考:[Introduction to HTTP/2](https://web.dev/performance-http2/https://web.dev/performance-http2/)6. 流控制(Flow control)HTTP2允许客户端和服务端在HTTP层面控制流的传输,避免发送端发送接受端无法处理/不想要的数据7. 服务端推送(Server push)允许服务端推送,如果服务器已经知道客户端需要哪些内容,可以直接提前推送的到客户端,无需客户端先请求再响应。<br />8. 头部压缩(Header compression)HTTP1.x采用明文传输,头部无法压缩。HTTP2使用HPACK压缩头部,包含以下内容:1. 允许头部使用静态哈夫曼编码,减少数据量1. 客户端和服务端都维护和更新之前请求的头部字段索引列表;下一次只需要传输不变的头部的索引值<a name="oF549"></a>#### 4. 服务端返回HTTP报文服务端处理请求并返回HTTP报文<a name="TWsmZ"></a>#### 5. 浏览器渲染页面<a name="Td0GZ"></a>##### 1) 构建DOM树渲染器按照如下的顺序构建DOM树<br /><br />流程如下1. 从网络或磁盘读取HTML原始字节1. 根据指定的编码格式(UTF-8)将字节转化成字符串1. 将字符串转换成token,如`<html>`,`<body>`等,token会标识出**起点标签,终点标签,文本**等信息1. 将token转化为节点对象并构建DOM树,通过起点标签,终点标签等确定节点之间的关系;token到DOM树是同步转化的,即生成的token会立马转化成节点对象Node并添加到DOM树中<br />如果遇到**link标签的css节点**会并行请求并构建CSSOM树;<br />如果**遇到同步脚本会暂停构建DOM树,请求并构建CSSOM树(js可能修改css),然后执行脚本;最后继续构建DOM树**<br /><br /><a name="C3eWq"></a>##### 2) 构建CSSOM树构建CSSOM树和DOM树类似,他们之间并行运行,互不影响,除非遇见阻塞DOM的js脚本<br />这一步确定每个节点样式<br /><br /><a name="ye4Hm"></a>##### 3) 构建渲染树Render Tree将DOM树和CSSOM组合成渲染树,渲染树中**只包含显示的节点**和节点的样式信息,如`display:none`的元素不会在渲染树中<br /><a name="zAVEn"></a>##### 4) 构建布局树(布局/回流)根据渲染树布局计算**元素的位置和大小**,构建布局树<a name="WMuBT"></a>##### 5) 分层阶段,构建图层树将拥有**层叠上下文属性**的节点和**需要被裁剪**的节点提升为渲染图层,构建图层树<br /><a name="Akfx3"></a>##### 6) 绘制阶段/合成与栅格化(重绘)1. 创建绘制列表:图层根据绘制指令按顺序创建绘制列表2. 栅格化和合成:上个的步骤都在渲染器主线程中进行;接下来主线程把绘制列表交给**合成线程**1,图层绘制列表即图层的信息<br />由于各个图层的大小不一,合成线程会把图层划分为图块,交给**栅格化线程池**,会优先栅格化视图附近的图块<br />栅格化线程池调用GPU加速栅格生成位图,**每一个图层对应一个位图**,保存在GPU内存中<br />合成线程将不同图层的位图合成为**合成帧**,交给浏览器主线程显示<br /><a name="XMFrs"></a>##### 1. 问题:引发重排的css属性有哪些?答:<br />改变元素几何信息(位置和大小)的操作都会引发重排;<br />但不是每次更新都引发一次重排,而是**异步更新**,修改的属性会存在队列中,每隔一个时间间隔便一起更新。当时如果用户**查询某些属性**,为确保属性值的精确性会清空队列更新引发重排1. 页面首次渲染1. 浏览器窗口大小发生变化1. 元素大小或位置发生变化1. 元素内容发生变化(文字数量,图片大小)1. 元素内字体大小发生变化1. 添加或删除**可见**的DOM元素1. 激活css伪类1. 查询某些参数(表格黑字部分)| width | height | margin | padding || --- | --- | --- | --- || display | border | position | overflow || clientWidth | clientHeight | clientTop | clientLeft || offsetWidth | offsetHeight | offsetTop | offsetLeft || scrollWidth | scrollHeight | scrollTop | scrollLeft || scrollIntoView() | scrollTo() | getComputedStyle()(存疑,测试发现不会清空队列) | getBoundingClientRect() || scrollIntoViewIfNeeded() | | | |对上面元素的注解,参考:[什么是client,offset,scroll](https://shubo.io/element-size-scrolling/)<br />clientWidth = width + 2 * padding<br />offsetWidth = width + 2 * padding _+ 2 _* border<br /><br />参考:[What forces layout / reflow](https://gist.github.com/paulirish/5d52fb081b3570c81e3a)<a name="AETo6"></a>##### 2. 问题:引发重绘的css属性有哪些?答:<br />元素样式的改变并不影响元素大小和位置的时候引发重绘| color | border-style | visibility | background || --- | --- | --- | --- || background-size | background-image | background-position | background-repeat || outline | outline-style | outline-color | outline-width || outline-radius | box-shadow | text-decoration | |<a name="SwCaq"></a>##### 3. 问题:如何减少重排?css:1. 避免使用table布局1. 尽量在DOM树末端改变CSS,减少重排范围1. 使用translate替代top、left等,translate使用GPU加速,不会引起重排重绘1. 避免使用CSS表达式`calc()`1. 避免设置多层的内联样式,如`div > p > span`1. 将有动画效果应用在`position: absolute`或`position: fixed`的元素上js:1. 分离读写操作分离修改(引起重排的)属性的操作和读取(引起重排的)属性的操作```javascript/** 每次读取offset都会强制刷新重排队列 */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);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);
- 集中改变样式
尽量将样式用一次修改操作完成
// badvar left = 10;var top = 10;el.style.left = left + "px";el.style.top = top + "px";// goodel.className += " theclassname";// goodel.style.cssText += "; left: " + left + "px; top: " + top + "px;";
- 缓存布局信息
通过缓存的方式保存强制刷新队列的属性,避免频繁读取
// 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';
- 离线改变DOM
- 隐藏DOM:先使用
display:none,然后修改元素属性,这里不会影响DOM,最后设置display:block - documentFragment,批量插入DOM时可以使用
- 优化动画
对有复杂动画使用绝对定位,使其脱离文档流;避免父元素和后续元素频繁重排
合理启动硬件加速:对opacity,transform,filter应用动画
4. 问题:如何提升节点为渲染图层?
答:
拥有相同坐标空间的布局对象(LayoutObject)属于同一个渲染层(PaintLayer),满足形成层叠上下文条件的布局对象(LayoutObject)一定会为其创建渲染层
普通布局对象(NormalLayoutObject)
- 根元素(HTML)
- 有明确的定位属性(relative, fixed,sticky)
- 透明度小于1(opacity)
- 有CSS filter属性
- 有CSS mask属性
- 有CSS mix-blend-mode属性(不为normal)
- 有CSS transform属性(不为none)
- 有CSS backface-visibility属性(为hidden)
- 有CSS reflection属性(-webkit-box-reflection)
- 有CSS column-count,column-width属性(不为auto)
- 对当前元素的opacity,transform,filter,backdrop-filter应用动画
Overflow裁切渲染层(OverflowClipPaintLayer)
- overflow不为visible
NoPaintLayer
- 没有视觉元素的空div
其他的布局对象(LayoutObject)与其拥有第一个独立渲染层的父元素共用一个渲染层
5. 问题:如何提升为合成层呢?有哪些好处
答:满足某些特殊条件的渲染层会被提升为合成层;注意渲染层和合成层是不同的
- 3D transform属性:translatez,translate3d
- video,canvas,iframe等元素
- position:fixed元素
- 具有will-change的元素
- 对当前元素的opacity,transform,filter,backdrop-filter应用动画
合理的提升好处如下:
- 元素交由GPU渲染,更快
- 元素重绘只重绘本身
- 对于transform,opacity效果,不会触发重排和重绘
参考:深入浅出渲染器原理
从图层的角度去看关键渲染路径的布局到合成
对布局树进行分层,以及图层的绘制
浏览器渲染之图层绘制与栅格化操作
深入了解现代浏览器之三 - 渲染
浏览器层合成与页面渲染优化
无线性能优化:Composite
6. 断开TCP连接
四. CSS
1. 常见的定位方案
1) 普通流
普通流分为行内元素和块级元素,行内元素排列成一行,每个块级元素单独占一行;他们有不同的性质:
行内元素:
- css属性为
inline,inline-block,inline-table - 设置宽度
width和高度height无效,宽度是内容的宽度,设置行高line-height有效 - 只能容纳文本和其他行内元素
块级元素:
- css属性为
block,list-item,table - 可以容纳其他行内元素和块级元素
外边距坍缩:
嵌套的块级元素父元素与第一个直接块级子元素。最后一个块级上下边距会合并,取较长的那一个
<!-- 上外边距和下外边距都产生了塌陷 --><style>.parent {/* 如果不设置父元素的border,会导致外边距坍缩 *//* border: 1px solid black; */margin: 50px;background-color: aquamarine;}.son-top {margin: 50px;background-color: greenyellow;width: 50px;height: 50px;}.next {background-color: pink;margin-left: 50px;height: 50px;}</style><div class="parent"><div class="son-top">son</div></div><div class="next"></div></body>

-
2) 浮动
浮动可以设置向左浮动或向右浮动
float:right,它有如下特性;
- 浮动脱离文档流,移动到包含块的边缘或另一个浮动元素的边缘按行排列,如果行宽容不下浮动元素则另起一行。
- 后面的块级元素会忽略浮动元素的存在
- 浮动元素虽然不影响其他元素布局,但是会影响其他元素的内容,其他元素的内容会围绕它
- 清除浮动:正常元素使用
clear:both说明元素左右两边都不能有浮动元素
3) 定位技术
指position属性,它有如下值:static:默认值,元素按照普通流排序relative:相对定位,元素仍然占有普通流的位置,可以使用top,left等让元素相对普通流位置偏移absolute:绝对定位,元素脱离文档流,定位相对于最近relative父元素或body,可以用top,left偏移fixed:绝对定位,元素脱离文档流,定位相对于视窗
2. 盒模型
块级元素的content + padding + border + margin构成了盒模型,分为标准盒模型和IE盒模型
标准盒模型:宽高width,height只包含content,CSS设置box-sizing:content-box
IE盒模型:宽高width,height包含content + padding + border,CSS设置box-sizing:border-box
3. BFC 块级格式化上下文
BFC(Block Formatting Contexts)可以看作是迷你的独立容器,容器内部的元素不会再布局上影响到外部元素
创建BFC:
- 根元素
<html> - 绝对定位元素
position为fixed或absolute - 浮动元素
flex - overflow
不为none的元素 - 无副作用的
display:flow-root - 行内块display为
inline-block的元素 - 表格标题和表格单元格
display为table-caption,table-cell - 表格和行内表格
display为table,inline-table - 弹性布局
display:flex的直接子元素 - 网格布局
display:grid的直接子元素
BFC特性:
- BFC的元素包含float元素(因为容器内的元素不影响外部元素布局,即使是脱离文档流的)

- BFC可以解决父子元素的外边距坍缩(还是因为容器内的元素不影响外部元素布局)

- BFC可以阻止环绕float元素(因为float和环绕的元素都是BFC,互相独立)
4. 实现两栏布局(左侧固定,右侧自适应)
<div class="outer"><div class="left"></div><div class="right"></div></div>
1) 浮动:
左侧float元素,宽度width固定,高度height固定全高;右侧设置BFC,固定的margin-left(可以不设置),宽度width自动,高度height固定全高
.outer {height: 500px;}.left {float: left;width: 200px;height: 100%;background: lightcoral;}.right {display: flow-root;margin-left: 100px;height: 100%;background: lightseagreen;}
2) flex布局:
左侧固定宽度,右侧设置flex:1
.outer {display: flex;height: 500px;}.left {float: left;width: 200px;height: 100%;background: lightcoral;}.right {flex: 1;margin-left: 100px;height: 100%;background: lightseagreen;}
3) 绝对定位:
父元素设为relative,左边元素absolute定位,固定宽度width,右边元素的margin-left设为左边的元素宽度
.outer {position: relative;height: 500px;}.left {position: absolute;width: 200px;height: 100%;background: lightcoral;}.right {/** 200+100 */margin-left: 300px;height: 100%;background: lightseagreen;}
4) 绝对定位:
父元素设为relative,左边元素固定宽度width,右边元素absolute定位,left为左侧元素固定宽度,right, top, bottom设为0(让子元素填满没有明确高度和宽度的父元素)
.outer {position: relative;height: 500px;}.left {width: 200px;height: 100%;background: lightcoral;}.right {position: absolute;/** 200+100 */left: 300px;top: 0;right: 0;bottom: 0;height: 100%;background: lightseagreen;}
5. 实现三栏布局(左右固定宽度,中间自适应)
1) 理解margin负值:
圣杯布局和双飞翼布局都用到了margin负值,所以先理解margin负值时元素的状态
- 没有浮动,没有设置定位
position:static
设置margin-top,margin-left本元素向上或向左移动;
设置margin-right,margin-bottom后续元素向左或者向上覆盖本元素。
- 没有浮动,设置相对定位
position:relative
设置margin-top,margin-left本元素向上或向左移动(和1相同);
设置margin-right,margin-bottom本元素向左或者向上覆盖本后续元素(和1相反)。
- 没有浮动,设置相对定位
position:absolute
设置margin-top,margin-left本元素向上或向左移动(和1相同);
设置margin-right,margin-bottom对后续元素没有影响(已经脱离文档流)。
- 设置浮动
浮动方向和负值margin方向一致则元素向相应位置移动
浮动方向和负值margin方向相反则后续、前续元素移动;按元素在HTML位置覆盖
参考:理解并运用 CSS 的负 margin 值
2) float布局实现三栏布局
<style>.outer {display: flex;height: 600px;}.left {width: 100px;background: lightcoral;}.middle {flex-grow: 1;background: lightsteelblue;}.right {width: 100px;background: lightseagreen;}</style><div class="outer"><div class="left"></div><div class="middle"></div><div class="right"></div></div>
2) 圣杯布局
- 容器为BFC,设置padding,padding的宽度为左右元素的固定宽度;
- center,left,right都向左浮动
- center在最前面,且宽度占满屏幕;
- left和right都使用相对定位
- left的margin-left设置-100%,left向左移动左边元素的宽度
right设置margin-right为-右边元素的宽度
<style>.container {display: flow-root;padding: 0 200px 0 100px;height: 600px;}.center {float: left;width: 100%;height: 100%;background: lightblue;}.left {float: left;position: relative;margin-left: -100%;left: -100px;width: 100px;height: 100%;background: lightgreen;}.right {float: left;position: relative;width: 200px;height: 100%;margin-right: -200px;background: lightpink;}</style><div class="container"><div class="center"></div><div class="left"></div><div class="right"></div></div>
参考:圣杯布局
3) 双飞翼布局
容器为BFC
- center,left,right都向左浮动
- center在最前面,宽度依然占满屏幕。这次不通过容器设置padding,而是通过在center内部添加一个div,设置div的margin
- left和right不需要设置相对定位了
- left的margin-left设为-100%
right的margin-left设为负右元素的宽度
<style>.container {display: flow-root;height: 600px;}.center {float: left;width: 100%;height: 100%;}.inner {margin: 0 200px 0 100px;height: 100%;background: lightblue;}.left {float: left;margin-left: -100%;width: 100px;height: 100%;background: lightgreen;}.right {float: left;margin-left: -200px;width: 200px;height: 100%;background: lightpink;}</style><div class="container"><div class="center"><div class="inner"></div></div><div class="left"></div><div class="right"></div></div>
6. 水平垂直居中的方法
1) 绝对定位+translate
对子元素没有要求
<style>.father {position: relative;border: 4px dotted powderblue;width: 400px;height: 400px;}.son {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);background: lightcoral;}</style><div class="father"><div class="son">这是一段文字</div></div>
2) 绝对定位+margin
让子元素占满父元素,然后使用
margin:auto平分可分配空间;需要子元素有明确的宽高<style>.father {position: relative;border: 4px dotted powderblue;width: 400px;height: 400px;}.son {position: absolute;top: 0;right: 0;bottom: 0;left: 0;margin: auto;width: 100px;height: 100px;background: lightcoral;}</style><div class="father"><div class="son"></div></div>
3) 绝对定位+负值margin
将子元素的左上角定位到父元素中心,再利用负值margin向上向右移动子元素一半宽高,需要子元素有明确的宽高
<style>.father {position: relative;border: 4px dotted powderblue;width: 400px;height: 400px;}.son {position: absolute;top: 50%;left: 50%;margin-top: -50px;margin-left: -50px;width: 100px;height: 100px;background: lightcoral;}</style><div class="father"><div class="son"></div></div>
4) flex布局
<style>.father {display: flex;justify-content: center;align-items: center;border: 4px dotted powderblue;width: 400px;height: 400px;}.son {width: 100px;height: 100px;background: lightcoral;}</style><div class="father"><div class="son"></div></div>
7. flex布局
flexbox根元素的属性
display:定义弹性布局
- flex-flow:flex- direction和flex-wrap的缩写
- flex-direction:弹性布局的行,列,正,反方向
- flex-wrap:定义布局是否换行
- justify-content:定义主轴上元素的对齐方式
- align-items:定义交叉轴上元素的对齐方式
- align-content:定义交叉轴剩余空间的对齐方式
- gap:定义元素间空格的大小
- row-gap:定义每一行的宽度
- column-gap:定义每一列的宽度
flexbox子元素的属性
- order:元素的排列顺序,按数字大小排列
- flex:flex-grow,flex-shrink和flex-basis的缩写,默认flex-grow
- flex-grow,定义主轴上剩余空间的分配比例,默认0
- flex-shrink:当主轴上空间不足时定义压缩比例,默认1
- flex-basis:定义分配剩余空间之前元素占用的空间
- align-self:允许某个元素脱离align-items,自己在交叉轴上的对齐方式
8. display布局
太复杂啦,直接参考:css-tricks,grid
9. CSS优先级
css优先级按一定规则运算;!important优先级最高;其余由A,B,C,D的值确定;ABCD
组成数组从前向后比较;值大的优先级高。优先级相同后面覆盖前面。
- A是
内联选择器;如果存在内联选择器,则A=1;否则A=0; - B是
id选择器出现的次数 - C是
属性选择器,类选择器,伪类选择器出现次数的综合 -
10.CSS动画之transition
transition动画是4个属性的集合
transition: <transition-property> <transition-duration> <transition-delay> <transition--timing-function>;第一个时间属性分配给duration,第二个时间属性分配给delay。多个属性同时设定用分号间隔。 transition-property:需要动画变换的属性,all表示所有变动的属性都加上过渡动画transition-duration:间隔时间transition-delay:延迟多少秒开始动画transition-timing-function:动画函数,是一个贝塞斯曲线;有预设值ease-out, ease-in等
transition的属性:
- 有一个事件
transitionend - 需要明确的开始和结束状态属性数值;auto属性可能会失效;如
width:100px到width:auto - 只能定义开始状态和结束两个状态
- transition是一次性的,不能重复发生,除非再次触发
transition需要事件触发,所以没法在加载的时候自动触发
11.CSS动画之animation
animation是多个属性的集合:
animation-name:动画名称,可以用keyframes定义animation-duration:动画持续时间animation-delay:动画延迟时间animation-timing-function:动画的时间函数animation-direction:动画播放顺序:normal,reverse,alternative,alternative-reverse等animation-iteration-count:动画重复次数,默认1;一直动下去设为infiniteanimation-play-state:设置播放running还是暂停paused
示例:
<head><style>.div {....animation-name: move, size;animation-duration: 3s;animation-timing-function: ease-in-out;animation-play-state: paused;animation-iteration-count: infinite;animation-direction: alternate}@keyframes move {0% {left: 0;transform: rotate(0deg);}100% {left: 500px;transform: rotate(720deg);}}@keyframes size {from { font-size: 25px; }to { font-size: 50px; }}</style></head><body><div class="div">😊</div><div><button id="run">run</button><button id="pause">pause</button></div><script>var ele = document.getElementsByClassName('div')[0];document.getElementById('run').addEventListener('click', function() {ele.style.animationPlayState = 'running'});document.getElementById('pause').addEventListener('click', function() {ele.style.animationPlayState = 'paused'});</script></body>
12. css选择器
1) 属性选择器:
[attr]包含这个属性的元素[attr=value]包含这个属性,且值为value的元素[attr~=value] 以空格分隔的多个值且至少一个为value[attr|=value]属性值以value或value-开头[attr^=value] 值以value开头[attr$=value]值以value结尾[attr*=value]值子串包含value
2) 组合选择器
`空格,后代选择器<br />>直接子代选择器<br />~一般兄弟选择器<br />+`紧邻兄弟选择器
3) 伪选择器
:伪类:元素的状态来选择,如a:visited::伪元素:无法用HTML语义表达的元素,如p::first-line``div::before``div::after
13. 媒体查询media
1) @media查询媒体属性
匹配不同的CSS,@media是可嵌套的。分为几个不同的部分:
@mediacss的@规则[media type]可选的媒体类型,可以是screen,print,speech等(media feature)媒体属性,用括号包裹,里面是css规则;常见的(min-width: 100px)``max-``height``width``color-
2) 在HTML CSS JS中的应用
CSS:@media规则,可嵌套,@import可以使用。会执行匹配的那部分css代码
@import url("./common.css") screen;.block {width: 100px;height: 100px;}@media screen and (min-width: 800px) {.block {background: red;}}@media screen and (max-width: 800px) {.block {background: green;}}
HTML:可以在style,source,link等标签使用。匹配的css link会优先加载,但是当前不匹配的css link也会加载 ```html
3. JS:使用`windw.matchMedia`使用,`mediaQuery.addListener(cb)`添加监听。每当有**条件越过(匹配到或匹配离开)**的时候便会触发回调```javascriptconst mediaQuery = window.matchMedia('(min-width: 700px)');function handler(e) {console.log('只要条件越界就会触发');}mediaQuery.addListener(handler);handler(mediaQuery);
五. 移动端布局
viewport适配方案
1) 单位:
- vw:viewport’s width的简写;1vw = 1%的window.innerWidth
- vh:viewport’s height的简写;1vh = 1%的window.innerHeight
- vmax: vw和vh中较小的那一个
-
2) html中viewport设置
viewport:利用meta viewport设置布局视口;content有如下属性 | 属性 | 作用 | | —- | —- | | width | 布局视口的宽度,可以是device-width或一个正整数 | | initial-sacle | 页面初始缩放值 | | minimun-scale | 允许用户缩放的最小级别 | | maximun-scale | 允许用户缩放的最大级别 | | user-scalable | yes或no,是否允许用户缩放 | | viewport-fit | ios11新增;
contain、auto:可视化窗口完全包含内容
cover:内容完全覆盖可视化窗口 |
3) px自动转化为vw
使用postcss的插件postcss-px-to-viewport将px单位转换为vw单位;方便开发和调试
参考:2022 年移动端适配方案指南




