1 HTML5 新增内容
1.1 新增的标签
- 布局标签
<header>
标记定义一个页面或一个区域的头部<nav>
标记定义导航链接<section>
标记定义一个区域<aside>
标记定义页面内容部分的侧边栏<article>
标记定义一篇文章<hgroup>
标记定义文件中一个区块的相关信息<figure>
标记定义一组媒体内容以及它们的标题<figcaption>
标记定义 figure 元素的标题<dialog>
标记定义一个对话框(会话框)类似微信<footer>
标记定义一个页面或一个区域的底部- 对应在页面上显示效果为:
- 表单新增。新增的
input
的类型有:- email:必须输入邮箱地址
- url:必须输入 url 地址
- number:必须输入数值
- range:必须输入一定范围内的数值
- Date Pickers:日期选择器(datetime-local)
- search: 搜索常规的文本域
- color:颜色选择
- 效果:
1.2 新增的 API
1.2.1 DOM 操作
- 获取元素新增
querySelector
及querySelectorAll
,使用起来很像 JQ 的$(xxx)
选择器。:
<body>
<p class="ptext">标签</p>
<p>标签</p>
<script>
const ptext = document.querySelector(".ptext"); // 查一个
const allp = document.querySelectorAll("p"); // 查全部
</script>
</body>
- 类名操作新增
add
,remove
,toggle
,contains
:
Node.classList.add("class"); //添加class
Node.classList.remove("class"); // 移除class
Node.classList.toggle("class"); //切换class,有则移除,无则添加
Node.classList.contains("class"); // 检测是否存在class,有true,无false
1.2.2 历史记录(路由)及存储
HTML5 提供了一组强大的 API 用来保存用户在一个会话期间的网站访问记录,并提供相应的方法进行追溯(也就是模拟浏览器的前进后退)。比较重要的 API 有:
history.pushState
可以改变页面的 URL 而不会刷新页面,
vue-router
及react-router-dom
这种路由管理库的实现就是基于history
API 。 前端路由实现思路:监听页面的 URL 变化,如果有变化,就把变化后 URL 对应的页面渲染出来,且整个过程页面不刷新。
实现一个单页面路由,加深对history.pushState
的理解。具体代码如下:
-
router.html
:<!DOCTYPE html> <html lang="en"> <body> <ul> <ul> <li><a href="/home">首页</a></li> <li><a href="/about">关于我们</a></li> <div id="routeView"></div> </ul> </ul> </body> <script> let routerView = document.querySelector("#routeView"); // 页面加载完毕后立即执行一遍 window.addEventListener("DOMContentLoaded", onLoad);//当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,无需等待样式表、图像和子框架的完全加载。 // 监听url变化后执行 window.addEventListener("popstate", () => {//做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用`history.back()`或者`history.forward()`方法) routerView.innerHTML = viewHTMl(location.pathname); }); function onLoad() { routerView.innerHTML = viewHTMl(location.pathname); var linkList = document.querySelectorAll("a"); linkList.forEach((el) => el.addEventListener("click", function (e) { e.preventDefault(); // 点击时,让浏览器url变更为a链接中href内容 history.pushState(null, "", el.getAttribute("href")); routerView.innerHTML = viewHTMl(location.pathname); }) ); } // 根据url 渲染不同内容 function viewHTMl(url) { switch (url) { case "/home": return `<h1>我是首页<h1>`; break; case "/about": return `<h1>我是关于我们页面<h1>`; break; default: return `<h1>我是首页<h1>`; break; } } </script> </html>
- 效果:
HTML5 新增的缓存 API localStorage
和 sessionStorage
:
1.2.3 文件读取
HTML5 虽然可以让我们访问本地文件系统,但是 js 只能 被动地读取:只有用户主动触发了文件读取行为,js 才能访问到 File Api,这通常发生在表单选择文件或者拖拽文件。
如果支持主动读取,当你随便打开一个网站,你电脑上的文件就被读取走了,甚至是被修改,那将是一件多么可怕的事。
FileReader 接口:将文件读入内存,并提供相应的方法,来读取文件中的数据。
FileReader 提供的方法和事件有:
参数 | 描述 |
---|---|
readAsText(file, [encoding]) | 将文件以文本方式读取,读取的结果即是这个文本文件中的内容。第二个参数是文本的编码方式,默认值为 UTF-8。 |
readAsBinaryString(file) | 将文件以二进制码方式读取。通常我们将它传送到后端,后端可以通过这段字符串存储文件 |
readAsDataURL(file) | 将文件读取为一串 Data URL 字符串(base64编码 ),将小文件以一种特殊格式的 URL 地址直接读入页面。(小文件指图像、 html 等格式的文件) |
abort | 中断读取 |
事件 | 描述 |
---|---|
onabort | 数据读取中断时触发 |
onerror | 数据读取出错时触发 |
onloadstart | 数据读取开始时触发 |
onload/onloadend | 数据读取成功完成时触发/数据读取完成时触发,无论成功失败 |
实现一个图片上传预览功能,来更加深刻的理解 FileReader
异步文件读取机制。
-
file.html
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>文件上传</title> </head> <body> <input type="file" id="files" onchange="fileChange()" /> <img src="" id="onImg" alt="" width="200px" height="200px" /> <script> function fileChange() { var selecFile = document.querySelector("#files").files[0]; var onImg = document.querySelector("#onImg"); var name = selecFile.name; var size = selecFile.size; console.log(name, size); // 获得上传文件的名称和大小 可以根据项目进行判断, 如果上传的图片大小必须小于200kb // 创建 FileReader 对象 var reader = new FileReader(); // 转化格式为base64输出 reader.readAsDataURL(selecFile); // 把上传完毕的图片信息给到img标签进行渲染 reader.onload = function () { onImg.src = this.result; }; } </script> </body> </html>
- 效果:
1.2.4 websocket
我们熟悉的前后端通信技术有 http/https
,局限:
- 只能由客户端发起,服务端响应,响应结束后就断开(短链接)。
websocket
也是一种前后端通信技术, 与http/https
相比: websocket
技术可以让服务端主动发消息给客户端。- 浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
对于前端而言使用 websocket
和使用 Ajax 一样简单。
function WebSocketTest() {
if ("WebSocket" in window) {
alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket 需要后端提供链接地址
var ws = new WebSocket("ws://localhost:9998/echo");
ws.onopen = function () {
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
alert("数据已接收...");
};
ws.onclose = function () {
// 关闭 websocket
alert("连接已关闭...");
};
}
}
总结
Q:HTML5 新增的内容有哪些。
A:HTML5 新增的内容比较多,主要有以下几个方面:
新增的标签有:布局标签 xxxx、画布 xxx、音视频 xxx、新增 form 表单类型有 xxx、同时也废弃了 big
、font
、s
等标签,在项目中使用 HTML5 规范进行开发对浏览器解析 HTML 代码更加友好,有利于网站的 SEO;
新增的 API 有:DOM 操作相关 xxx、历史记录 xxx、存储 xxx、文件读取、websocket 等。在实际应用中我使用 xxx API 实现过 xxx 功能,感觉 xxx 还是很强大的。
2 CSS 基础常见问答
2.1 伪类和伪元素
CSS3 规定:为了区分伪元素,伪元素使用 ::
伪类使用 :
。
2.1.1 伪类
用于已有元素处于某种状态时为其添加对应的样式,这个状态是根据用户行为而动态变化的。主要是用来描述用户的行为,常见的伪类有:
属性 | 描述 |
---|---|
:active | 向被激活的元素添加样式。 |
:focus | 向拥有键盘输入焦点的元素添加样式。 |
:hover | 当鼠标悬浮在元素上方时,向元素添加样式。 |
:link | 向未被访问的链接添加样式。 |
:visited | 向已被访问的链接添加样式。 |
:first-child | 向元素的第一个子元素添加样式。 |
:lang | 向带有指定 lang 属性的元素添加样式。 |
2.1.2 伪元素
用于创建一些不在 DOM 树中的元素,并为其添加样式。主要用于扩展 DOM,常见的伪元素有: | 属性 | 描述 | | ------------- | ---------------- | | :first-letter | 向文本的第一个字母添加特殊样式。 | | :first-line | 向文本的首行添加特殊样式。 | | :before | 在元素之前添加内容。 | | :after | 在元素之后添加内容。 |
使用伪元素实现下三角形:
element.html
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>使用伪元素实现对话框</title> <style type="text/css"> .warp { display: flex; justify-content: space-between; width: 260px; } .say { padding-top: 20px; color: red; } .box { width: 200px; height: 100px; line-height: 100px; text-align: center; border: 1px solid black; position: relative; } .box::before, .box::after { width: 0; height: 0; content: ""; border: solid transparent; position: absolute; left: 100%; } .box::after { border-width: 10px; border-left-color: white; top: 20px; } .box::before { border-width: 12px; border-left-color: black; top: 18px; } </style> </head> <body> <div class="warp"> <div class="box">只要功夫深,铁杵磨成针</div> <div class="say">xxx说</div> </div> </body> </html>
- 效果:
- 虽然是个简单的功能,但如果不用伪元素来实现得话,那就得使用背景图片来实现,使对于项目来说就又多一次资源请求。
2.1.3 总结
Q:了解伪类和伪元素吗?它们有什么不同?
A:伪类是 xxx(定义)伪元素用于(定义)。在项目中使用过伪元素做一些简单的图标展示,从而减少了图片资源的请求,对于项目来说也是一种小的优化点。
2.2 1px 问题
2.2.1 什么是 1px 问题
什么是 1px 问题:UI 设计稿标注一个边框是 1px,若直接在代码上写 border:1px
,结果发现在有的手机中,1px 会比实际效果粗,这就是典型的 1px 问题。
注意:1px 问题只有在部分情况下出现,那什么时候会出现这个 bug 呢?
-
CSS 像素
以 iPhone6/7/8 为例,从 Chrome 开发者工具中可以看到 iPhone6/7/8 是 375*667 这就是 css 像素,又名逻辑像素和设备独立像素。 -
物理像素
实际上,iPhone7 的说明书上写着屏幕是 750*1334,表示屏幕大小,是屏幕的物理像素,也就是说 css 像素 != 物理像素,这就又引出一个概念,设备像素比。 -
设备像素比 DPR
设备像素比 = 物理像素/css像素
pc端:当物理像素等于 css 像素时没有 1px 问题。在 pc 端页面上写 1px 就是对应着屏幕的 1px。
手机端:1px 问题在 设备像素比不等于 1 的时候出现,手机屏幕没有电脑屏幕大,可能会出现设备像素比等于 2 的情况。当 UI 的设计稿中设计的边框是 1px(物理像素)时,需要转化为 css 像素:css像素 = 物理像素/设备像素比 = 1px/2 = 0.5px
0.5px
就这样诞生了,但是呢,我们的手机一般又不支持0.5px
的渲染,会把它渲染成1px
,那这样我们再用公式转化一下物理像素 = css像素 * 设备像素比 = 1px * 2 = 2px
设计稿上原本是 1px(物理像素)到最后呈现出来却变成了 2px(物理像素),也就是我们说的“变粗了”。
2.2.2 如何解决 1 px 问题
- 使用图片,让设计师给你切一个 1px(css 像素)的图片。显然这不是好的解决方式。
- 缩放整个视图(也不推荐)。
- 伪元素 + transform
- 实现原理:把原先元素的 border 去掉,然后利用
::before
或者::after
重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位(推荐)。
- 实现原理:把原先元素的 border 去掉,然后利用
2.2.3 总结:
Q:了解移动端 1px 问题吗?说说为什么会出现 1px 问题及解决方案。
A:在 pc 的浏览器上设置的 1px 的边框,在移动端的浏览器上看上去会“更粗” 一些。
之所以会出现 1px 本质上是因为手机屏幕的物理像素不等于 css 像素,当前端拿到 UI 设计稿(通常是 2 倍图 物理像素)还原页面时,会转化为对应的 css 像素,这时可能出现 0.5px,但是手机端对 0.5px 支持度不是很好会解析成 1px,所以在页面上显示的时候比设计稿粗。解决的方案一般使用伪元素 + transform 。
2.3 盒模型
盒模型分为 W3C 标准盒模型和 IE 盒模型。
2.3.1 W3C 标准盒模型
内容的宽度=设置的宽度(width);
盒子的宽度=内容的的宽度+内边距(padding)*2+外边框(border)*2+外边距(margin)*2;
盒模型结构为:
2.3.2 IE 盒模型
盒子内容宽度=设置的宽度(width)-内边距(padding)*2-外边距(margin)*2-外边框(border)*2;
盒子整体的宽度度=设置的宽度(width);
盒模型结构为:
默认情况是标准盒模型,当设置
box-sizing: border-box
时,div 就会变成 IE 盒模型。
- 标准盒模型:没有把
padding
和border
包含在内。 - IE盒模型:把
padding
和border
包含在内。
2.4 元素水平居中
- 水平居中
- 行内元素: text-align: center
- 块级元素:margin: 0 auto
- 通用:absolute + transform
- 通用:flex + justify-content: center
- 竖直居中
- 行内元素:line-height: height
- 通用:absolute + transform
- 通用:flex + align-items: center
- 通用:table
- 水平垂直居中:
- table-cell + text-align:center codepen.io/airfri/pen/…
- absolute + margin:auto codepen.io/airfri/pen/…
- flex + justify-content: center + align-items: center codepen.io/airfri/pen/…
3 浏览器及 HTTP 常考点
什么是三次握手?什么是跨域?什么是 TCP......
- HTTP 状态码
- 跨域问题的产生与解决方案
- 什么是三次握手
- cookies 、sessionStorage 和 localStorage 的区别
- 从浏览器输入地址到页面渲染都经历了哪些过程
3.1 HTTP 状态码
Q:你知道 HTTP 状态码都有哪些吗?
A:首先状态码分为以下几种类型:2XX 3XX 4XX 5XX
2XX
2XX 类状态码表示:服务器收到并成功处理了客户端的请求
,这也是客户端最愿意看到的状态码,常见以 2 开头的状态码有:
- 200 OK 是最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果;
- 204 含义与“200 OK”基本相同,但响应头后没有 body 数据;
- 206 它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分。是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现。
3XX
3XX 类状态码:客户端请求的资源发生了变动,客户端必须用新的 URI 重新发送请求获取资源
,也就是通常所说的重定向,包括著名的 301、302 跳转,常见以 3 开头的状态码有:
- 301 Moved Permantly 永久重定向,此次请求的资源已经不存在了,需要改用新的 URI 再次访问;
- 302 临时重定向,请求的资源还在,但需要暂时用另一个 URI 来访问;
- 304 它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成 “重定向到已缓存的文件”。
4XX
4XX 类状态码表示客户端发送的请求报文有误,服务器无法处理
,它就是真正的“错误码”含义了,常见以 4 开头的状态码有:
- 400 Bad Request 表示请求报文有错误,但具体是数据格式错误、缺少请求头还 是 URI 超长它没有明确说,只是一个笼统的错误;该请求不能被服务器解读。
- 403 表示服务器禁止访问资源;
- 404 Not Found 资源在本服务器上未找到,所以无法提供给客户端。
5XX
5XX 类状态码表示客户端请求报文正确,但服务器在处理时内部发生了错误,无法返回应有的响应数据
,是服务器端的“错误码”,常见以 5 开头的状态码有:
- 500 服务器内部错误;
- 501 客户端请求的功能还不支持;
- 502 通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误;
- 503 表示服务器当前很忙,暂时无法响应服务。
- 505 HTTP Version Not Supported 服务器不支持这种版本号。
3.2 浏览器跨域
Q:什么是跨域问题?为什么有跨域问题?前端如何解决跨域问题?
3.2.1 为什么存在跨域问题
- 跨域的存在的必要性:为了网络的安全。
如果没有跨域的存在,也就是我们可以任意的在浏览器中请求任何服务器的资源,更有甚者做个恶意的网站通过 iframe 嵌入银行的登录页面,恶意网页上的 javascript 脚本就可以在用户登录银行的时候获取用户名和密码。这样一来网络将不再有安全可言。 - 跨域问题存在的原因:浏览器同源策略的限制。
同源策略是指浏览器不能去请求域名、端口、协议任何一个不相同的服务器资源:
浏览器 服务器 是否跨域 www.aabb.com:8081 www.aabb.com:8080 跨域(端口不同) www.aabbcc.com www.aabb.com 跨域(域名不同) www.aabbcc.com www.aabbcc.com 跨域(协议不同) www.aabbcc.com www.aabbcc.com 不跨域
3.2.2 如何解决跨域问题
在平常开发中,如果就需要从前端服务去请求后端服务的接口,且这两个服务地址还不一样,那如何解决?
-
vue 反向代理解决跨域问题
如果用的是 Vue/React ,在开发环境中可以配置反向代理模式去解决跨域问题,拿 vue 为例:// vue.config.js module.exports = { devServer: { // 配置反向代理 proxy: { "/api": { // 以/api开头的请求 target: "https://xxx.com", // 以/api开头的请求,全部代理到https://xxx.com上 // ws: true, changeOrigin: true, }, }, }, };
反向代理的原理就是把目标服务器的请求代理到自己的服务器中,这样就实现了在自己的服务器中去请求自己的接口资源,当然就不存在跨域问题了。当然在生产环境中我们也可以配置 nginx 的反向代理来解决跨域问题。
-
后端处理,开启白名单
如果服务的响应报文包含了正确 CORS 响应头,那么前后的通讯就不再存在跨域问题,可以理解为在后台服务中开启了白名单。所以本质上来讲,解决跨域问题还是需要后端处理,如下所示:Access-Control-Allow-Origin: *
以上设置表示该资源可以被任意外域访问,如果服务端仅允许来自 xxx.com 的访问,该首部字段的内容如下:
Access-Control-Allow-Origin: http://xxx.com
-
在前端使用 JSONP 方式解决跨域问题
浏览器的同源策略对 JavaScript 脚本,向不同域的服务器请求数据,进行了限制,但是没有对 HTML 中的<script>
标签进行限制,我们可以基于此规则,动态创建<script>
标签进行跨域资源访问。<script>
标签中 src 这一属性值设置为:接口地址+处理数据的回调函数名称。相关代码示例如下:var script = document.createElement("script"); script.type = "text/javascript"; // 设置接口地址+数据获取成功后的回调函数(handleData) script.src = "http://xxx.com/api/list&callback=handleData"; // 通常为后端提供的接口 document.body.appendChild(script); // 回调执行函数 function handleData(res) { data = JSON.stringify(res); console.log(data); }
在这里值得注意的是,因为请求数据的接口地址是写在了
<script>
标签的 src 这一属性值里面,那么数据请求的方式就只能支持 GET 请求,其他请求无法实现。目前常用于解决请求第三方服务,比如解决调用百度地图 API 的跨域问题。
3.2.3 总结
存在跨域是为了保证用户的隐私和数据安全,跨域问题是因为浏览器的同源策略,即如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源否则就不同源(跨域)。跨域本质上是后端解决,当然在开发环境下我们可以配置反向代理来解决跨域,对于那些第三方的接口,可以使用 jsonp 的形式解决跨域。
3.3 三次握手 建立连接
在工作中前后端用的最多的通讯协议就是 TCP/IP,HTTP/HTTPS 是 TCP/IP 协议的上层应用。
三次握手是服务端与客户端建立 TCP 连接的关键。只有先建立了 TCP 连接,才能使用 HTTP/HTTPS 传输数据。
3.3.1 Q:为什么要设计成三次握手呢?两次不行吗?四次不行吗?
A:假设是两次握手后建立 TCP 连接,连接过程应该是这样的:
- 服务器:收到连接请求时,就认为连接建立好了,进入 ESTABLISHED 状态。
- 客户端:收到服务器发来的同步确认报文后,认为连接建立好了,进入 ESTABLISHED。 在报文都能正常收到的情况下,两次握手是可以安全建立连接的。但是存在这样的情况,假设第二次握手的报文丢失了:
- 客户端:没有收到同步确认报文,认为连接还没有建立好,便会向服务器重传同步报文。
- 服务器端:收到了客户端的同步报文,认为连接已经建立好了,服务器给客户端发送的同步确认报文(第二次握手的报文)丢失了,或者是说有人恶意向服务器不断发送 SYN 同步报文(SYN 洪水)。 那么服务器端就会有大量的无效连接,然而服务器处理连接的数量是有限的,大量的无效链接使得服务器处理有效连接的能力受限,且建立连接会消耗大量是资源,至此有可能导致服务器崩溃。
简单来说两次握手中:
- 第一次握手成功:确定发送端(客户端)有发送的能力,接收端(服务端)有接收的能力,且接收端已经确认发送端有发送的能力;
- 第二次握手成功:确定接收端(服务端)有发送的能力,且发送端已经确认接收端有发送及接收的能力。 但是这时 服务端并没有确认客户端有没有接收的能力。这就是为什么需要第三次握手的原因。
通过三次握手成功后,可以确认发送端及接收端同时具有发送及接受的能力,这个时候也标志着 TCP 连接建立成功。
四次握手当然可以,但是明明可以三次握手就可以确认的事情,就不用再浪费一次资源了。
3.3.2 Q:一个 TCP 连接上能发起多少个 HTTP 请求?
A:回答这个问题需要区分 HTTP 的版本。
HTTP1.0
一个服务器在发送完一个 HTTP 响应后,会断开 TCP 连接,这就意味着一个 TCP 连接默认情况下只能发送一个 HTTP 请求。
缺点:每次请求都会重新建立和断开 TCP 连接,代价过大。
HTTP1/1
在 HTTP1/1 默认开启 TCP 持久连接,浏览器和服务器之间是会维持一段时间的 TCP 连接,不会一个请求结束就断掉,除非请求中写明 Connection: close
。也就说理论上只要 TCP 不断开连接,一个 TCP 连接可以发送无数个 HTTP 请求。
缺点:HTTP/1.1 存在一个问题,单个 TCP 连接在同一时刻只能处理一个请求。
HTTP2.0
在 HTTP2 中由于 Multiplexing(多路复用) 特点的存在,多个 HTTP 请求可以在同一个 TCP 连接中并行进行(理论无上限,但是一般浏览器会有 tcp 并发数的限制,这个根据浏览器而异)。
3.4 四次挥手 连接断开
TCP 使用完毕后就要断开连接, 由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。
当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭:
- 客户端 A 发送一个 FIN,用来关闭客户 A 到服务器 B 的数据传送(报文段 4)。
- 服务器 B 收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加 1(报文段 5)。和 SYN 一样,一个 FIN 将占用一个序号。
- 服务器 B 关闭与客户端 A 的连接,发送一个 FIN 给客户端 A(报文段 6)。
- 客户端 A 发回 ACK 报文确认,并将确认序号设置为收到序号加 1(报文段 7)。
这是因为服务端的 LISTEN 状态下的 SOCKET 当收到 SYN 报文的连接请求后,它可以把 ACK 和 SYN(ACK 起应答作用,而 SYN 起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的 FIN 报文通知时,它仅仅表示对方没有数据发送给你了。
3.5 cookies 、sessionStorage 和 localStorage 的区别
cookies 、sessionStorage 和 localStorage 是前端三大缓存技术,每种缓存技术有着自己独特的使用场景,所以弄清楚他们之间的异同就显得十分重要。
3.5.1 cookie
HTTP 是无状态的协议,服务器不维护客户端的状态。好处:简单,同样的服务器资源可以支持更多数量的客户端。缺点:。
什么是cookie
cookie 是浏览器储存储在用户电脑上的一小段文本文件,cookie 是纯文本格式,不包含任何可执行的代码,通常用作保留用户的登录状态。
cookie组成部分
-
在HTTP响应报文中有一个cookie的首部行
-
在HTTP请求报文含有一个cookie的首部行
-
在用户端系统中保留有一个cookie文件,由用户的浏览器管理
-
在Web站点有一个后端数据库 cookie 工作流程:
-
浏览器发送请求给服务器;
-
服务器设置 cookie 的值;(最初的HTTP请求到达服务器时,该Web站点产生一个唯一的ID,并以此作为索引在它的后端数据库中产生一个项)
-
浏览器中保存 cookie 的值。
cookie 的优点:
- 数据持久性;
- 不需要任何服务器资源,因为 cookie 是存储在客户端并发送给服务器读取;
- 可配置到期规则,控制 cookie 的生命周期,使之不会永远有效,偷盗者可能拿到的是一个过期的 cookie;
- 简单性,基于文件的轻量结构。
cookie能带来什么?:用户验证、购物车、推荐、用户状态 (Web e-mail)。
cookie 的缺点
- 由于 cookie 的实现机制,一旦服务器向客户端发送了设置 cookie 的意图,除非 cookie 过期,否则客户端每次请求都会发送这些 cookie 到服务器端,一旦设置的 cookie 过多,将会导致报头较大。大多数的 cookie 并不需要每次都用上,因为这会造成带宽的部分浪费;
- cookie 只能保存 4K 左右的数据量,且大多数浏览器只允许每个站点保存 20 个 Cookie。
- cookie与隐私:
- 暴露出用户访问网站的行为。Cookies允许站点知道许多关于用户的信息,可能将它知道的东西卖给第三方。使用重定向和cookie的搜索引擎还能知道用户更多的信息,如通过某个用户在大量站点上的行为,了解其个人浏览方式的大致模式。广告公司从站点获得信息。
上图是百度接口缓存的 cookie 信息
3.5.2 sessionStorage
由于 cookie 在使用的时候需要后端去设置要缓存的值,在使用的过程中增加前后端沟通成本,且储存的文件大小过小(一般为 4kb)。
sessionStorage 完美的解决了 cookie 的缺点。
sessionStorage 的优点
- 可以大量保存浏览器中数据,存储大小一般为 5M;
- 不会随 http 请求一起发送;
- 不同窗口下数据存储相互独立,互不干扰。
sessionStorage 的缺点
- 不能存储复杂数据类型(复杂数据类型需要使用 JSON.stringify 进行处理);
- 关闭网页后缓存信息失效,是一种短储存。
sessionStorage 的简单使用
sessionStorage.name = "Tina"; // 设置sessionStorage
sessionStorage.name; // 读取sessionStorage
sessionStorage.clear(); // 清除sessionStorage
以上是 sessionStorage 在浏览器中的储存位置
3.5.3 localStorage
localStorage 和 sessionStorage 很类似,都是浏览器本地缓存,存储大小一般为 5M。
和 sessionStorage 不同的是: localStorage 将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。
localStorage 的简单使用
localStorage.name = "Tina"; // 设置localStorage
localStorage.name; // 读取localStorage
localStorage.clear(); // localStorage
以上是 localStorage 在浏览器中的储存位置
3.5.4 总结
特点 | cookie | sessionStorage | localStorage |
---|---|---|---|
数据生命周期 | 可以指定 maxAge 的值规定 cookie 失效时间,默认关闭浏览器失效 | 关闭浏览器/关闭网站失效 | 除非数据被清理,否则一直存在 |
存放数据大小 | 4K 左右 | 5M 或者更大 | 5M 或者更大 |
与服务器通信 | 每次都会携带在 HTTP 头中 | 不参与和服务器通信 | 不参与和服务器通信 |
易用性 | 需要自己封装 setCookie,getCookie | 可以直接使用原生接口 | 可以直接使用原生接口 |
3.6 从浏览器输入地址到页面渲染都经历了哪些过程
从浏览器输入地址到页面渲染经过了很多的过程,且每个过程都可以深挖出很多知识点
- 构建请求
// 请求方法是GET,路径为根路径,HTTP协议版本为1.1
GET / HTTP / 1.1;
3.6.1 查找强缓存
我们常说的缓存,根据资源存放位置、具体用途和运行机制不同,一般可以分为:
- 数据库缓存
- 服务器缓存
- 客户端缓存 客户端缓存一般指的是浏览器缓存。 浏览器会先检查是否存在缓存,如果存在缓存就直接从缓存里面拿数据,给到浏览器进行渲染。
3.6.2 DNS 解析
输入的是域名,而数据包是通过 IP 地址传给对方的,因此需要得到域名对应的 IP 地址。
这个过程需要依赖一个服务系统——DNS(域名系统),这个系统将域名和 IP 一一映射,得到具体 IP 的过程就是 DNS 解析。
注:浏览器提供了 DNS 数据缓存功能:如果一个域名已经解析过,那会把解析的结果缓存下来,下次处理直接走缓存,不需要经过 DNS 解析
3.6.3 建立 TCP 连接
建立 TCP 连接经历了下面三个阶段:
- 建立连接。通过三次握手(即总共发送 3 个数据包确认已经建立连接)建立客户端和服务器之间的连接。
- 数据传输。
- 这里有一个重要的机制:接收方接收到数据包后必须要向发送方确认, 如果发送方没有接到这个确认,就判定为数据包丢失,并重新发送该数据包。(数据包校验保证数据到达接收方)
- 发送的过程中的一个优化策略:把大的数据包拆成一个个小包,依次传输到接收方,接收方按照小包的顺序把它们组装成完整数据包。
- 断开连接。数据传输完成,通过四次挥手来断开连接保证数据传输的可靠性。
3.6.4. 发送 HTTP 请求
现在 TCP 连接建立完毕,浏览器可以和服务器开始通信,即开始发送 HTTP 请求。浏览器发 HTTP 请求要携带三样东西:请求行、请求头和请求体。
3.6.5. 网络响应
HTTP 请求到达服务器,服务器进行对应的处理。最后要把数据传给浏览器,也就是返回网络响应。
响应头包含了服务器及其返回数据的一些信息:服务器生成数据的时间
返回的数据类型
即将写入的 Cookie 信息
。
如果请求头或响应头中包含 Connection: Keep-Alive
,表示建立了持久连接,这样 TCP 连接会一直保持,之后请求统一站点的资源会复用这个连接。
完成以上过程后,数据已经达到浏览器端,接下来就是浏览器解析并渲染数据了。
3.6.6. 解析过程
(1) 构建 DOM 树
浏览器无法直接直接理解 HTML 字符串,浏览器可以将这一系列的字节流转换为一种有意义并且方便操作的数据结构——DOM 树(本质上是一个以 document 为根节点的多叉树)。
(2) 样式计算
浏览器无法直接识别 CSS 样式文本,渲染引擎接收到 CSS 文本后首先将其转化为一个结构化的对象——styleSheets。( 这个格式化过程过于复杂,对于不同的浏览器会有不同的优化策略) 。在浏览器控制台能够通过 document.styleSheets 来查看这个最终的结构。当然,这个结构包含了以上三种 CSS 来源,为后面的样式操作提供了基础。
(3) 生成布局树
现在已经生成了 DOM 树和 DOM 样式,接下来通过浏览器的布局系统确定元素的位置,也就是要生成一棵布局树(Layout Tree):
- 遍历生成的 DOM 树节点,并把他们添加到布局树中;
- 计算布局树节点的坐标位置。
注: 区分布局树和DOM树,布局树只包含可见元素,对于 head 标签和设置了 display: none 的元素,将不会被放入其中。
3.6.7. 渲染
(1) 构建 DOM 树
浏览器将 HTML 解析成树形结构的 DOM 树。
一般来说,这个过程发生在页面初次加载,或 JavaScript 修改了节点结构的时候。
(2)构建渲染树
浏览器将 CSS 解析成树形结构的 CSSOM 树,再和 DOM 树合并成渲染树。
(3)布局(Layout)
浏览器根据渲染树所体现的节点、各个节点的 CSS 定义以及它们的从属关系,计算出每个节点在屏幕中的位置。
回流:Web 页面中元素的布局是相对的,在页面元素位置、大小发生变化,往往会导致其他节点联动,需要重新计算布局,这时候的布局过程一般被称为回流(Reflow)。
(4)绘制(Paint)
遍历渲染树,调用渲染器的 paint() 方法在屏幕上绘制出节点内容,本质上是一个像素填充的过程。这个过程也出现于回流或一些不影响布局的 CSS 修改引起的屏幕局部重画,这时候它被称为重绘(Repaint)。实际上,绘制过程是在多个层上完成的,这些层我们称为 渲染层(RenderLayer)
(5)渲染层合成(Composite)
多个绘制后的渲染层按照恰当的重叠顺序进行合并,而后生成位图,最终通过显卡展示到屏幕上。
什么是渲染层合成?
在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当DOM节点的渲染对象处于相同的坐标空间(z 轴空间)时,就会形成一个渲染层(RenderLayers)。
渲染层将保证页面元素以正确的顺序堆叠,渲染层合成(composite)能正确处理透明元素和重叠元素的显示。 类似于 Photoshop 的图层模型:每个设计元素都是一个独立的图层,多个图层以恰当的顺序在 z 轴空间上叠加,最终构成一个完整的设计图。
对于有位置重叠的元素的页面,渲染层合成过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。
(6)显示器显示内容
栅格化(渲染层合成)完成后,合成线程会生成一个绘制命令("DrawQuad"),并发送给浏览器进程。 浏览器进程中的 viz 组件接收到这个命令,根据这个命令把页面内容绘制到内存,也就是生成了页面,然后把这部分内存发送给显卡,从而展示在屏幕上。
总结
从浏览器的渲染过程中我们知道,页面 HTML 会被解析成 DOM 树,每个 HTML 元素对应了树结构上的一个 node 节点。而从 DOM 树转化到一个个的渲染层,并最终执行合并、绘制的过程,中间其实还存在一些过渡的数据结构,它们记录了 DOM 树到屏幕图形的转化原理,其本质也就是树结构到层结构的演化。