其他收录
link 和 src 的区别
- 请求资源类型不同
- href 是 Hypertext Reference 的缩写,表示超文本引用。用来建立当前元素和文档之间的链接。常用的有:link、a。
- 在请求 src 资源时会将其指向的资源下载并应用到文档中,常用的有 script,img 、iframe;
- 作用结果不同
- href 用于在当前文档和引用资源之间确立联系;
- src 用于替换当前内容;
- 浏览器解析方式不同
- 若在文档中添加 href ,浏览器会识别该文档为 CSS 文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用 link 方式加载 CSS,而不是使用 @import 方式。
- 当浏览器解析到 src ,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等也如此,类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。
网页访问过程
DNS 解析
:查询本地缓存 => 查询 DNS 服务器 => 返回域名对应的 IP 地址建立 TCP 连接
:- A 开门? => B 可以(暂无顾客状态直接开门) => A 已开门
- A 关门? => B 可以(通知潜在顾客离店关门) => B 已关门(暂无顾客状态完成开门) => A 已关门
发送 HTTP 请求
:一旦 TCP 连接建立,浏览器会构建一个 HTTP 请求,请求所需资源服务器处理请求
:进行数据库查询、文件读取、运行服务器端的脚本等操作服务器返回响应
:服务器处理完请求后,会生成一个 HTTP 响应(状态码-头部信息-内容)下载网页内容
:浏览器接收并下载 HTML 和关联内容 (解析、构建、下载同时进行)浏览器渲染
:构建 DOM 树,构建 CSSOM 树,构建渲染树、样式计算和布局、绘制、合成帧并绘制到屏幕几个过程标记化
:渲染引擎将 HTML 代码分解为一个个的标记(如元素、属性、文本等),构建 DOM 树
:DOM 树由各个 HTML 标记组成,形成了网页的层次结构。样式计算
:一旦 DOM 树构建完成,渲染引擎会根据 CSS 样式表对每个 DOM 节点进行样式计算。它会确定每个元素的最终样式,包括颜色、大小、位置等。样式计算过程可能涉及继承、层叠和计算单位等。布局排版
:布局(也称为排版或重排)是确定每个 DOM 节点在屏幕上的位置和大小的过程。渲染引擎会根据 DOM 树和样式计算的结果,计算每个元素的几何属性(如位置、大小、边距等),并构建一个称为"布局树"(也称为渲染树或盒模型树)的内部表示结构。绘制
:绘制是将布局树转换为屏幕上的像素的过程。渲染引擎会遍历布局树,根据每个元素的几何属性和样式,将其绘制为屏幕上的可见内容。绘制过程通常包括填充颜色、绘制文本、绘制边框等。偏移计算生成布局(Layout)
:遍历布局树计算(增量布局、脏区域检测等)每个元素在屏幕上的位置和大小.绘制(Painting)
:布局完成,浏览器会将布局树转换为屏幕上的像素。这个过程被称为绘制或渲染。栅格化(Rasterization)
:绘制完成,浏览器会将绘制的矢量图形或者一些效果转换为屏幕上的像素图像。合成(Compositing)
:最后,浏览器会将栅格化的图像按照正确的顺序合成到屏幕上。
执行 JS 脚本
:当遇到<script>
标记或外部 JS 文件时,它会执行 JS 脚本 (解析-创建执行环境-执行(事件驱动和异步操作))。执行 JS 脚本的过程通常是单线程的,以避免脚本之间的竞争条件。DOMContentLoaded
是一个 DOM 事件,它在 HTML 文档加载解析完成并构建了 DOM 树之后触发load
事件在整个页面及其关联资源(如图像、样式、脚本)都加载完成后触发
同源策略
http://www.example.com:80/path/file.html?k1=v1&k2=v2#anchor
协议://域名:端口/路径?请求参数#瞄点
如果两个 URL 的
协议
、端口
(如果有指定的话)和主机
都相同的话,则这两个 URL 是同源的。
同源策略允许的内容和屏蔽的内容(通常,允许嵌入跨源资源,而禁止读取跨源资源。)
类型 | 规则 |
---|---|
iframe | 通常允许跨源嵌入(具体取决于 X-Frame-Options 指令),但不允许跨源读取(例如,使用 JavaScript 访问 iframe 中的文档)。 |
CSS | 跨源 CSS 可在 CSS 文件中使用 <link> 元素或 @import 来嵌入。可能需要正确的 Content-Type 标头。 |
表单 | 跨源网址可用作表单元素的 action 属性值。Web 应用可以将表单数据写入跨源目的地。 |
图片 | 允许嵌入跨源图片。但是,系统会禁止读取跨源图片数据(例如,使用 JavaScript 从跨源图片检索二进制数据)。 |
多媒体 | 跨源视频和音频可以使用 <video> 和 <audio> 元素嵌入。 |
脚本 | 可以嵌入跨源脚本,但可能会阻止访问某些 API(例如跨源提取请求)。 |
简单请求
若请求满足所有下述条件,则该请求可视为简单请求:
使用下列方法之一:
- GET
- HEAD
- POST
除了被用户代理自动设置的标头字段(例如 Connection、User-Agent 或其他在 Fetch 规范中定义为禁用标头名称的标头),允许人为设置的字段为 Fetch 规范定义的对 CORS 安全的标头字段集合。该集合为:
- Accept
- Accept-Language
- Content-Language
- Content-Type(需要注意额外的限制)
- Range(只允许简单的范围标头值 如 bytes=256- 或 bytes=127-255)
Content-Type 标头所指定的媒体类型的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
如果请求是使用 XMLHttpRequest 对象发出的,在返回的 XMLHttpRequest.upload 对象属性上没有注册任何事件监听器;也就是说,给定一个 XMLHttpRequest 实例 xhr,没有调用 xhr.upload.addEventListener(),以监听该上传请求。
请求中没有使用 ReadableStream 对象。
复杂请求
- 使用
GET
、POST
或HEAD
以外 方法的请求 - 包含除
Accept
、Accept-Language
或Content-Language
以外 的标头的请求 - 请求具有除
application/x-www-form-urlencoded
、multipart/form-data
或 text/plain
以外 的 Content-Type 标头
预检请求
如果 Web 应用需要复杂请求,浏览器会将预检请求添加到请求链的前面。以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
避免预检请求:使用简单请求
或者服务器端设置 Access-Control-Max-Age
字段
// 请求
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
// 响应
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, DELETE, HEAD, OPTIONS
// 尽量避免预检请求:服务器端设置 `Access-Control-Max-Age` 响应头字段
// 这个响应头表示预检请求的返回结果(即 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 提供的信息)可以被缓存多久。
Access-Control-Max-Age: 600 // 将预检请求的结果缓存 10 分钟:
// 请求
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
// 响应
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, DELETE, HEAD, OPTIONS
// 尽量避免预检请求:服务器端设置 `Access-Control-Max-Age` 响应头字段
// 这个响应头表示预检请求的返回结果(即 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 提供的信息)可以被缓存多久。
Access-Control-Max-Age: 600 // 将预检请求的结果缓存 10 分钟:
跨域请求
同源政策会告知浏览器屏蔽跨源请求。 如果您想从另一个源获取公开资源,资源提供服务器需要告知浏览器“请求的源可以访问我的资源”。 浏览器会记住这一点,并允许跨源资源共享。
// 发起请求
fetch('https://cors-demo.glitch.me/allow-cors', { mode: 'cors' })
// 请求头
orign: https://example.com // 跨域请求会携带 orign 属性
// 响应头
Access-Control-Allow-Origin: * // * 或者 https://example.com,表示允许跨域访问
// 发起请求
fetch('https://cors-demo.glitch.me/allow-cors', { mode: 'cors' })
// 请求头
orign: https://example.com // 跨域请求会携带 orign 属性
// 响应头
Access-Control-Allow-Origin: * // * 或者 https://example.com,表示允许跨域访问
与 CORS 共享凭据
// 出于隐私保护方面的原因,CORS 通常用于“匿名请求”,即请求不识别请求者的身份。
// 如果您想在使用 CORS(可以识别发件人)时发送 Cookie,则需要在请求和响应中添加其他标头。
// 请求
// 将 credentials: 'include' 添加到如下所示的提取选项中。这会将该 Cookie 添加到请求中
fetch('https://example.com', {
mode: 'cors',
credentials: 'include'
})
// 响应
// Access-Control-Allow-Origin 必须设置为特定源站(不使用 * 的通配符),
// 并且必须将 Access-Control-Allow-Credentials 设置为 true。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
// 出于隐私保护方面的原因,CORS 通常用于“匿名请求”,即请求不识别请求者的身份。
// 如果您想在使用 CORS(可以识别发件人)时发送 Cookie,则需要在请求和响应中添加其他标头。
// 请求
// 将 credentials: 'include' 添加到如下所示的提取选项中。这会将该 Cookie 添加到请求中
fetch('https://example.com', {
mode: 'cors',
credentials: 'include'
})
// 响应
// Access-Control-Allow-Origin 必须设置为特定源站(不使用 * 的通配符),
// 并且必须将 Access-Control-Allow-Credentials 设置为 true。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
其他跨域的手段
jsonp
利用了 script 不受同源策略的限制 (缺点:只能 get 方式,易受到 XSS 攻击)Nginx
前端向发送请求,经过代理,请求需要的服务器资源 (缺点:需要额外的代理服务器)HTML5 PostMessage
方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递 (缺点:浏览器版本要求,部分浏览器要配置放开跨域限制)websocket
是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求 (缺点:浏览器一定版本要求,服务器需要支持 websocket 协议)iframe
通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中 (缺点:页面的属性值有大小限制)
如何在服务器中添加 CORS 支持
// CORS on ExpressJS
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'YOUR-DOMAIN.TLD'); // update to match the domain you will make the request from
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
app.get('/', function (req, res, next) {});
app.post('/', function (req, res, next) {});
// CORS on ExpressJS
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'YOUR-DOMAIN.TLD'); // update to match the domain you will make the request from
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
app.get('/', function (req, res, next) {});
app.post('/', function (req, res, next) {});
页面通信
- WebSocket
- 定时器 + 客户端存储
- postMessage
- StorageEvent
- Broadcast Channel
- SharedWorker
- MessageChannel
单点登录
SSO (Single Sign On); cookie 是 http-only;token 有效期是 1 天且支持随时手动失效;ticket 有效期 5 分钟;
- 访问子系统,鉴权失败重定向到 SSO 系统进行登录
- 登录成功:SSO 系统生成 token,并将用户信息存到 Redis 中,并设置过期时间
- 设置 SSO 前端 cookie (cookie 包含 token 信息), SSO 系统生成临时票据 ticket (可换取 token)
- 携带 ticket 重定向回子系统,SSO 系统拿到子系统临时票据 ticket 换取 token 鉴权
- 鉴权成功:ticket 作废(防盗用);设置子系统 cookie
- 以后每次请求时,Cookie 都会带上,拦截器得到 token 做鉴权
- ===>
- 访问其他子系统,鉴权失败重定向到 SSO 系统发现 SSO 前端 cookie 已登录
- SSO 系统生成临时票据 ticket (可换取 token)
- 携带 ticket 重定向回其他子系统,SSO 系统拿到其他子系统临时票据 ticket 换取 token 鉴权
- 鉴权成功:ticket 作废(防盗用);设置其他子系统 cookie
- 以后每次请求时,Cookie 都会带上,拦截器得到 token 做鉴权
重绘
指当页面中的一些元素样式发生改变,但并不影响它们在文档流中的位置或布局时,浏览器会重新绘制这些元素的外观。例如,改变元素的背景颜色、字体颜色等。
回流
指当页面的布局和几何属性发生改变时,浏览器需要重新计算元素的位置和大小,并重新构建渲染树。 回流必定会发生重绘,重绘不一定会引发回流。
回流往往是由于以下的操作引起的:
- 页面初始化渲染
- 添加、删除、修改元素
- 修改 CSS 样式
- 修改元素的尺寸、位置、显隐
- 修改文字内容或字体样式
- 查询某些元素的几何属性(例如 width、height、offset*、scroll*、client*、getComputedStyle()、getBoundingClientRect()等)
为了减少回流的次数,可以采取以下优化策略:
- 使用 CSS 的
transform
属性来进行位移和动画效果,而不是通过修改元素的 top、left 等属性。 - 将需要多次修改的 DOM 操作,通过
离线
方式完成,例如使用文档片段(DocumentFragment)进行操作,最后再将其添加到文档中。 - 使用批量修改样式的方式,将多个样式的
修改合并
为一次操作,避免多次触发回流。 避免频繁查询
元素的几何属性,尽量在一次操作中获取需要的属性值。
页面加载优化
- 压缩和合并文件:压缩 CSS 和 JavaScript 文件,减小文件大小,提高下载速度。合并多个文件为一个文件,减少请求数量。
- 使用缓存:设置适当的缓存策略,让浏览器缓存静态资源,减少重复的请求。通过设置 HTTP 响应头中的 Cache-Control 和 Expires 来控制缓存策略。
- 延迟加载:将不是首次展示需要的资源进行延迟加载,例如图片懒加载、按需加载 JavaScript 等。这样可以减少初始页面加载时间。
- 异步加载:使用 async 或 defer 属性来异步加载 JavaScript 脚本,避免阻塞页面的渲染和其他资源的加载。
- 优化图片:使用适当的图片格式,选择合适的压缩级别,以减小图片文件大小。使用 CSS Sprites 或图标字体来减少图片请求次数。
- 优化 CSS 和 JavaScript:避免使用过多的 CSS 和 JavaScript 文件,合并和压缩它们。将 CSS 放在页面头部,将 JavaScript 放在页面底部,以避免阻塞渲染。
- DNS 预解析:通过使用<link rel="dns-prefetch">标签来预解析主机名,加速 DNS 查找,提高资源请求的速度。
- 减少重定向:避免不必要的重定向,每个重定向都会增加额外的网络延迟。
- 使用 CDN:将静态资源部署到内容分发网络(CDN)上,使用户可以从离其地理位置较近的服务器加载资源,提高加载速度。
- 优化服务器响应时间:通过优化服务器端的代码和配置,减少服务器响应时间,加速页面加载。
- 移除不必要的第三方插件和库:精简使用第三方插件和库的数量,只保留必要的功能,减少额外的加载和处理开销。
- 按需加载资源:根据页面的实际需要,只加载必要的资源,减少不必要的请求和加载时间。
页面性能评估
浏览器开发者工具
:现代浏览器都提供了内置的开发者工具,其中包含了一些性能评估工具。通过浏览器开发者工具中的"Performance
"或"Network
"选项卡,您可以收集并分析页面加载的各个方面,包括请求时间、资源大小、渲染时间等。PageSpeed Insights
:PageSpeed Insights 是由 Google 提供的免费在线工具,用于评估网页的性能和提供改进建议。您只需输入网页的 URL,PageSpeed Insights 会分析页面并生成性能报告,包括页面加载时间、优化建议等。WebPageTest
:WebPageTest 是另一个免费的在线工具,它可以模拟不同地理位置和网络条件下的页面加载,并提供详细的性能报告。您可以选择不同的测试服务器和浏览器,以获取更全面的性能评估。Lighthouse
:Lighthouse 是一个开源工具,可以作为浏览器开发者工具的一部分,也可作为命令行工具使用。它可以评估网页的性能、可访问性、最佳实践和 SEO 等方面。Lighthouse 提供了综合的性能报告,并给出了优化建议。Real User Monitoring (RUM)
:通过使用 RUM 工具,您可以收集实际用户在访问网站时的性能数据。这些工具会在用户浏览器中插入脚本,以追踪页面加载时间、资源下载时间、用户交互等指标,并生成性能报告。一些流行的 RUM 工具包括 Google Analytics、New Relic、SpeedCurve 等。
前端切片上传
前端切片上传(Frontend Chunked Upload)是一种将大型文件分割成小块(切片)并逐个上传的技术。这种上传方式可以改善大文件上传的稳定性和用户体验,因为它允许在上传过程中断或出错时,只需要重新上传中断或出错的那个切片,而不需要重新上传整个文件。
切割文件:使用 JavaScript 将大文件切割成多个大小相等的切片。您可以使用 File.slice()方法或 Blob.slice()方法来实现切割操作。每个切片的大小可以根据具体需求确定,通常选择在几百 KB 到几 MB 之间。
逐个上传切片:使用 AJAX 或 Fetch 等技术,将每个切片逐个上传到服务器。您可以将切片作为请求的一部分,将其发送到服务器端的特定上传接口。在每个切片上传完成后,服务器端会返回一个确认响应,包含切片在整个文件中的索引或标识。
记录切片上传状态:在前端,您需要记录每个切片的上传状态,以便在上传过程中进行管理和控制。您可以使用一个数组或对象来保存每个切片的上传状态,可以是已上传、待上传、上传中、上传失败等。
处理上传完成:当所有切片都成功上传后,您需要通知服务器端进行切片的合并操作,以还原原始的完整文件。服务器端可以根据切片的索引或标识来确定切片的顺序和完整性,并将它们合并成完整的文件。
处理上传中断和错误:如果在上传过程中出现中断或错误,您可以根据记录的切片上传状态,重新上传中断或失败的切片,而无需重新上传整个文件。这可以节省时间和带宽,并提升上传的鲁棒性。
浏览器调试
- 打印日志(console)
- 断点调试 (debugger)
- 本地域名代理 (node proxy)
- 线上域名代理 (Charles 或者 Whistle)
- 本地代码覆盖 (Chrome Source Override)
HTTP 状态码
状态码 | 类别 | 描述 |
---|---|---|
1xx | Informational(信息状态码) | 接受请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向状态码) | 需要附加操作已完成请求 |
4xx | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 |
XMLHttpRequest.readyState
值 | 状态 | 描述 |
---|---|---|
0 | UNSENT | 代理被创建,但尚未调用 open() 方法。 |
1 | OPENED | open() 方法已经被调用。 |
2 | HEADERS_RECEIVED | send() 方法已经被调用,并且头部和状态已经可获得。 |
3 | LOADING | 下载中;responseText 属性已经包含部分数据。 |
4 | DONE | 下载操作已完成。 |
常见的:
200
ok 请求成功。一般用于 GET 与 POST 请求301
Moved Permanently 永久性重定向。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI 代替302
Found 临时性重定向。与 301 类似。但资源只是临时被移动。客户端应继续使用原有 URI303
See Other 查看其它地址。与 302 类似。使用 GET 请求查看304
Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源307
Temporary Redirect 临时重定向。与 302 类似。使用 GET 请求重定向,会按照浏览器标准,不会从 POST 变成 GET。400
Bad Request 客户端请求报文中存在语法错误,服务器无法理解。浏览器会像 200 OK 一样对待该状态吗401
Unauthorized 请求要求用户的身份认证,通过 HTTP 认证(BASIC 认证,DIGEST 认证)的认证信息,若之前已进行过一次请求,则表示用户认证失败403
Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求404
Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面。也可以在服务器拒绝请求且不想说明理由时使用500
Internal Server Error 服务器内部错误,无法完成请求,也可能是 web 应用存在 bug 或某些临时故障501
Not Implemented 服务器不支持请求的功能,无法完成请求503
Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中