SSR/CSR:HTML拼接?网页源码?SEO/交互性
SSR (server side render)服务端渲染,是指由服务侧(server side)完成页面的DOM结构拼接,然后发送到浏览器,为其绑定状态与事件,成为完全可交互页面的过程。
CSR(client side render)客户端渲染,是指由客户端(client side)JS完成页面和数据的拼接,生成DOM结构再交由浏览器渲染成页面的过程。
浏览器从输入url到渲染页面 过程
合成 URL->本地缓存/拦截请求->DNS解析IP/端口号(域名缓存)
TCP->HTTP请求
- DNS 解析:(域名系统Domain Name System)
DNS 查找数据缓存服务中是否缓存过当前域名信息,有则直接返回;
否则,会进行 DNS 解析返回域名对应的 IP 和端口号,
如果没有指定端口号,http 默认 80 端口,https 默认 443。
DNS协议属于应用层协议,一般是运行在UDP协议之上,使用53端口。
OSI模型和TCP/IP协议
HTTP协议:无状态的协议(Cookie/Session识别->状态)
无状态是指服务端无状态是指服务端不会存储每次的会话信息,对于客户端每次发送的请求,都认为它是一个新的请求,上一次会话和下一次会话没有联系。
Http和Https
端口80/443
HTTPS = HTTP + SSl/TLS协议(传输层)
用SSL/TLS对数据进行加密和解密
SSL的全称是Secure Sockets Layer,即安全 套接 层协议
TLS的全称是Transport Layer Security,即安全 传输 层协议
明文/密文
对称加密 数据
非对称加密 秘钥
服务端的公钥和私钥,非对称加密
客户端生成的随机秘钥,对称加密
常见的请求方式
GET/POST
HEAD(报文首部,验证URI有效性)
PUT/DELETE(报文文件)
OPTIONS(查询URI支持的HTTP方法)
HTTP标头
Connection: keep-alive TCP 就会一直保持连接。
在隔开一段时间之后发送几次没有数据内容的网络请求来判断当前连接是不是应该继续保留。
可能会造成大量的无用途连接,白白占用系统资源
Cache-Control
public:响应可以被任何缓存(客户端/代理服务器)缓存
private:响应只能被客户端缓存
no-cache:不应直接使用缓存的副本,而是需要先进行验证
no-store:响应和请求都不应被缓存,即不在本地存储中保存副本。
max-age=<seconds>:秒
Last-Modified/If-Modified-Since
如果将资源的最后修改日期Last-Modified<=If-Modified-Since,会 304(Not Modified)
ETag/If-None-Match:服务器生成的资源标识符(哈希值/实体标签)
使用了该头部,即为条件请求,若请求标识符==资源的当前标识符,则304
If-None-Match: "d41d8cd98f00b204e9800998ecf8427e"
主体:服务端响应/客户端请求
Content-Length:字节Byte为单位
Content-Length: 123
Content-Type/Accept:type/subtype ;parameter
应用程序类型(application):
application/x-www-form-urlencoded (默认)(URL encoded)表单数据被转换成键值对
application/json:JSON 数据(axios传递对象时默认)
application/xml:XML 数据
多媒体类型(multipart):
multipart/form-data:用于在 HTML 表单上传文件时的数据类型。
文本类型(text):
text/plain:纯文本文件,例如 .txt 文件。
text/html:HTML 文档,用于网页。
text/css:层叠样式表文件,控制网页的样式。
text/xml:XML 数据。
Content-Encoding/Accept-Encoding
Accept-Encoding: gzip, deflate
请求头
User-Agent:发起请求的用户代理(浏览器/应用程序)
Host:访问的主机名+端口
Origin:跨域请求时,指示请求的来源
Origin: https://www.example.com
Connection: keep-alive
Accept-Language:客户端 偏好的语言。
Accept-Language: en-US,en;q=0.5
凭证Credentials
(例如 cookies、HTTP 认证(用户名和密码)等)
Authorization:在需要身份验证的情况下,包含用于认证的凭据
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
withCredentials:是否发送凭据
Cookie: sessionid=abc123; username=johndoe
响应头
Set-Cookie:session_id=abcd123; Path=/; HttpOnly
Access-Control-Allow-Origin
Access-Control-Allow-Credentials:是否允许在跨域请求中包含凭据Credentials
Server:HTTP服务器的信息
Server: Apache/2.4.29 (Ubuntu)
Location:客户端重定向的URL。
Location: https://www.example.com/new-page
Content-Range: bytes <start>-<end>/<total> 分段下载
Content-Disposition:如何显示响应体,通常用于下载文件
Content-Disposition: attachment; filename="document.pdf"
缓存:
Cache-Control+max-age(HTTP/1.1)>
Expires(
HTTP/1.0)
http版本
1.0->1.1(一次传输多个文件,默认Connection: keep-alive)
http1.x解析基于文本,
http2.0采用二进制格式,新增特性 多路复用、header压缩、服务端推送(静态html资源)
HTTP/2和HTTP/3,主要支持 并发处理 和 减少延迟 方面。
HTTP/1.0
短连接(关闭连接)
无持久化(单个连接单个请求/响应)
无管道化(无法并发发送请求)
HTTP/1.1
持久(单个连接不同时间多个请求/响应)
管道化
分块/流式传输(边收边处理)
HTTP/2
二进制传输(更小的帧)
多路复用(单个连接同时多个请求/响应)
头部压缩
可服务器推送
HTTP/3
(QUIC协议)UDP代替TCP(时延,拥塞)
传输层也能多路复用
0-RRT握手
改进头部压缩
http状态码
1xx Informational (信息):接受请求正在处理
100 Cotinue
2xx Successful(成功):请求正常处理完毕
200 OK:有返回内容
201 Created:服务器创建新资源
204 No Content:无返回内容
206 Partial Content:处理了部分请求,返回了相应的数据范围【分段下载】
3xx - Redirection(重定向):附加操作已完成请求
301 Moved Permanently:永久重定向
服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302 Found:临时重定向
A.cookie 失效 有 CORS 问题
B.代理服务器的目标地址请求的不对,config/index.js 配置的 proxy 的 target
//响应拦截器
if (error.response && error.response.status === 302) {
if (window.location.host === 'expenseexternal.xx.cn') {
window.location.href = 'https:///passport-cloud.xxx.cn/login?service=' + encodeURIComponent(window.location.href)
}
}
304 Not Modified:304资源缓存
表示客户端发送了一个带有条件的请求(通常If-Modified-Since或
If-None-Match
)
4xx Client Error(客户端错误): 服务器无法处理请求
400 Bad Request:请求无效,服务器无法理解
401 Unauthorized:请求未经授权,需要提供凭证
403 Forbidden:服务器理解请求,但拒绝授权访问
404 Not Found:请求的资源不存在
5xx Server Error(服务器错误):服务器处理请求出错
500 Internal Server Error:服务器遇到了错误,无法完成请求
502 Bad Gateway:作为代理或网关的服务器从上游服务器接收到无效响应
503 Service Unavailable:连接超时
强制刷新:重新请求
后端查看配置 Nginx 反向代理参数有没有问题或者重启 Nginx 服务
缓存
强制缓存:Cache-Control+max-age(HTTP1.1)>
Expires(
1.0)
状态码为灰色的请求则代表使用了强制缓存,请求对应的Size值则代表该缓存存放的位置,分别为from memory cache 和 from disk cache。
- 浏览器读取缓存的顺序为memory –> disk。
- 访问URL–> 200 –> 关闭标签页 –> 重新打开URL–> 200(from disk cache) –> 刷新 –> 200(from memory cache)
协商缓存:If-None-Match/If-Modified-Since
存储:js/图片(内存,刷新),css(硬盘,渲染)
本地存储
Cookie:客户端内存/用户硬盘
属性
必选:Name,Value
Domain:域名或 ip(作用域,无法跨域,与端口无关),默认是当前域名
一级域名和二级域名之间是允许共享使用的
如果设成 .example.com,那么子域名 a.example.com 和 b.example.com 都可以使用.example.com 的 cookie;反之不可以。
Path:指定 cookie 在哪个路由下生效,默认 '/'
Expires/Max-Age:两种存储类型的Cookie:会话性(默认)与持久性
会话性cookie仅保存在客户端内存中,并在隐私浏览模式中用户关闭 整个 浏览器时失效;
(会话恢复/恢复上次打开的标签页,导致浏览器保留会话信息,包括Session Cookie
迫使服务器为 session 设置了一个失效时间,
当距离客户端上一次使用 session 的时间超过这个失效时间时,
服务器就认为客户端已经停止了活动,才会把 session 删除以节省存储空间)
持久性Cookie会保存在用户的硬盘中(txt ),直至生存期到或用户直接在网页中单击“注销”等按钮结束会话时才会失效
Max-Age:失效时间(单位秒)
为负数,该 cookie 为临时 cookie ,关闭浏览器即失效,
如果为 0,表示删除该 cookie 。默认为 -1。
如果 Max-Age 和 Expires 同时存在,以 Max-Age 为准。
HttpOnly:为 true 时,只有 http 能读取, js不能读,防止恶意脚本通过Cookie 攻击
但能通过 Application 中手动修改 cookie,所以还是没办法防止 XSS 攻击
Secure:默认为false,为true表示只有 https 的请求可以携带
SameSite:限制第三方 URL 是否可以携带 cookie(Chrome51 新增,chrome80+ 强制)
一个站点可以包含多个网页,例如个人博客
Strict:仅允许发送同站点请求的 cookie
Lax(默认):在顶级导航和POST请求中发送Cookie,但在跨站点的GET请求中不会发送
"顶级导航" 指的是从一个站点的一个URL跳转到另一个站点的URL,通常是通过点击链接或输入网址来实现的。
None:任意发送 cookie,但需要同时设置 Secure,即必须采用 https
Priority:优先级
Chrome :Low/Medium/High,当 cookie 数量超出时,低的会优先被清除
js操作cookie
读/改 document.cookie:HttpOnly为 true 时,只有 http 能读取, js不能读取
设置 public /index.html /<script>
<script>document.cookie = "sso_ticket=ST-xx-cn"</script>
删除:expires=new Date(0)
new Date(0)
:970年1月1日UTC时间,0代表的是 Unix 时间戳的起始时间
document.cookie = ‘cookieName=; expires=${new Date(0)}; path=/;’;
基本刷新只是从本地硬盘中重新拿取数据到浏览器
跨域请求
服务端设置cookie:SameSite=None(任意发送cookie)Secure=true(https)
响应头Access-Control-Allow-Credentials=true(允许发送Credentials)
请求头withCredentials=true(会发送Credentials)
session:存储在服务端,首次会话创建Cookie(name/value:‘JSESSIONID’/id)
大小/数量:一般单个cookie 保存的数据大小不超过 4kb,单个域名最多保存 30 个 cookie(浏览器不同有差异),session 没有大小和数量限制;
存储格式:cookie ,localstorage只能存字符串,session 可以存对象;
Token存在cookie/localstorage
http://www.example.com/user?token=xxx
服务端不用存储session
避开同源策略:token 完全由应用管理
移动端对cookie支持不友好,存于localstorage
WebStorage
localStorage
localStorage.setItem('uname','zwq')
localStorage.getItem('uname')
localStorage.key(0)
localStorage.removeItem('uname')
localStorage.clear()
sessionStorage
IndexDB
(运行在浏览器的非关系型数据库)
前端/后端路由
位置:
-
- 前端路由:处理在浏览器中展示的页面之间的导航,单页面应用程序(SPA)中常见,如React或 Vue Router。
- 后端路由:处理服务器端资源的请求和响应,确定服务器如何处理不同的 URL 请求,并返回适当的数据或页面。这是服务器端框架(如Express、Django)中的基本组成部分。
-
导航方式:
- 前端路由:更改 URL 的一部分,而不会重新加载整个页面。这使得用户体验更加流畅,因为只有局部内容会被刷新,使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置。
- 后端路由:在后端路由中,当用户点击链接或发出请求时,整个页面可能会重新加载,因为服务器会根据请求的 URL 来返回相应的页面或资源。
-
SEO(搜索引擎优化):
- 前端路由:由于前端路由一般是基于单页面应用程序,搜索引擎的爬虫可能会遇到困难,因为页面内容大部分是通过 JavaScript 动态生成的。需要额外的工作来优化 SEO。
- 后端路由:后端路由通常更利于 SEO,因为页面内容在服务器端就已经存在,搜索引擎可以直接索引。
同步 js标签跨域、url
<img src>,<link href>
异步
web-worker(创建分线程):适用于计算密集型任务
当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成。
web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。
Web Worker 线程无法直接访问 DOM(文档对象模型)元素、操作页面内容,也不能访问浏览器的 Window 和 Document 对象。这是为了防止 Web Worker 影响主页面的交互
index.js为加载到html页面中的主线程(js文件)
work.js为在index中创建的分线程
index.js:
创建分线程
var w =new webwork('work.js')//创建 Web Worker 对象
向子线程发送数据
w.postmesage('数据')
work.js:
onmessage = function(ev) {
console.log(ev);//接受主线程发送过来的ev.data数据
this.postMessage('数据')//通过 postmesage('数据') 向主线程发送数据
}
通过w.onmessage=function(ev){ev.data} ev.data 接受 a 的值.
w.terminate();//终止 Web Worker
异步ajax、websock协议
ajax是异步的技术术语,最早的api是xhr(XMLHttpRequest)
axios
同构:同样的代码在nodejs端,浏览器端都可用
在浏览器用xhr,Node.js中使用Node的内置http模块
// 在浏览器中,根据其Content-Type头信息,自动转换数据
axios.get('/api/data').then(response => {
// response.data 将是一个JavaScript对象
});
// 在Node.js中手动设置响应数据类型
axios.get('/api/data', { responseType: 'json' }).then(response => {
// response.data 将是一个JavaScript对象
});
- axios 新版本也支持了fetch
- 第三方库都是基于原生API的,所以axios也还是基于xhr的
fetch es6 api:基于promise,简单易用
window.fetch(url, { method: 'get'}) //fetch() 返回响应的promise
// 第一个then 设置请求的格式
.then(res => return res.json()) // 第一层then返回的是:响应的报文对象
.then((data) => { //第二层:如果想要使用第一层的数据,必须把数据序列化
res.json() 返回的对象是一个 Promise对象再进行then()
<!-- data为真正数据 -->
}).catch(e => console.log("Oops, error", e))
回调函数:参数为函数,且满足条件才执行该参数函数
隐式return 原promise对象/ 显式return (非promise会包装成promise)
then/catch/finally/async
创建Promise实例
await 值=Promise.resolve(值)
Promise.resolve(值):如果该值本身就是一个 promise实例,将直接被返回
Promise.reject(值)
constructor的函数参数:起始函数;同步(立即执行)
起始函数的函数参数(均可选):resolve (起始函数成功时调用)、reject(失败时调用)
状态:都没有的话,状态为pending[待定]初始状态
//什么都不输出
new Promise(()=>{}).then(()=>{
console.log("then")
}).finally(()=>{
console.log("finally")
})
Promise
Promise.prototype(实例)方法
then回调函数:
resolveFn的形参=resolve的参数
catch回调函数:
rejectFn的形参=reject的参数
finally回调函数:
Promise并发方法
allSettled()
:多个不依赖于彼此成功完成的异步任务时,或者:想知道每个 promise 的结果时
all
:当异步操作之间不存在结果的依赖时,all中的所有方法是一同执行的
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');//setTimeout(function[, delay, arg1, arg2, ...]);
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// Expected output: Array [3, 42, "foo"]
map:promise.reslove(catch)
catch方法返回的promise实例会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的。
let p1 = Promise.resolve(1)
let p2 = Promise.resolve(2)
let p3 = Promise.resolve(3)
let p4 = Promise.resolve(4)
let p5 = Promise.reject("error")
let arr = [p1,p2,p3,p4,p5];
let all = Promise.all(arr.map((promise)=>promise.catch((e)=>{console.log("错误信息"+e)})))
all.then(res=>{console.log(res)}).catch(err=>console.log(err));
async/await函数
Promise.resolve(a)
.then(b => {
// do something
})
.then(c => {
// do something
})
//等价于
async () => {
const a = await Promise.resolve(a);
const b = await Promise.resolve(b);
const c = await Promise.resolve(c);
}
async
关键字+函数
await 操作符+promise对象
promise对象变成reject状态时,整个async函数会中断
处理异常: try-catch 块
async function asyncFunc() {
try {
await new Promise(function (resolve, reject) {
throw "Some error"; // 或者 reject("Some error")
});
} catch (err) {
console.log(err);
// 会输出 Some error
}
}
asyncFunc();
宏任务,微任务,事件循环event loop
微任务microtask(异步):Promise async/await
微任务是宏任务
宏任务macrotask: setTimeout,setInterval定时事件,Ajax,DOM事件,script 脚本的执行、 I/O 操作、UI 渲染等。
微任务是JS级别的,宏任务是宿主级别的,是包含关系,不是先后关系。
1.macro task先执行
2.当前所有micro task逐条执行
3.下一条macro task执行”, 重复上面loop
setTimeout(delay=0)=setImmediate:下个Event Loop执行
setTimeout(_ => console.log(5))
new Promise(resolve => {
resolve()
console.log(1)
}).then(_ => {
console.log(3)
}).finally(_=>{
console.log(4)
})
console.log(2)
// 1 2 3 4 5
url:?(分割url和参数) &(参数分隔符 )#(锚点,id)
http://example.com/page?param1=value1¶m2=value2#section1
跨域
jsonp:js、css,img静态资源,仅get方式,
现在浏览器兼容性高了,逐步淘汰了
JSONP(JSON With Padding)是利用<script src=XXX>跨域
因为是动态创建script标签,所以它只支持get请求,不支持post请求
代理服务器:服务器间不用同源
正向代理主要是用来解决访问限制问题;
反向代理则是提供负载均衡、安全防护等作用。
Nginx反向代理:类似cors字段设置+proxy_pass
前端应用运行在 http://localhost:3000
,跨域访问后端 API ,它运行在 http://localhost:8000
server {
#HTTP 协议来监听请求,应该使用 listen 80;
listen 80;
server_name localhost;
location /api {
proxy_pass http://localhost:8000;
# 允许来自前端应用的跨域请求
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# 支持 OPTIONS 请求,用于预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# 响应预检请求,直接返回 204 No Content
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
proxy
vue.config.js
const configStatic = {
proxy: {
'/test': {
target: 'http://www.xx.cn',// 设置目标服务器的地址
changeOrigin: true,
pathRewrite: {
'^/test': '/'
}
}
}
module.exports = configStatic
cors(浏览器IE10以下不支持)
服务端Access-Control-Allow-Origin
preflight预检查:跨域复杂请求前,OPTIONS 请求
预检请求是指浏览器在发送跨域请求之前,会先发送一个 OPTIONS 请求,用于询问服务器是否接受实际的请求。
预检请求主要用于对复杂请求(例如包含自定义请求头或使用非简单标头字段的请求)进行安全验证。
简单请求:GET/HEAD/POST+Content-Type:text/plain,multipart/form-data,application/x-www-form-urlencoded
条件2:除了被允许的自定义请求头(例如:X-PINGOTHER),请求头仅包含一些简单标头字段,如:Accept、Accept-Language、Content-Language、Content-Type,Range。
其中Content-Type 的值仅限于下列三者之一:
- text/plain无格式正文(可以有效避免XSS漏洞)
- multipart/form-data(键值对型数据)
- application/x-www-form-urlencoded(URL encoded)(默认)
应用
当服务端没有设置跨域时,可以将json伪装成text/plain
原理:
包装成简单请求,以躲过预检查。但是有些客户端或者代理还是会预检查
安全:
浏览器对于 JSON 响应的默认处理机制是自动将其解析为 JavaScript 对象。
当浏览器接收到响应时,如果响应的 Content-Type 是 application/json 或没有指定 Content-Type,浏览器会自动将响应体解析为 JSON, JavaScript 对象,如果是恶意代码可能会被执行,导致安全漏洞或攻击。
通过将 JSON 响应伪装成 text/plain 类型,可以避免浏览器将响应自动解析为 JavaScript 对象的默认行为。
这样一来,JavaScript 代码需要手动对响应进行解析,确保数据的安全性和正确性。这种伪装可以提供额外的安全层,防止对于响应的自动解析可能带来的安全风险。
//请求头
GET /example HTTP/1.1
Host: example.com
Accept: text/plain
//响应头
HTTP/1.1 200 OK
Content-Type: text/plain
//响应体
{"foo": "bar"}
跨源资源共享(CORS) - HTTP | MDN
Cross-Origin Resource Sharing (CORS) - HTTP | MDN
websocket:HTML5新特性,TCP
兼容性不好,只适用于主流浏览器和IE10+
HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信
WebSocket 和 HTTP 都是应用层协议,都基于 TCP 协议。
WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了
原生WebSocket API使用起来不太方便
Socket.io
,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
本地文件socket.html向localhost:3000
发生数据和接受数据:
// socket.html
<script>
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = function () {
socket.send('我爱你');//向服务器发送数据
}
socket.onmessage = function (e) {
console.log(e.data);//接收服务器返回的数据
}
</script>
// server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');//记得安装ws
let wss = new WebSocket.Server({port:3000});
wss.on('connection',function(ws) {
ws.on('message', function (data) {
console.log(data);
ws.send('我不爱你')
});
})
web安全
XSS跨站脚本攻击Cross-Site Scripting(登陆时执行脚本读取)
解决:
- url参数使用encodeURIComponent方法转义
- 尽量不用InnerHtml插入HTML内容
CSRF跨站请求伪造Cross-site request forgery(凭证请求)
解决:添加验证码、使用token
SQL注入攻击(交表单/输域名 执行SQL命令)
解决:表单输入时通过正则表达式将一些特殊字符进行转换
DDoS攻击分布式拒绝服务 Distributed Denial of Service(请求过载)
解决:
- 限制单IP请求频率。
- 检查特权端口的开放