# HTML
# HTML文档类型
- 怪异模型(默认)→ 浏览器按照自己的方式解析
- 标准模式 → 按照W3C标准
通过声明文档的解析类型 <!DOCTYPE html> 可避免浏览器的怪异模式
# Meta
meta提供页面一些元信息,有利于SEO。
属性值:
- name:用于描述网站
- http-equiv:没有name属性时,会采用这个属性的值关联到http请求头。(可取值为content-type、expires、refresh、set-cookie、content)
- content:名称/值中的值,可以是任意有效的字符串。
- scheme:指定翻译属性的方案。
# name
- keywords(关键字) 告诉搜索引擎,你网页的关键字
- description(网站内容描述) 用于告诉搜索引擎,你网站的主要内容。
- viewport(移动端的窗口)
- robots(定义搜索引擎爬虫的索引方式) robots用来告诉爬虫哪些页面需要索引,哪些页面不需要索引
- author(作者)
- generator(网页制作软件)
- copyright(版权)
# http-equiv
没有name属性时,会采用这个属性的值关联到http请求头。(可取值为content-type、expires、refresh、set-cookie、content)
- cache-control(请求和响应遵循的缓存机制)
- expires(网页到期时间)
- X-UA-Compatible(浏览器采用哪种版本来渲染页面)
# content
名称/值中的值,可以是任意有效的字符串。
# scheme
指定翻译属性的方案。
# HTML语义化
- 可以使页面更加结构化,有利于浏览器解析。
- 即使在没有css样式的情况下,页面也会以文档结构显示,更加易于阅读。
- 有利于SEO。因为搜索引擎爬虫是根据HTML标签来搜索的
- HTML语义化便于阅读、维护和理解。
# 浏览器内核
- -webkit-——webkit内核(Chrome)
- -moz-——Gecko内核(Firefox)
- -o-——Opera内核(Opera)
- -ms-——Trident内核(IE)
# 从输入URL到看到页面
# 检查缓存
浏览器查找当前URL是否存在缓存,并比较缓存是否过期。如果有缓存并且没有过期,便直接读取缓存。
检查缓存过程:
- 如果是https的话,有可能先找Service Worker。
- 如果没有,再找浏览器的内存缓存(Memory Cache)。
- 如果还没有,再找硬盘缓存(Disk Cache) (强缓存和协商缓存都属于硬盘缓存)。
- 如果这三种都没有找到,请求还是http2的话,还可能会查找推送缓存(Push Cache),就是找Session(Session会话结束就会释放,所以存在时间很短)。
# URL解析
把我们请求需要的协议、域名、端口、路径这些信息解析提取出来。
# DNS解析
需要DNS解析的原因:
浏览器并不能直接通过域名找到对应的服务器,而是要通过 IP 地址。
# 递归查询
主机向本地域名服务器查询的过程。
递归顺序:
- 浏览器缓存
- 系统缓存(hosts文件)
- 路由器缓存
- IPS(运营商)服务器缓存
# 迭代查询
本地域名服务器向根域名服务器查询的过程
迭代顺序:
- 根域名服务器
- 顶级域名服务器
- 主域名服务器
# 建立TCP连接
# 三次握手
# 发送HTTP请求
# 服务器响应请求
# 浏览器渲染页面
# 构建DOM树
Bytes → characters → tokens → nodes → DOM

