一. 如何理解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 | 低(由服务端决定) |
第一次请求服务器返回ETag
header,第二次请求自动带上if-None-Match
,命中了协商缓存
第一次请求服务器返回Last-Modified
header,第二次请求自动带上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) HTTPS
HTTPS是将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握手
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1646743360626-59925bfe-eae4-4ca3-9dd8-257d59073ed6.png#clientId=u632c1995-f932-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=414&id=u4bb4fc49&margin=%5Bobject%20Object%5D&name=image.png&originHeight=910&originWidth=1304&originalType=binary&ratio=1&rotation=0&showTitle=false&size=350929&status=done&style=none&taskId=u7109fda5-d383-4eef-8ae2-8bbb52494a4&title=&width=592.7272598802554)<br />参考:[HTTPS详解](https://juejin.cn/post/6844904127420432391)<br />**证书合法性校验:**<br />数字证书包含以下信息:
- 公钥
- 持有者的信息
- 证书认证机构的信息(CA)
- 证书的数字签名(Certificate Signature)
- 证书有效期
- 其他信息
CA签发证书的过程:
1. CA机构将持有者的公钥,用途,颁发机构,有效日期打包,并使用在哈希算法生成哈希Hash1
1. 使用CA私钥生成数字签名Certificate Signature,这一步是CA对证书做了签名
1. 将签名包含在文件证书中,形成数字证书
证书验证过程:
1. 客户端将从服务端收到的数字证书分解成明文Text和签名Sign
1. 将明文Text使用同样的哈希算法生成Hash2
1. 使用操作系统或浏览器中的CA公钥解密签名Sign得到Hash3
1. 对比Hash2和Hash3,如果相同说明证书通过
下图是证书验证的过程<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1646743276137-5d599e35-ad12-4a41-9670-d064e0d6b077.png#clientId=u632c1995-f932-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=272&id=u7c8279d8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=598&originWidth=1080&originalType=binary&ratio=1&rotation=0&showTitle=false&size=308049&status=done&style=none&taskId=u68e747a0-2851-45cf-bcf8-89558ec44f7&title=&width=490.90908026892316)<br />参考:[浏览器是如何验证HTTPS证书的合法性?](https://www.zhihu.com/question/37370216/answer/1914075935)
<a name="M4fYp"></a>
##### 7) HTTP2
**特点:**
1. 二进制分帧层(Binary Framing Layer)
使用二进制传输代替原来的明文传输,一个HTTP消息被分解为一个或多个帧。这样头部也能压缩了。最小的传输单位是帧<br />![](https://cdn.nlark.com/yuque/0/2022/svg/23168078/1650272079433-030b3154-4ed2-4226-9e48-e02d4d9d84f4.svg#clientId=u9d8a8796-a2db-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u1ea6f910&margin=%5Bobject%20Object%5D&originHeight=150&originWidth=291&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u7f228c39-6abc-4da6-86e5-0668e1075da&title=)
2. 流,消息和帧(Stream,message and frames)
1. 通信只需要一次TCP连接,可以传输任意数量的**双向流**
1. 每一个流都有一个唯一ID和可选的优先级参数
1. 一个HTTP消息由一个或多个帧组成
1. 帧是传输的最小单位,帧可以在不同的流之间传输,通过帧头的标识符组装
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1650293883478-bca8f449-37c1-452e-ac43-3f426a872fab.png#clientId=ua0c34180-32e3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=381&id=u6d1cfdb1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1248&originWidth=1516&originalType=binary&ratio=1&rotation=0&showTitle=false&size=143388&status=done&style=none&taskId=u9c042c1b-0df3-4df1-9711-186ab9b3144&title=&width=463.09088134765625)
3. 请求和响应的多路复用(Request and response multiplexing)
HTTP2将一个请求/响应消息分解为多个帧放在流中传输,和HTTP1.x一个TCP连接只允许同时传输一个消息不同,HTTP2允许在一个TCP连接中同时传输多个请求/响应的帧。有效的解决了队头阻塞和并行请求限制的问题<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1650296320803-f129a4d3-56f8-4876-a053-195504c51766.png#clientId=ua0c34180-32e3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=204&id=u61832e81&margin=%5Bobject%20Object%5D&name=image.png&originHeight=448&originWidth=1500&originalType=binary&ratio=1&rotation=0&showTitle=false&size=76254&status=done&style=none&taskId=u077b270c-0a79-41fe-ac38-012e23eef11&title=&width=681.818167040171)
4. 流的优先级(Stream prioritization)
流的优先级由它可选的依赖和权重决定;权重取值1-256。如果一个流中A,B依赖于另一个流D。那全部资源优先处理D,然后处理A,B。如果A权重12,B权重4。那A获得3/4资源处理,B获得1/4资源处理(不是100%保证)。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1650296548361-3c01db61-f55b-4cb1-a65d-1899a7dda7ed.png#clientId=ua0c34180-32e3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=298&id=u2aad8156&margin=%5Bobject%20Object%5D&name=image.png&originHeight=656&originWidth=1514&originalType=binary&ratio=1&rotation=0&showTitle=false&size=67387&status=done&style=none&taskId=ubef90cd7-279f-460e-b6d1-8a7a613abea&title=&width=688.1818032658794)
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 />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1650298512488-6c24b360-2159-4541-bd20-6cc2f4247eca.png#clientId=ua0c34180-32e3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=278&id=u0edc6537&margin=%5Bobject%20Object%5D&name=image.png&originHeight=612&originWidth=1504&originalType=binary&ratio=1&rotation=0&showTitle=false&size=109808&status=done&style=none&taskId=u36bd0384-b7a6-47a6-b126-bc8225d516e&title=&width=683.6363488189448)
8. 头部压缩(Header compression)
HTTP1.x采用明文传输,头部无法压缩。HTTP2使用HPACK压缩头部,包含以下内容:
1. 允许头部使用静态哈夫曼编码,减少数据量
1. 客户端和服务端都维护和更新之前请求的头部字段索引列表;下一次只需要传输不变的头部的索引值
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1650299119390-3a9df807-1c5b-47a1-ab1c-d62e6dd11e8c.png#clientId=ua0c34180-32e3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=581&id=u5a406b70&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1278&originWidth=1520&originalType=binary&ratio=1&rotation=0&showTitle=false&size=196001&status=done&style=none&taskId=uab629e4a-6ba1-49ca-8675-438de2778af&title=&width=690.90907593404)
<a name="oF549"></a>
#### 4. 服务端返回HTTP报文
服务端处理请求并返回HTTP报文
<a name="TWsmZ"></a>
#### 5. 浏览器渲染页面
<a name="Td0GZ"></a>
##### 1) 构建DOM树
渲染器按照如下的顺序构建DOM树<br />![](https://cdn.nlark.com/yuque/0/2022/jpeg/23168078/1646835001042-d7f53446-03ec-4e0c-a052-7930a66232a1.jpeg)<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 />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1646842706603-af3a1a7f-0179-46fa-a904-77f9ec769ab3.png#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=384&id=u29ee5321&margin=%5Bobject%20Object%5D&name=image.png&originHeight=629&originWidth=1093&originalType=url&ratio=1&rotation=0&showTitle=false&size=110805&status=done&style=none&taskId=ud62cc640-08ea-4400-add7-4aed4ca4fae&title=&width=668)<br />![](https://cdn.nlark.com/yuque/0/2022/webp/23168078/1646837499411-763df984-21e8-4918-a679-488124bb1705.webp#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=364&id=u638d90a9&margin=%5Bobject%20Object%5D&originHeight=622&originWidth=1123&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u7b3939c6-68a8-420e-91cd-e930b3c56bb&title=&width=658)
<a name="C3eWq"></a>
##### 2) 构建CSSOM树
构建CSSOM树和DOM树类似,他们之间并行运行,互不影响,除非遇见阻塞DOM的js脚本<br />这一步确定每个节点样式<br />![](https://cdn.nlark.com/yuque/0/2022/jpeg/23168078/1646835929511-4efc5e99-b647-4997-9717-f8e25fd05e39.jpeg)<br />![](https://cdn.nlark.com/yuque/0/2022/webp/23168078/1646837483672-311de61e-fc32-4da2-900a-6c92558f3f97.webp#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=261&id=u811b936b&margin=%5Bobject%20Object%5D&originHeight=299&originWidth=582&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u4a54188c-5d60-4ee7-867c-cd30485be65&title=&width=508)
<a name="ye4Hm"></a>
##### 3) 构建渲染树Render Tree
将DOM树和CSSOM组合成渲染树,渲染树中**只包含显示的节点**和节点的样式信息,如`display:none`的元素不会在渲染树中<br />![](https://cdn.nlark.com/yuque/0/2022/webp/23168078/1646837468036-8a9e1834-a713-4092-bc13-502701f05439.webp#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=281&id=u61e92c5d&margin=%5Bobject%20Object%5D&originHeight=537&originWidth=1150&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u569c1ce9-3d29-40eb-9e6f-59f6fcd24c0&title=&width=601)
<a name="zAVEn"></a>
##### 4) 构建布局树(布局/回流)
根据渲染树布局计算**元素的位置和大小**,构建布局树
<a name="WMuBT"></a>
##### 5) 分层阶段,构建图层树
将拥有**层叠上下文属性**的节点和**需要被裁剪**的节点提升为渲染图层,构建图层树<br />![](https://cdn.nlark.com/yuque/0/2022/webp/23168078/1646840761266-79a844b7-fe09-4b2c-b6e6-6052a431f613.webp#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=292&id=u950c70b4&margin=%5Bobject%20Object%5D&originHeight=674&originWidth=1142&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u43ff2ce6-ac1b-4b10-9f5f-7b2640543e5&title=&width=494)
<a name="Akfx3"></a>
##### 6) 绘制阶段/合成与栅格化(重绘)
1. 创建绘制列表:图层根据绘制指令按顺序创建绘制列表
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23168078/1646840281501-2c1bf93f-aace-44f8-90d6-7e79c35ca703.png#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=345&id=u3569638a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=880&originWidth=895&originalType=binary&ratio=1&rotation=0&showTitle=false&size=154927&status=done&style=none&taskId=u20c34c80-bc7a-4460-bd8c-a65cfad4853&title=&width=351)
2. 栅格化和合成:
上个的步骤都在渲染器主线程中进行;接下来主线程把绘制列表交给**合成线程**1,图层绘制列表即图层的信息<br />由于各个图层的大小不一,合成线程会把图层划分为图块,交给**栅格化线程池**,会优先栅格化视图附近的图块<br />栅格化线程池调用GPU加速栅格生成位图,**每一个图层对应一个位图**,保存在GPU内存中<br />合成线程将不同图层的位图合成为**合成帧**,交给浏览器主线程显示<br />![](https://cdn.nlark.com/yuque/0/2022/webp/23168078/1646842461389-315b49de-0591-4d7d-b2b7-f917b5b61032.webp#clientId=u1e312de1-56a7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=364&id=u5037e044&margin=%5Bobject%20Object%5D&originHeight=857&originWidth=1142&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u09fa7b6b-be9b-42fa-9f66-e0323ca1ab7&title=&width=485)
<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 />![](https://cdn.nlark.com/yuque/0/2022/png/23168078/1646901141490-58861824-b4b8-4046-bb2e-e8d267eb588f.png#clientId=u6ef1036c-aa34-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=368&id=u4a2d0241&margin=%5Bobject%20Object%5D&originHeight=567&originWidth=630&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u9451b54d-e024-42b3-b200-5f54e781e8e&title=&width=409)<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);
- 集中改变样式
尽量将样式用一次修改操作完成
// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// good
el.className += " theclassname";
// good
el.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是可嵌套的。分为几个不同的部分:
@media
css的@规则[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)`添加监听。每当有**条件越过(匹配到或匹配离开)**的时候便会触发回调
```javascript
const 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 年移动端适配方案指南