一、前端需要注意哪些seo
- 合理的title/description/keywords(权重逐渐减小,每个页面应该不一样)
- 语义化的html代码(容易让搜索引擎理解)
主要有header aside section article p footer main等等 - 重要html代码放到最前面(搜索引擎抓取html是从上到下,有的搜索引擎抓取长度有限制)
- 重要的内容不要用js输出(搜索引擎不会执行js获取内容)
- 少用iframe
- 非装饰图片不用alt
提高网站速度
二、从浏览器输入url到页面展示,经历哪些步骤
- DNS解析
- TCP连接(三次握手)
- 发送http请求
- 服务器处理请求并返回http报文
- 浏览器解析报文,并渲染页面
断开连接(四次挥手)
1、浏览器如何DNS解析
从上到下找,知道找到为止
1、浏览器缓存
2、操作系统缓存
3、路由器缓存
4、本地DNS服务器递归解析(求助根服务器)2、域名空间结构
- . 根域名 全世界13个根域名服务器
- 一级域名(顶级域) edu gov com org cn
- 二级域名 imooc baidu 等
主机名 www news
3、OSI七层模型(参考模型)
1、应用层
- 发送数据
2、表示层 - 加密、压缩、转换二进制等
3、会话层 - 判断数据类型 ftp(ASII/二进制)
- 是否远程传输
4、传输层(确认收件人) - 确认传输协议(TCP/UDP)
- 确认端口号
5、网络层(选择哪个快递) - 写入ip地址
- 选择传输路径
6、数据链路层 - 打入MAC物理地址(局域网)
7、物理层 - 网线之类,开始传输
打个比方更容易理解
端口:收件人
ip:区域地址
mac:区域中某个邮局
物理层:邮筒(开始传递)
4、与七层参考模型对应的是TCP/IP四层模型
- 应用层
- 传输层
- 网络互联层
网络接口层
5、TCP/IP三次握手、四次分手
(1)什么是TCP协议、三次握手
TCP传输就像打电话,UPD就像发短信
TCP协议:面向连接的、可靠的传输协议
为什么是面向连接的、可靠的传输协议- 面向连接:当三次握手走通之后,双方要在内存开辟资源,然后app拿到资源,才能发送接收数据
可靠的:三次握手中,都是在内核传输控制层完成,且如果有一方断开,另一方会失败重传,直到对方由回应,所以可靠
// 应用层 app=new HttpServer() // 传输控制层
(2)四次分手
连接占用着资源、端口号,所以要四次分手
1、我想分手
2、我知道了
3、我也想分
4、我知道了
5、释放资源
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server
端收到Client
端的SYN
连接请求报文后,可以直接发送SYN+ACK
报文。其中ACK
报文是用来应答的,SYN
报文是用来同步的。但是关闭连接时,当Server
端收到FIN
报文时,很可能并不会立即关闭SOCKET
,所以只能先回复一个ACK
报文,告诉Client
端,"你发的FIN
报文我收到了"。只有等到我Server
端所有的报文都发送完了,我才能发送FIN
报文,因此不能一起发送。故需要四步握手。
6、进程process和线程Thread
1、进程:同时进行多个程序
2、线程:一个进程中同时做多个事情
3、栈内存(stack):提供一个执行代码的环境
4、堆内存 存放引用数据(function Array object ...)
(1)GUI渲染线程与JS引擎线程互斥
由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致
因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起, GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。
(2)渲染进程
1、解析html
(1)遇到script标签停止解析,执行script标签终的js,如果是外联,等待下载,并且执行完成js
(2) 继续解析html 解析完成 触发 DOMContentLoaded事件
注意: 由于js可以操作css, 一但js操作了css, 那会先解析css,解析完成再执行js,然后再继续解析dom
2、解析css
解析css和解析Html是并行处理的 两者互不相关
(3)defer(延迟执行)和async(异步下载)
- async(异步下载)
<script async src="script.js"></script>
async 属性表示异步执行引入的JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行,无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。
需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件;也就是async-script可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。 - defer(延迟执行)
defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。
async、defer与DOMContentLoaded的执行先后关系
再谈DOMContentLoaded与渲染阻塞—分析html页面事件与资源加载
最新总结:
onLoad: 所有资源加载完成包含async js/图片/css
onload不会等setTimout
DOMContentLoad:指的是加载、解析完成DOM,包括执行每一个js(包括defer js),和是否渲染到页面没关系
渲染:只要DOM和css完成render就会渲染(边解析边渲染),和js没直接关系(只要它不是在前面阻塞就好)
没有css情况下
DOM解析完成直接渲染到页面,无需等defer js
有css情况下
DOM解析完成 然后css解析完成,才会第一次渲染DOM,此时js(defer js或者放在底部的js)可能还在下载执行过程中,下载执行完成才会 DOMContentLoad,需要说明有css情况,js(包含defer js,不包含async js, 换句话说async会阻塞渲染)也会等css加载完成,才执行
同时如果css放在底部,html会先渲染一次,然后加载css再渲染一次
defer js会在所有执行js后再执行,
是否在setTimeout之后,要看setTimout时间
(4)渲染页面时常见哪些不良现象
由于浏览器的渲染机制不同,在渲染页面时会出现两种常见的不良现象—-白屏问题和FOUS(无样式内容闪烁)
FOUC
由于浏览器渲染机制(比如firefox),再CSS加载之前,先呈现了HTML(坑爹,css还没执行完就开始了第一次渲染),就会导致展示出无样式内容,然后样式突然呈现的现象;
白屏
- 有些浏览器渲染机制(比如chrome)要先构建DOM树和CSSOM树,构建完成后再进行渲染,如果CSS部分放在HTML尾部,由于CSS未加载完成,浏览器迟迟未渲染,从而导致白屏;
- 也可能是把js文件放在头部,脚本会阻塞后面内容的呈现,脚本会阻塞其后组件的下载,出现白屏问题。
其它注意点:
- css加载不会阻塞DOM树的解析
- css加载会阻塞DOM树的渲染
- css加载会阻塞后面js语句的执行
由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。所以css会阻塞后面js的执行 - 当页面只存在css,或者js都在css前面,那么DomContentLoaded不需要等到css加载完毕
- 当页面里同时存在css和js,并且js在css后面的时候,DomContentLoaded必须等到css和js都加载完毕才触发。
当js线程执行时,渲染线程呈挂起状态,只有当js线程空闲时渲染线程才会执行。所以显示提示和隐藏提示的dom操作都被浏览器记下来了并放在gui渲染线程的任务队列中,但都没有立刻进行渲染,而是在当前函数完成后(js线程已处于空闲状态),进行最终的dom渲染,而我们的用户则基本感受不到这个过程
function sleep(duration) { let start = Date.now(); while (duration + start > Date.now()) { } } for (var i = 0; i < 1000000; i++) { document.getElementById('box').innerText = i } document.getElementById('box').innerText = 'haha' new Promise((resolve) => { sleep(5000) resolve() }).then(() => { document.getElementById('box').innerText = "good" }) setTimeout(()=>{ document.getElementById('box').innerText = "bad" },5000) console.log('主执行栈宏任务');
执行过程
观察不到浏览器渲染0,1,2,...,哈哈
直到5s后渲染出good(因为微任务会阻塞渲染)
再5s后渲染出bad
7、操作系统多任务
单核CPU执行多任务:让每个任务轮流执行,0.01s就切换,感觉像是多任务
多核CPU: 可以多任务,但是任务太多了,操作系统也会轮流执行
8、https和http
过程
- 访问 https://baidu.com 会自动向443端口发请求(只要是https协议,就向443端口发请求,即使你指定了端口号也不行)
SSL
目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流浏览器都已经实现了TLS 1.2的支持。
TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。
1、在对SSL介绍之前先了解加密方法
主要的加密方法分为两种:
- 共享密钥加密(对称密钥加密)
- 公开密钥加密(非对称密钥加密)
(1)共享密钥加密:加密与解密使用同一个密钥
也就是说在加密的同时,也会把密钥发送给对方。在发送密钥过程中可能会造成密钥被窃取
一般所有客户端和服务端用一个密匙k
算法f 密匙k
加密:f1(k,data)=y 解密:f2(k,y)=data
- 缺点:发送消息会将y和k都发给服务端,黑客可以截取到y和k从而解密
(2)公开密钥(非对称密钥) - 使用一对非对称密钥。一把叫私有密钥,另一把叫公开密钥;
- 私有密钥不让任何人知道,公有密钥随意发送。
也就是说,发送密文方使用对方的公开密钥进行加密,对方接受到信息后,使用私有密钥进行解密。再不使用私有密钥情况下很难还原信息。
刚开始pk 和 sk都在服务端
f1(pk,data)=y;
f2(sk,y)=data;
f1(sk,data)=y;
f2(pk,y)=data;
- 缺点:客户端访问时先获取到公匙pk(黑客也知道),然后加密并发送给服务端,服务端通过sk解密,看似完美,但是服务端发送给客户端难办了:通过sk加密,黑客和客户端都可以通过pk解密;通过pk加密,客户端又没有私密
(3) 非对称+对称
客户端随机生成一个key,通过非对称加密发送到服务端,服务端解密得到key, 回复客户端ok, 之后的发送信息通过使用key对称加密传输信息,可以避免被黑客攻击 - 缺点:看似完美,但是还是有缺点(中介人攻击)
上述问题主要因为客户端不知道拿到的pk是黑客的还是服务端的,因此需要保证pk是从客户端拿到的
(4)非对称+对称+ca - 客户端存有ca大量的公匙信息,也就是知道cpk
- 客户端想获得服务端得pk但是又不想让黑客拦截,需要服务端从ca机构获取一个证书,这个证书是把服务端pk,用csk加密生成的,也就是f(pk, csk)=license
- 客户端从服务端请求证书,获取到后可以用spk去解密,获得服务端得pk
- 然后f(key,pk) = data发送给服务端,服务端用f(data,sk) = key获取到key
- 接下来双方通过对称加密key, 传输数据
这样保证了黑客是无法知道pk的
详细过程确认pk的详细过程
https原理
一文看懂HTTPS、证书机构(CA)、证书、数字签名、私钥、公钥
9、浏览器渲染机制
步骤:
1、浏览器拿到html资源后,浏览器再内存中开辟一块栈内存(大小由电脑 内存大小决定)给代码提供执行环境,同时分配一个主线程去一行行解析和执行代码(进栈出栈)
2、浏览器碰到link/script/img等请求后会开辟新的线程去加载这些资源文件(这些任务会放到task queue中),主线程不会停下
3、第一次走完主线程会生成DOM tree, 开始查看task queue的任务,取出任务并执行 css执行完生成CSSOM
4、DOM tree和CSSOM会生成render tree
5、render tree 经过回流计算绘制的位置、布局,经过重绘计算绘制的颜色、样式等
6、然后浏览器交给GUI去渲染(display)
10、重绘和回流
1、重绘
元素样式的改变(但宽高、位置、大小不变)
outline visible color background-color等
2、回流
2.1 什么情况下会引起回流
元素大小、位置、布局发生变化,导致需要重新布局(重新计算所有元素的位置)
- dom增删
- 元素尺寸发生变化
- 内容发生变化(文本或不同尺寸图片)
窗口resize
2.2 如何减少回流
1、避免操作dom
vue/react 数据响应,虚拟dom,diff算法能尽可能少的操作dom
2、样式集中改变dom.style.cssText="width:10px;height:20px;..." dom.className=".box"
3、分离读写
不要把读写写一块,浏览器由渲染队列,样式写一块,浏览器会先加入渲染队列,知道下一个不是样式,才进行一次回流;如果中间断开了会回流多次dom.style.width="10px" dom.style.height="10px"
4、元素批量修改
(1)createDocumentFragment(避免循环appendChild这种)
(2)模板字符串拼接
(3)缓存布局信息(和分离读写差不多)
看上去回流一次,其实是两次,因为碰到了document.clientWidthdom.style.width = document.clientWidth+10px dom.style.height = document.clientWidth+10px
改为
x=document.clientWidth+10px y=document.clientWidth+10px dom.style.width=x dom.style.height=y
5、动画用absolute fix 可以脱离文档流,不会影响其它布局
6、css3硬件加速(GPU加速)
transform filter opacity 不会影响回流重绘,可能会耗损内存
7、牺牲平滑度换取速度
比如每秒移动1px, 移动100px 需要100次,会发生100次回流
但每秒10px只需10次回流
8、避免table布局,避免使用css的js表达式
table是一层一层渲染的,会发生多次回流
11、性能优化
(1)减少http请求次数、大小
- 资源压缩合并(js/css/雪碧图)
- 图片懒加载(滚动到具体位置时再加载)
音视频走流文件(只加载一部分m3u8)
(2)DNS Prefetch
// 浏览器自动解析 <meta http-equiv="x-dns-prefetch-control" content="on"> // 手动 <meta http-equiv="x-dns-prefetch-control" content="off"> <link rel="dns-prefetch" href="//www.imooc.com">
其它还有
preconnect
preconnect用于启动预链接,其中包含DNS查找,TCP握手,以及可选的TLS协议,允许浏览器减少潜在的建立连接的开销。<link rel="preconnect" href="//example.com"> <link rel="preconnect" href="//cdn.example.com" crossorigin>
prefetch
<link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials"> <link rel="prefetch" href="/library.js" as="script">
prerender
<link rel="prerender" href="//example.com/next-page.html">
Preload(css)
<link rel="preload" href="/styles/other.css" as="style">
(3)图片懒加载
- getBoundingClientRect判断图片什么时候出现在可视化区域
- HTML5 有一个新的
IntersectionObserver
API,它可以自动观察元素是否可见 - IntersectionObserver API 使用教程
{
time: 3893.92,
rootBounds: ClientRect {
bottom: 920,
height: 1024,
left: 0,
right: 1024,
top: 0,
width: 920
},
boundingClientRect: ClientRect {
// ...
},
intersectionRect: ClientRect {
// ...
},
intersectionRatio: 0.54,
target: element
}
public UNSAFE_componentWillUnmount() {
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
}
public UNSAFE_componentWillMount() {
this.io = new IntersectionObserver((entries)=>{
entries.forEach((item)=>{
if (item.intersectionRatio <= 0) {return};
const ele = item.target.getElementsByTagName('img')[0];
if(!ele.src){
ele.src = ele.getAttribute('property') || '';
console.log('Loaded new items');
}
})
});
}
public ref =(ele:any)=>{
this.io.observe(ele);
}
(4)响应式图片
尽可能通过srcset,sizes和<picture>元素使用响应式图片。还可以通过<picture>元素使用WebP格式的图像(体积更小)
响应式图像教程
<h1>不同像素比</h1>
<img
src="./assets/1x.png"
srcset="./assets/1x.png 1x,
./assets/2x.png 2x,
./assets/3x.png 3x"
alt=""
>
<h1>不同屏幕</h1>
<img
src="./assets/1x.png"
srcset="./assets/1x.png 200w,
./assets/2x.png 400w,
./assets/3x.png 800w"
sizes="(max-width: 440px) 100vw,
(max-width: 900px) 33vw,
254px"
alt=""
>
<h1>不同屏幕不同像素</h1>
<picture>
<source srcset="./assets/2x.png,
./assets/3x.png 2x"
media="(min-width: 990px)">
<source srcset="./assets/1x.png,
./assets/2x.png 2x"
media="(min-width: 750px)">
<img srcset="./assets/1x.png,
./assets/2x.png 2x"
alt="Shopify Merchant, Corrine Anestopoulos">
</picture>
<h1>
不同类型图片支持
</h1>
<picture>
<source type="image/svg+xml" srcset="./assets/1x.png,
./assets/2x.png 2x">
<source type="image/webp" srcset="./assets/1x.png,
./assets/2x.png 2x">
<img src="./assets/2x.png" alt="ACME Corp">
</picture>
(5)非核心代码异步加载
- 异步无阻塞加载JS async deffer
异步加载css
(6)利用浏览器缓存
- 强缓存(问都不问,直接拿过来用)(如果以下两个服务器都下发了,以后者为准)
Expires 过期的绝对时间(服务器下发的,会有偏差)
Cache-Control 相对时间 3600s内就不请求数据
协商缓存(有缓存,但要问下服务器要不要用)
Last-Modified 上次修改的时间
if-Modified-Since
Etag 哈希值
If-None-Match
// node协商缓存 const http = require('http'); const querystring = require('querystring'); let number = 1; let date = new Date().getTime(); let etag = `W/"${date}"`; setInterval(() => { number++; date = new Date().getTime(); etag = `W/"${date}"`; }, 150000); const server = http.createServer((req, res) => { if (req.method === 'POST') { } else { res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8888'); res.setHeader('Cache-Control', 'max-age=0'); res.setHeader('Last-Modified', date); // res.setHeader('ETag', etag); let ifModifiedSince = req.headers['if-modified-since'] *1;//获取 if (ifModifiedSince >= date) { res.statusCode = 304; return res.end();//直接返回 } res.end(JSON.stringify({ url: req.url, method: req.method, data: number })); } }); server.listen(9000, () => { console.log('listening on port 9000 ~'); });
(7)http2(基于https)
- 多路复用(一个连接并发发送多个请求,http1是启用多个连接(6-8个),由数量限制、且资源损耗)
- 二进制传输(http1文本传输)
- header压缩(:表示)
后端主动发送response, 而不是前端请求(不需要有请求过程,直接读取 serser push)
(8) SSR(服务端渲染)
- 返回的html是js执行玩的代码,不需要再请求资源
- SSR: next.js nuxt.js
SSG: gatsby
12、performance数据分析
connectEnd: 1599389439394 connectStart: 1599389439328 domComplete: 1599389442115 domContentLoadedEventEnd: 1599389440900 domContentLoadedEventStart: 1599389440895 domInteractive: 1599389440890 domLoading: 1599389440830 domainLookupEnd: 1599389439328 domainLookupStart: 1599389439317 fetchStart: 1599389439308 loadEventEnd: 1599389442128 loadEventStart: 1599389442115 navigationStart: 1599389439308 redirectEnd: 0 redirectStart: 0 requestStart: 1599389439394 responseEnd: 1599389440827 responseStart: 1599389439426 secureConnectionStart: 1599389439361 unloadEventEnd: 0 unloadEventStart: 0
DNS查询耗时 :domainLookupEnd - domainLookupStart
TCP链接耗时 :connectEnd - connectStart
request请求耗时 :responseEnd - responseStart
解析dom树耗时 : domComplete- domInteractive
白屏时间 :domComplete - navigationStart
domready时间 :domContentLoadedEventEnd - navigationStart
onload时间 :loadEventEnd - navigationStart
参考文档
从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
css加载会造成阻塞吗
嗨,送你一张Web性能优化地图
js更新dom后页面及时渲染问题(js线程阻塞和解决办法)
使用window.performance分析web前端性能
TCP三次握手四次挥手
TCP协议中的三次握手和四次挥手(图解)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。