# 解析CSS,生成CSS规则树
Bytes → characters → tokens → nodes → CSSOM
# 合并DOM树和CSS规则树,生成render树
# 布局render树,计算各个元素的尺寸、位置等
通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸。
# 合成图层
调用GPU绘制,合成图层,显示在屏幕上。
# 关闭TCP连接
# 四次挥手
# 缓存
# 强制缓存
# Expires(http1.0)
Exprires的值为服务端返回的数据到期时间。当再次请求时的请求时间小于返回的此时间,则直接使用缓存数据。但由于服务端时间和客户端时间可能有误差,这也将导致缓存命中的误差,另一方面,Expires是HTTP1.0的产物,故现在大多数使用Cache-Control替代。
# Cache-Control(http1.1)
private:客户端可以缓存,中间的代理服务器不能缓存。
public:客户端和代理服务器都可以缓存
max-age=t:缓存内容将在t秒后失效
no-store:所有内容都不会缓存。
no-cache:需要使用协商缓存来验证缓存数据
# 协商缓存
进入协商缓存有两种情况:
- 强缓存失效了
- 强缓存Cache-Control字段的值为no-cache
# Last-Modified
服务器在响应请求时,会告诉浏览器资源的最后修改时间。
if-Modified-Since:
浏览器再次请求服务器的时候,请求头会包含的字段,后面跟着在缓存中获得的最后修改时间。然后,与服务器上面资源文件最后的修改时间作比较。如果文件被修改了服务器返回请求的资源文件,响应码为200;如果没有修改就只返回请求头,响应代码为304。
# Etag
服务器响应请求时,生成资源的唯一标识。只要里面的内容有改动,这个值就会变。服务器通过响应头把这个值给浏览器。
If-None-Match:
再次请求服务器时,浏览器的请求报文头部包含的字段,它的值为在缓存中获取的标识。服务器与被请求资源的唯一标识进行对比,如果不一样就响应整个资源内容,返回状态码200。如果相同,就返回响应header,状态码为304。
# 两者对比
- 在精度上,Etag比Last-Modified高。因为ETag是根据内容给资源加上标识的,可以准确感知资源的变化。而Last-Modified是根据修改时间来感知的,有两种情况是不准确的:
- 编辑了资源文件,但是文件内容没有更改。
- Last-Modified能够感知的单位时间是秒,如果在一秒内改变了很多次,Last-Modified就体现不出修改了。
- 在性能上,Last-Modified比Etag高。因为Last-Modified仅仅只是记录一个时间点,而Etag则需要根据具体内容生成哈希值。
# 缓存的位置
# Service Worker
Service Worker是浏览器里面一个独立的线程,可以用来实现缓存功能。使用 Service Worker 的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 借鉴了 Web Worker的 思路,即让 JS 运行在主线程之外。
# Memory Cache
内存缓存,从效率上讲它是最快的。但是从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了。
# Disk Cache
存储在磁盘中的缓存,从存取效率上讲是比内存缓存慢的,但是他的优势在于存储容量和存储时长。
# Push Cache
推送缓存。它是HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂。
# 缓存策略
- 缓存优先,先查询缓存,若存在,直接返回,不存在,请求服务,更新缓存。
- 服务端优先,不查询缓存,直接请求服务端,服务端失败才会查询缓存。
- 稳定优先,先查询缓存,有就读取,同时请求服务端更新资源。
# H5的新特征
- canvas
- audio/video
- localStorage/sessionStorage
- 新的表单控件
- webworker、websocket
# script加载方式
# defer延迟加载
等到整个页面都渲染结束(DOM结构完全生成,以及其他脚本执行完)才会执行,多个defer会按脚本出现的顺序依次加载。
# async异步加载
一旦加载完,渲染引擎就会停止加载,然后执行脚本,脚本执行完才继续渲染(阻塞渲染),多个async不能保证加载顺序。
# link中rel属性的preload和prefetch
- preload预加载当前页面需要用到的资源。
- prefetch加载可能需要用到的资源,浏览器并不一定会加载这些资源。
# iframe
一种内联框架,也是一种很常见的网页嵌入方式。
# 优点
- 能够原封不动的把嵌入的网页展示出来
- 如果有多个网页引用iframe,只需要修改iframe的内容
# 缺点
- 会产生很多页面,不容易管理,同时造成onload阻塞
- 代码结构变得复杂,影响搜索引擎
- 会增加服务器的http请求
# 安全
# XSS攻击
XSS(Cross Site Scripting),跨站脚本。XSS 攻击是指浏览器中执行恶意脚本,从而拿到用户的信息并进行操作。
# 危害
- 窃取Cookie
- 监听用户行为,比如监听用户输入的行为,从而窃取用户密码
- 修改DOM伪造登录表单
- 在页面中生成浮窗广告
# 攻击方式
# 存储型
将脚本存储到了服务端的数据库,然后在客户端执行这些脚本,从而达到攻击的效果。
# 反射型
通过作为网络请求的参数,经过服务器,然后再反射到HTML文档中,执行解析。
# 文档型
在数据传输过程劫持到网络数据包,然后修改里面的 html 文档!
劫持方式
- WiFi路由器劫持
- 本地恶意软件
# 防范措施
# 校验输入
不要相信用户的输入,在前端和服务端对用户的输入进行过滤和校验。
# 利用CSP
CSP,即浏览器中的内容安全策略,它的核心思想就是服务器决定浏览器加载哪些资源。
- 限制其他域下的资源加载。
- 禁止向其它域提交数据。
- 提供上报机制,能帮助我们及时发现 XSS 攻击。
CSP启动方式:
通过 HTTP 头信息的Content-Security-Policy的字段
通过meta标签
# HttpOnly
很多 XSS 攻击脚本都是用来窃取Cookie, 而设置 Cookie 的 HttpOnly 属性后,JavaScript 便无法读取 Cookie 的值。
# CSRF攻击
CSRF(Cross-site request forgery), 即跨站请求伪造。是指诱导用户点击链接,打开恶意的网站,然后攻击者利用用户目前的登录状态发起跨站请求。
防范措施:
- 利用Cookie的SameSite字段。这个字段有三个值,分别是Strict、Lax和None。
- strict模式下,浏览器完全禁止第三方请求携带Cookie。
- lax模式下,只有在get方法下第三方可以携带Cookie。
- none模式下,请求会自动携带Cookie。
- CSRF Token。浏览器向服务器发送请求时,服务器生成一个字符串,将其植入到返回的页面中。然后浏览器如果要发送请求,就必须带上这个字符串,然后服务器来验证是否合法,如果不合法则不予响应。
# 跨域
产生原因:浏览器存在同源策略。
跨域原理:通过各种方法避开浏览器的安全限制。
同源策略:浏览器对JS实施的安全限制,只有协议、域名、端口有任何一个不同,都被当做不同的域。
当浏览器向目标 URI 发 Ajax 请求时,只要当前 URL 和目标 URL 不同源,则产生跨域。
# JSONP
在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的。
//原生的实现方式
let script = document.createElement('script');
script.src = 'http://domain.com/login?username=fendy&callback=callback';
document.body.appendChild(script);
function callback(res) {
console.log(res);
}
# CORS
"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。(服务端设置响应头字段,Access-Control-Allow-Origin)它的思想是:使用自定义的 HTTP 头部 让浏览器与服务器进行沟通。
# postMessage
利用h5新特性window.postMessage()
# 开发环境代理
# Charles花瓶
# TCP与UDP
TCP是一个面向连接的、可靠的、基于字节流的传输层协议。
UDP是一个面向无连接的传输层协议。
面向连接:所谓的连接,指的是客户端和服务器的连接,在双方互相通信之前,TCP 需要三次握手建立连接。
可靠性:体现在有状态、可控制。
- 有状态:TCP 会精准记录哪些数据发送了,哪些数据被对方接收了,哪些没有被接收到,而且保证数据包按序到达,不允许半点差错。
- 可控制:当意识到丢包了或者网络环境不佳,TCP 会根据具体情况调整自己的行为,控制自己的发送速度或者重发。
面向字节流:UDP的数据传输是基于数据报的,而 TCP 为了维护状态,将一个个 IP 包变成了字节流。
区别:
TCP面向连接,UDP面向无连接的
TCP仅支持单播,UDP支持单播,多播,广播。
TCP需要三次握手建立连接确保可靠性,而UDP是无连接、不可靠的数据传输协议
UDP的头部开销比TCP的更小,数据传输速率更高,实时性更好。
三次握手:

