BFC
盒模型
CSS 盒模型描述了通过文档树中的元素以及相应的视觉格式化模型(visual formatting model)所生成的矩形盒子
基础盒模型(CSS basic box model)
- 当浏览器对一个 render tree 进行渲染时,浏览器的渲染引擎就会根据基础盒模型(CSS basic box model),将所有元素划分为一个个矩形的盒子,这些盒子的外观,属性由 css 来决定。
视觉格式化模型
CSS 的视觉格式化模型时根据基础盒模型将文档中的元素转换一个个盒子的实际算法
官方解释:它规定了用户端在媒介中如何处理文档树
- 每个盒子的布局由以下因素决定
- 盒子的尺寸
- 盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)、块级盒子(block-level)
- 定位:正常流、浮动、绝对定位
- 文档树中当前盒子的子元素或者兄弟元素
- 视口(viewport)的尺寸和位置
- 盒子内部图片的尺寸
- 其他某些外部因素
- 视觉格式化模型的计算,都取决于一个矩形的边界,这个矩形,被称为包含块。一般来说,(元素)生成的框会扮演子孙元素包含块的角色;
- 每个盒子的布局由以下因素决定
盒子的生成
盒子的生成是 CSS 视觉格式化模型的一部分,用于从文档元素生成盒子,盒子的类型取决于 CSS display 属性
格式化上下文是定义盒子环境的规则,不同格式化上下文下的盒子有不同的表现。
- 盒子相关的概念定义
- 块级元素
- 当元素的
display为block、list-item或table时,她就是块级元素
- 当元素的
- 块级盒子
- 块级和用于描述它父、兄弟元素之间的关系。
- 每个块级盒子都会参与块格式化上下文的创建
- 每个块级元素都会至少生成一个块级盒子,即主块级盒子
- 主块级盒子包含由后代元素生成的盒子以及内容,同时它也会参与定位方案
- 一个同时是块容器盒子的块级盒子称为块盒子
- 匿名盒子
- 某些情况下需要进行视觉格式化时,需要添加一些增补性的盒子,这些盒子不能被 CSS 选择器选中,也就是所有可继承的 CSS 属性都为
inherit,而所有不可继承的 CSS 属性值都为initial。因此称为匿名盒子
- 某些情况下需要进行视觉格式化时,需要添加一些增补性的盒子,这些盒子不能被 CSS 选择器选中,也就是所有可继承的 CSS 属性都为
- 行内元素
- 当元素的 display 为 inline、inline-block 或 inline-table 时、它就是行内级元素。
- 显示时可以与其他行内级内容一起显示为多行
- 行内盒子
- 行内级元素会生成行内级盒子,该盒子同时会参与行内格式化上下文(inline formatting context)的创建
- 匿名行内盒子
- 类似于块盒子,CSS 引擎有时候也会自动创建一些行内盒子。这些行内盒子无法被选择符选中,因此是匿名的,它们从父级元素那里继承那些可继承的属性,其他属性保持默认值 intial
- 行盒子
- 行盒子由行内格式化上下文创建,用来显示一行文本。在块盒子内部,行盒子总是从块盒子的一边延伸到另一边即占据整个块盒子的宽度。当有浮动元素时,块盒子会向左浮动的元素的右边缘延伸到向右浮动的元素的左边缘
- 块级元素
- 盒子相关的概念定义
BFC
BFC 这个概念来自于视觉格式化模型中的正常流
定义
浮动元素、绝对定位元素,‘display’特性为‘inline-block’,‘table-cell’,‘table-caption’的元素,以及‘overflow’不是‘visible’的元素,会创建新的 BFC
是这些元素创建了块格式化上下文,它们本身不是快格式化上下文
触发 BFC 布局:
- float 的值不为 none
- overflow 的值不为 visible
- display 的值为 inline-block、table-cell、table-caption
- position 的值为 absolute 或 fixed
margin 重叠问题
- 将父级添加
overflow:hidden,变成 BFC 模块
- 将父级添加
JavaScript 基础
从输入 URL 到页面展示,这中间发生了什么?
简单描述
DNS 解析
发起 TCP 连接
发送 HTTP 请求
服务器处理请求并返回 HTTP 报文
浏览器解析渲染页面
连接结束
DNS 解析
DNS 解析实际上就是寻找所需资源的过程。输入了一个网址,这个网址并不是真实地址,是通过 DNS 解析的 IP 地址,原因是 IP 地址过于复杂难以记忆
具体解析
DNS 解析其实是一个递归的过程
- 输入网址后,首先在本地的域名服务器中查找,没找到去根域名服务器查找,没有继续向顶级域名服务器查找,以此类推,直到找到 IP 地址,然后记录在本地,供下次使用。
DNS 优化
DNS 缓存
DNS 存在着多级缓存:浏览器缓存、系统缓存、路由缓存、IPS 服务器缓存、根域名服务器缓存、顶级域名服务器缓存、主域名服务器缓存
- 在 chrome 输入
chrome://net-internals/#dns,可以查看 DNS 缓存 - 系统缓存主要存在
/etc/hosts(Linux)中
- 在 chrome 输入
DNS 负载均衡
- 一般访问一个网址时,每次响应都不是同一个服务器,一般大公司都是服务器集群支撑访问。DNS 可以返回一个合适的机器 IP 分配给用户,例如可以根据每台机器的负载量、与用户的距离等,这就是 DNS 负载均衡
发起 TCP 连接
TCP 提供一种可靠的传输,这个过程设计三次握手,四次挥手
TCP 首部数据字段
- 源端口:源端口和 IP 地址的作用是识别报文的返回地址。
- 目的端口:端口指明接收方计算机上的应用程序接口。
- 序号:是 TCP 可靠传输的关键部分。是该报文发送的数据组的第一个字节的序号。
- 确认号:即 ACK,下一个期待收到的序号,表示序号之前的所有数据已收到。
- 首部长度/数据偏移: 占 4 位,指出 TCP 报文的数据距离 TCP 报文的起始距离有多远。
- 保留:占 6 位,保留今后使用,但目前都应为 0
- 控制位:URG ACK PSH RST SYN FIN,每一个标志表示一个控制功能
- URG(紧急):URG=1 时,表明指针字段有效。告诉报文段中有紧急数据
- ACK(确认):ACK=1 时,确认字段才有效
- PSH(推送):两个进程交互通信时,一端键入一个命令后立即能收到对方的回应,将 PSH=1
- RST(复位):RST=1 时,表示 TCP 出现严重错误,必须释放连接,然后在重新建立连接
- SYN(同步):在连接建立时用来同步序号,当 SYN=1,ACK=0,表明是连接请求报文,若同意,则 SYN=1,ACK=1
- FIN(终止):表明报文发送方数据发送完毕,并要求释放
- 窗口:滑动窗口大小,告知发送端接收端的缓存大小,以此控制发送端数据速率,以 16 位进行计算。窗口大小时一个 16bit 字段,因而窗口大小最大为 65535
- 校验和:奇偶校验,对整个 TCP 报文(包括 TCP 头部和 TCP 数据)以 16 位计算所得,由发送端计算和存储,接收端验证
- 紧急指针:只有 URG=1 时才有效。是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。
- 选项和填充:最常见的可选字段是最长报文大小,又称为 MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段中指明这个选项,它表示本端所能接受的最大报文长度。
- 数据部分:TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换报文段仅有 TCP 首部。
三次握手
握手的过程中传送的包里不包含数据
- 第一次握手
- 客户端发送 syn 包(Seq=x)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
- 第二次握手
- 服务器收到 syn 包,必须确认客户的 SYN(ack=x+1),同时自己也发送一个 SYN 包(Seq=y),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;
- 第三次握手
- 客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=y+1),此包发送完毕,客户端与服务器进入 ESTABLISHED 状态,完成三次握手。
- 第一次握手
四次挥手
- 第一次挥手
- 客户端发送一个 FIN,用来关闭客户端到服务器的数据传送,告诉服务器不会再发送数据了,但是此时客户端还可以接收数据。客户端进入终止等待 1(FIN-WAIT1)状态。
- 第二次挥手
- 服务器收到 FIN 包后,发送一个 ACK 给对方并带上自己的序列号 seq,确认序号为收到序号+1,此时服务器进入关闭等待状态(CLOSE-WAIT)。TCP 服务器通知高层的应用进程,客户端向服务器方向释放了,这时候等于半关闭状态,即客户端已经没有数据发送,但服务器发送数据,客户端依然要接受。状态还会维持一段时间。
- 第三次挥手
- 服务器发送一个 FIN,用来关闭服务器到客户端的数据传送,也是告诉客户端数据发送完了,不会再发数据了。此时服务器进入最后确认(LAST-ACK)状态,等待客户端确认。
- 第四次挥手
- 主动关闭方收到 FIN 后,发送一个 ACK 给被关闭方,确认序号为收到序号+1,此时客户端进入等待时间(TIME-WAIT)状态。
- 第一次挥手
发送 HTTP 请求
HTTP 端口为 80/8080,HTTPS 端口为 443
发送 HTTP 请求的过程就是构建 HTTP 请求报文并通过 TCP 协议发送到服务器指定端口,请求报文由请求行,请求报头,请求正文组成
- 请求行
- 格式为
Method Request-URL HTTP-Version CRLFeg: GET index.html HTTP/1.1常用的方法有:GET,POST,PUT,DELETE,OPTIONS,HEAD。
- 格式为
- 请求报头
- 请求报头允许客户端向服务器传递请求的附加信息和客户端自身的信息。请求报头中使用了 Accept, Accept-Encoding, Accept-Language, Cache-Control, Connection, Cookie 等字段。
- 请求正文
- 当使用 POST, PUT 等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中。例如: 现在的 Web 应用通常采用 Rest 架构,请求的数据格式一般为 json。这时就需要设置
Content-Type: application/json。
- 当使用 POST, PUT 等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中。例如: 现在的 Web 应用通常采用 Rest 架构,请求的数据格式一般为 json。这时就需要设置
- 请求行
HTTP 缓存
HTTP 属于客户端缓存,我们常认为浏览器有一个缓存数据库,用来保存一些静态文件,包含以下几点
缓存的规则:
- 强制缓存:当缓存数据库中有客户端需要的数据,客户端直接将数据从其中拿出来使用(如果数据未失效),当缓存服务器没有需要的数据时,客户端才会向服务端请求。
- 协商缓存:又称对比缓存。客户端会先从缓存数据库拿到一个缓存的标识,然后向服务端验证标识是否失效,如果没有失效服务端会返回 304,这样客户端可以直接去缓存数据库拿出数据,如果失效,服务端会返回新的数据
缓存的方案:
- 强制缓存
- Expires :Exprires 的值为服务端返回的数据到期时间。
- Cache-Control :Cache-Control 有很多属性,不同的属性代表的意义也不同。
- private:客户端可以缓存
- public:客户端和代理服务器都可以缓存
- max-age=t:缓存内容将在 t 秒后失效
- no-cache:需要使用协商缓存来验证缓存数据
- no-store:所有内容都不会缓存。
- 协商缓存
- Last-Modified:服务器在响应请求时,会告诉浏览器资源的最后修改时间。
- ETag:服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识(生成规则由服务器决定)
- 强制缓存
缓存的优点:
- 减少了冗余的数据传递,节省宽带流量
- 减少了服务器的负担,大大提高了网站性能
- 加快了客户端加载网页的速度 这也正是 HTTP 缓存属于客户端缓存的原因。
不同刷新的请求执行过程:
CSS 基础
回流
- 当 Render Tree 中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
- 会导致回流的操作:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的 DOM 元素
- 激活 CSS 伪类(例如::hover)
- 查询某些属性或调用某些方法
- 一些常用且会导致回流的属性和方法:
clientWidth、clientHeight、clientTop、clientLeftoffsetWidth、offsetHeight、offsetTop、offsetLeftscrollWidth、scrollHeight、scrollTop、scrollLeftscrollIntoView()、scrollIntoViewIfNeeded()getComputedStyle()getBoundingClientRect()scrollTo()
重绘
- 当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
优化
CSS
- 避免使用 table 布局。
- 尽可能在 DOM 树的最末端改变 class。
- 避免设置多层内联样式。
- 将动画效果应用到 position 属性为 absolute 或 fixed 的元素上。
- 避免使用 CSS 表达式(例如:calc())。
JavaScript
- 避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
- 避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
- 也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
JS 的解析
JS 的解析是由浏览器的 JS 引擎完成的。由于 JavaScript 是单线程运行,也就是说一个时间只能干一件事,干这件事情时其他事情都有排队,但是有些人物比较耗时(例如 IO 操作),所以将任务分为同步任务和异步任务,所有的同步任务放在主线程上执行,形成执行栈,而异步任务等待,当执行栈被清空时才去看看异步任务有没有东西要搞,有再提取到主线程执行,这样往复循环(冤冤相报何时了,阿弥陀佛),就形成了 Event Loop 事件循环
JavaScript 是一门单线程语言,尽管 H5 中提出了
Web-Worker,能够模拟实现多线程,但本质上还是单线程,说它是多线程就是扯淡。
事件循环
JS 同步和异步任务在进程里面会分别进入不同场所,同步的会进入主进程,异步任务会进入事件列表里面并注册函数,当指定的任务完成,事件列表会将这个函数移入事件队列。主线程内的任务执行完毕为空,回去事件队列读取对应函数,进入主进程执行。此过程不断重复,就是常说的事件循环(Event Loop)
除了同步和异步任务,还分为宏任务和微任务
JS 引擎开始工作后,先在宏任务中开始第一次循环,当主线程执行栈全部任务都被清空。会去微任务查找,如果有等待执行的任务,就执行全部的微任务,然后再去宏任务找进入队列的任务执行,执行这个任务后再去主线程执行,执行栈清空再去微任务,循环往复。
微任务会全部执行,而宏任务会一个一个来执行
- macro-task(宏任务):包括整体代码
script、setTimeout、setInterval - micro-task(微任务):
Promise,process.nextTick不同任务会进入不同的任务队列来执行。
- macro-task(宏任务):包括整体代码