注:seq是序号;ack是确认号,大小均为4字节;syn: 同步序列编号。
- 客户端发送SYN报文到服务端
- 服务器收到请求后向客户端发送自己的SYN报文和ACK报文
- 客户端收到服务器的确认应答后,向服务器发送确认包ACK(ack=k+1)
不是两次握手的原因:
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生的错误以及资源浪费。
(会带来连接资源的浪费。如果是两次,现在发了 SYN 报文想握手,但是这个包滞留在了当前的网络中迟迟没有到达,TCP 以为这是丢了包,于是重传。)
四次挥手:
- 客户端要断开,向服务器发送FIN报文请求断开连接。此时无法向服务端发送报文,只能接收。
- 服务端接收后,向客户端发送ACK确认已经收到报文了。
- 服务端向客户端发送FIN和ACK询问是否确定断开连接。
- 客户端收到服务端发来的FIN后,发送ACK给服务器确认关闭连接。

四次挥手的原因:
因为服务端在接收到FIN, 往往不会立即返回FIN, 必须等到服务端所有的报文都发送完毕了,才能发FIN。因此先发一个ACK表示已经收到客户端的FIN,延迟一段时间才发FIN。这就造成了四次挥手。
如果是三次挥手:
等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN。
# HTTP
# 优缺点
优点:
- 灵活可扩展。传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。
- 可靠传输。HTTP 基于 TCP/IP,因此把这一特性继承了下来。
- 无状态。仅仅只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了 http 的优点。
- 不支持服务器推送消息。
缺点:
- 明文传输。协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。
- 队头阻塞。当 http 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于阻塞状态。
- 无状态。在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,那么这时候无状态就是 http 的缺点了。
# 请求方法
- GET: 通常用来获取资源
- POST: 提交数据,即上传数据
- HEAD: 获取资源的元信息
- PUT: 修改数据
- DELETE: 删除资源
- CONNECT: 建立连接隧道,用于代理服务器
- OPTIONS: 在跨域请求前发起预检请求,以检测请求是否被服务器接受。
- TRACE: 追踪请求-响应的传输路径
GET和POST区别:
- Get会把请求的内容主动缓存下来,POST则不会。
- Get只能进行URL编码,而post没有限制。
- Get的参数放在URL中,POST则不会。Post更适合传输敏感信息。
- Get请求的URL有长度限制,而post一般没有。
# 状态码
1xx
101:在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101
2xx
200:请求成功
3xx
301:永久重定向
302:临时重定向
304:当协商缓存命中时会返回这个状态码
4xx
401:未授权
403:服务器禁止访问
404:资源未找到
405:请求方法不被服务器端允许
408:服务器等待了太长时间
5xx
500:服务器出错
502:代理服务器网关不可用
503:表示服务器当前很忙,暂时无法响应服务
# HTTP2.0
新特性:
二进制传输。
头部压缩。HTTP/2 针对头部字段采用了对应的压缩算法——HPACK,对请求头进行压缩。
多路复用。把报文全部换成二进制格式,二进制分帧之后,HTTP2不再依赖TCP链接去实现多流并行了。
- 同域名下所有通信都在单个连接上完成。
- 单个连接可以承载任意数量的双向数据流。
- 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送。
服务器推送。当 TCP 连接建立之后,比如浏览器请求一个HTML文件,服务器就可以在返回HTML的基础上,将HTML中引用到的其他资源文件一起返回给客户端,减少客户端的等待。
# HTTP3.0
基于QUIC协议。该协议基于UDP,又取了TCP 中的精华,实现了即快又可靠的协议。
速度快。基于UDP,而UDP是“无连接”的,根本就不需要“握手”和“挥手”,所以就比TCP来得快。
可靠传输。实现了类似TCP的流量控制——QUIC在UDP的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些TCP中存在的特性。
集成了TLS加密功能。QUIC使用的是TLS1.3,减少了握手所花费的RTT个数。
# HTTPS
HTTPS是在HTTP和TCP的传输中建立的一个安全层,利用对称加密和非对称加密结合数字证书认证的方式,让传输过程的安全性大大提高。
数字证书作用:
- 服务器向浏览器证明自己的身份。
- 把公钥传给浏览器。
传输过程图解:

https的工作原理:
客户端使用https访问服务器时,会携带client_random随机数、加密方法列表、session_id(如果存在)。
web服务器接收到客户端的请求之后,会将网站的数字证书(包含公钥)、server_random随机数、加密方法传输给客户端。
客户端验证数字证书,产生pre_random,用公钥加密之后发送给服务端。
服务端使用私钥解密pre_random
两端通过client_random、server_random、pre_random生成secret,用于对称加密的秘钥。
# WebSocket
websocket是浏览器与服务器进行全双工通讯的应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。
优点:
- 支持双向通信,实时性更强。
- 更好的二进制支持。
- 较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。
- 支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。
缺点:
- 当连接终止时,WebSockets 无法自动恢复连接
- 早于 2011 年的浏览器无法支持 WebSocket 连接
短轮询:
为了定时获取并刷新页面上的数据,客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
长轮询:
长轮询解决了普通轮询资源浪费,频繁的轮询问题。长轮询只需启动一个HTTP请求,其连接的服务器会保持住此次连接,直到有新消息才返回响应信息并关闭连接。
# 进程与线程
- 进程是系统进行资源分配和调度的基本单位,线程是程序执行的最小单位。
- 线程是基于进程运行的。一个进程可以有多个线程,但是一个线程至少有一个进程。
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间。
# sessionStorage、localStorage和cookie
共同点:都保存在浏览器端、且同源
区别:
- cookie数据始终在同源的http请求中携带(即使不需要),而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。
- cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下 ,而sessionStorage和localStorage没有。
- 存储大小限制不同,cookie不能超过4K,sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大 。
- 数据有效期不同:
- sessionStorage:仅在当前浏览器窗口关闭之前有效。
- localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据。
- cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭。
- 作用域不同:
- sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面。
- localstorage在所有同源窗口中都是共享的。
- cookie也是在所有同源窗口中都是共享的。
# 判断元素是否到达可视区域
内容(eg: imgs)达到显示区域的:imgs.offsetTop < window.innerHeight + document.body.scrollTop
- window.innerHeight是浏览器可视区的高度。
- document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动过的距离。
- imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离)。
# PWA
运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。(渐进式网页应用)
# 核心API
# APP Shell
App Shell 就类似于原生app,没网络也可以本地启动。这个模型包含界面所需要的最小资源文件,如果离线缓存,页面一样可渲染,网络仅仅获取数据。
# ServiceWork
- 一个独立的 worker 线程,有自己独立的 worker context。
- 一旦被 install,就永远存在,除非被手动 unregister。
- 用到的时候可以直接唤醒,不用的时候自动睡眠
- 离线内容开发者可控。
- 能向客户端推送消息。
- 不能直接操作 DOM。
- HTTPS 环境下才能工作。
# 常见的区别
# href & src
- href:是引用,用于在当前文档和引用资源之间确立联系
- src:下载资源并替换当前内容
# 性能优化
# 资源优化
- 压缩CSS、JS、图片等静态资源
- 小图标使用iconfont代替
- 使用webp图片格式
# 加载优化
- 使用缓存
- 合并HTTP请求
- 图片懒加载 (对于图片过多的页面先不用全部图片都加载,等到滚动到可视区域后再去加载。)
- 服务端开启gzip压缩
- js写在body标签后面
- 拆包按需加载
- 骨架屏
- 预加载
# 代码优化
- 减少回流与重绘
- 防抖与节流
- 开启GPU加速
- 删除无用的函数或者css
懒加载的原理
- 将页面中的img标签src指向一张默认小图片,然后定义data-src(这个属性可以自定义命名,我才用data-src)属性指向真实的图片。
- 当载入页面时,先把可视区域内的img标签的data-src属性值负给src,然后监听滚动事件,把用户即将看到的图片加载。这样便实现了懒加载。
懒加载的实现
let num = document.getElementsByTagName('img').length;
let img = document.getElementsByTagName("img");
let n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
lazyLoad(); //页面载入完毕加载可是区域内的图片
window.onscroll = lazyLoad;
function lazyLoad() { //监听页面滚动事件
let seeHeight = document.documentElement.clientHeight; //可见区域高度
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
for (let i = n; i < num; i++) {
if (img[i].offsetTop < seeHeight + scrollTop) {
if (img[i].getAttribute("src") === "default.jpg") {
img[i].src = img[i].getAttribute("data-src");
}
n = i + 1;
}
}
}