自学几个月前端,为什么感觉什么都没学到??
这种现象在很多的初学者和自学前端的同学中是比较的常见的。
因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。
最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。
很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。
这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。
但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。
所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。
| no-cache | 不直接使用缓存,要求向服务器发起(新鲜度校验)请求 |
| no-store | 所以内容都不会被保存到缓存或Internet临时文件中 |
| no-transform | 告知客户端缓存文件时不得对实体数据做任何改变 |
| noly-if-cached | 告知(代理)服务器客户端希望获取缓存的内容(若有),而不用向原服务器发去请求 |
| must-revalidate | 当前资源一定是向原方法服务器发去验证请求的,如请求是吧会返回504(而非代理服务器上的缓存) |
| proxy-revalidate | 与must-revalidate类似,但仅能应用于共享缓存(如代理) |
| max-age=delta-seconds | 告知客户端该资源在delta-seconds秒内是新鲜的,无需向服务器发请求 |
| s-maxage=delta-seconds | 同max-age,但仅能应用于共享缓存(如代理) |
| cache-extension | 自定义扩展值,若服务器不识别该值将被忽略掉 |
示例:
// server.js
const http = require(‘http’)
const fs = require(‘fs’)
http.createServer(function (request, response) {
console.log(‘request come’, request.url)
if (request.url === ‘/’) {
const html = fs.readFileSync(‘test.html’, ‘utf8’)
response.writeHead(200, {
‘Content-Type’: ‘text/html’
})
response.end(html)
}
if (request.url === ‘/script.js’) {
response.writeHead(200, {
‘Content-Type’: ‘text/javascript’,
‘Cache-Control’: ‘max-age=20,public’ // 缓存20s 多个值用逗号分开
})
response.end(‘console.log(“script loaded”)’)
}
}).listen(8888)
console.log(‘server listening on 8888’)
// test.html
协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
模拟Last-Modified
if (request.url === ‘/index.js’) {
const filePath = path.join(__dirname, request.url); // 拼接当前脚本文件地址
const stat = fs.statSync(filePath); // 获取当前脚本状态
const mtime = stat.mtime.toGMTString() // 文件的最后修改时间
const requestMtime = request.headers[‘if-modified-since’]; // 来自浏览器传递的值
console.log(stat);
console.log(mtime, requestMtime);
// 走协商缓存
if (mtime === requestMtime) {
response.statusCode = 304;
response.end();
return;
}
// 协商缓存失效,重新读取数据设置 Last-Modified 响应头
console.log(‘协商缓存 Last-Modified 失效’);
response.writeHead(200, {
‘Content-Type’: ‘text/javascript’,
‘Last-Modified’: mtime,
});
const readStream = fs.createReadStream(filePath);
readStream.pipe(response);
}
模拟ETag
if (request.url === ‘/index.js’) {
const filePath = path.join(__dirname, request.url); // 拼接当前脚本文件地址
const buffer = fs.readFileSync(filePath); // 获取当前脚本状态
const fileMd5 = md5(buffer); // 文件的 md5 值
const noneMatch = request.headers[‘if-none-match’]; // 来自浏览器端传递的值
if (noneMatch === fileMd5) {
response.statusCode = 304;
response.end();
return;
}
console.log(‘Etag 缓存失效’);
response.writeHead(200, {
‘Content-Type’: ‘text/javascript’,
‘Cache-Control’: ‘max-age=0’,
‘ETag’: fileMd5,
});
const readStream = fs.createReadStream(filePath);
readStream.pipe(response);
}
Last-Modified(响应头),If-Modified-Since(请求头)
在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。浏览器接收到后,如果再次请求,会在请求头中携带If-Modified-Since
字段,这个字段的值也就是服务器传来的最后修改时间。
服务器拿到请求头中的If-Modified-Since
的字段后,其实会和这个服务器中该资源的最后修改时间Last-Modified
对比,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
但是如果在本地打开缓存文件,就会造成 Last-Modified
被修改,所以在 HTTP / 1.1
出现了 ETag
。
ETag(响应头)、If-None-Match(请求头)
ETag
是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过响应头把这个值给浏览器。浏览器接收到ETag的值,会在下次请求时,将这个值作为If-None-Match
这个字段的内容,并放到请求头中,然后发给服务器。
如果两种方式都支持的话,服务器会优先考虑ETag
存储位置
- Service Worker
Service Worker
是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker
的话,传输协议必须为 HTTPS
。因为 Service Worker
中涉及到请求拦截,所以必须使用 HTTPS
协议来保障安全。Service Worker
的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
Service Worker
实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker
,然后监听到 install
事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
当 Service Worker
没有命中缓存的时候,我们需要去调用 fetch
函数获取数据。也就是说,如果我们没有在 Service Worker
命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache
中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker
中获取的内容。
- Memory Cache
Memory Cache
也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。一旦我们关闭Tab页面,内存中的缓存也就被释放了。
那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。
需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头
Cache-Control
是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type
,CORS等其他特征做校验。
- Disk Cache
Disk Cache
也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache
胜在容量和存储时效性上。
- Push Cache
Push Cache
(推送缓存)是 HTTP/2
中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
-
所有的资源都能被推送,并且能够被缓存,但是
Edge
和Safari
浏览器支持相对比较差 -
可以推送
no-cache
和no-store
的资源 -
一旦连接被关闭,
Push Cache
就被释放 -
多个页面可以使用同一个
HTTP/2
的连接,也就可以使用同一个Push Cache
。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。 -
Push Cache
中的缓存只能被使用一次 -
浏览器可以拒绝接受已经存在的资源推送
-
你可以给其他域名推送资源
DNS域名解析
在发起http请求之前,浏览器首先要做去获得我们想访问网页的IP地址,浏览器会发送一个UDP的包给DNS域名解析服务器。
递归查询
我们的浏览器、操作系统、路由器都会缓存一些URL对应的IP地址,统称为DNS高速缓存。这是为了加快DNS解析速度,使得不必每次都到根域名服务器中去查询。
迭代查询
迭代查询的方式就是,局部的DNS服务器并不会自己向其他服务器进行查询,而是把能够解析该域名的服务器IP地址返回给客户端,客户端会不断的向这些服务器进行查询,直到查询到了位置,迭代的话只会帮你找到相关的服务器,然后说我现在比较忙,你自己去找吧。
DNS负载均衡
DNS还有负载均衡的作用,现在很多网站都有多个服务器,当一个网站访问量过大的时候,如果所有请求都请求在同一个服务器上,可能服务器就会崩掉,这时候就用到了DNS负载均衡技术,当一个网站有多个服务器地址时,在应答DNS查询的时候,DNS服务器会对每个查询返回不同的解析结果,也就是返回不同的IP地址,从而把访问引导到不同的服务器上去,来达到负载均衡的目的。例如可以根据每台机器的负载量,或者该机器距离用户的地理位置距离等等条件。
DNS预解析
大型网站,有多个不同服务器资源的情况下,都可采取DNS预解析,提前解析,减少页面卡顿。
第二部分 TCP/IP连接:三次握手
网络协议分层
TCP/IP协议
TCP(Transmission Control Protocol)传输控制协议。TCP/IP协议将应用层、表示层、会话层合并为应用层,物理层和数据链路层合并为网络接口层。
TCP/IP协议不仅仅指的是TCP和IP两个协议,⽽是指的⼀个由FTP,SMTP,TCP,UDP,IP,ARP等等协议构成的协议集合。
三次握手
客服端和服务端在进行http请求和返回的工程中,需要创建一个TCP connection
(由客户端发起),http
不存在连接这个概念,它只有请求和响应。请求和响应都是数据包,它们之间的传输通道就是TCP connection
。
位码即tcp标志位,有6种标示:
-
SYN(synchronous建立联机)
-
ACK(acknowledgement 确认)
-
PSH(push传送)
-
FIN(finish结束)
-
RST(reset重置)
-
URG(urgent紧急)
第一次握手:主机A发送位码为SYN=1
,随机产生Seq number=1234567
的数据包到服务器,主机B由SYN=1
知道,A要求建立联机;(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1)
,SUN=1,ACK=1234567 + 1
,随机产生Seq=7654321
的包;(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)
第三次握手:主机A收到后检查ack number
是否正确,即第一次发送的seq number+1
,以及位码SYN
是否为1,若正确,主机A会再发送ack number=(主机B的seq+1)
,ack=7654321 + 1
,主机B收到后确认Seq
值与ACK=7654321+ 1
则连接建立成功;(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)
总是要问:为什么需要三次握手,两次不行吗?其实这是由TCP的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的
接收
和发送
能力。第一次握手可以确认客服端的发送能力
,第二次握手,服务端SYN=1,Seq=Y
就确认了发送能力
,ACK=X+1
就确认了接收能力
,所以第三次握手才可以确认客户端的接收能力
。不然容易出现丢包的现象。
第三次握手的必要性?
试想如果是用两次握手,则会出现下面这种情况:如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。
数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。
什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题:服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…
ISN是固定的吗?
当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN。ISN可以看作是一个32比特的计数器,每4ms加1 。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。
三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据。
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
SYN攻击?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。
netstat -n -p TCP | grep SYN_RECV
常见的防御 SYN 攻击的方法有如下几种:
-
缩短超时(SYN Timeout)时间
-
增加最大半连接数
-
过滤网关防护
-
SYN cookies技术
第三部分 HTTP请求
HTTP 发展历史
HTTP/0.9
-
只有一个命令GET
-
响应类型: 仅 超文本
-
没有header等描述数据的信息
-
服务器发送完毕,就关闭TCP连接
HTTP/1.0
-
增加了很多命令(post HESD )
-
增加
status code
和header
-
多字符集支持、多部分发送、权限、缓存等
-
响应:不再只限于超文本 (Content-Type 头部提供了传输 HTML 之外文件的能力 — 如脚本、样式或媒体文件)
HTTP/1.1
-
持久连接。TCP三次握手会在任何连接被建立之前发生一次。最终,当发送了所有数据之后,服务器发送一个消息,表示不会再有更多数据向客户端发送了;则客户端才会关闭连接(断开 TCP)
-
支持的方法:
GET
,HEAD
,POST
,PUT
,DELETE
,TRACE
,OPTIONS
-
进行了重大的性能优化和特性增强,分块传输、压缩/解压、内容缓存磋商、虚拟主机(有单个IP地址的主机具有多个域名)、更快的响应,以及通过增加缓存节省了更多的带宽
HTTP2
-
所有数据以二进制传输。HTTP1.x是基于文本的,无法保证健壮性,HTTP2.0绝对使用新的二进制格式,方便且健壮
-
同一个连接里面发送多个请求不再需要按照顺序来
-
头信息压缩以及推送等提高效率的功能
HTTP3
- QUIC“快速UDP互联网连接”(Quick UDP Internet Connections)
HTTP3 的主要改进在传输层上。传输层不会再有我前面提到的那些繁重的 TCP 连接了。现在,一切都会走 UDP。
HTTP3详细介绍[2]
HTTP协议特点
-
支持客户/服务器模式。
-
简单快速客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
-
灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type(Content-Type是HTTP包中用来表示内容类型的标识)加以标记。
-
无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
-
无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
现在 HTTP3 最快!
HTTP报文
请求报文:
响应报文:
各协议与HTTP协议关系
-
DNS 服务:解析域名至对应的IP地址
-
HTTP 协议:生成针对目标Web服务器的HTTP请求报文
-
TCP 协议:将请求报文按序号分割成多个报文段
-
IP 协议:搜索对方的地址,一边中转一边传送
-
TCP 协议:按序号以原来的顺序重组请求报文请求的处理结果也同样利用TCP/IP协议向用户进行回传
- TCP是底层通讯协议,定义的是数据传输和连接方式的规范;
-
HTTP是应用层协议,定义的是传输数据的内容的规范;
-
HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP也就一定支持TCP。
关于HTTP的东西还有很多,我在最后放了张大图[3]。
HTTPS
在HTTP的基础上再加一层TLS(传输层安全性协议)或者SSL(安全套接层),就构成了HTTPS协议。
HTTPS 默认工作在 TCP 协议443端口,它的工作流程一般如以下方式:
-
TCP 三次同步握手
-
客户端验证服务器数字证书
-
DH 算法协商对称加密算法的密钥、hash 算法的密钥
-
SSL 安全加密隧道协商完成
-
网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash算法进行数据完整性保护,保证数据不被篡改。
-
客户端向服务端发送
Client Hello
消息,其中携带客户端支持的协议版本、加密算法、压缩算法以及客户端生成的随机数; -
服务端收到客户端支持的协议版本、加密算法等信息后;
-
向客户端发送
Server Hello
消息,并携带选择特定的协议版本、加密方法、会话 ID 以及服务端生成的随机数; -
向客户端发送
Certificate
消息,即服务端的证书链,其中包含证书支持的域名、发行方和有效期等信息; -
向客户端发送
Server Key Exchange
消息,传递公钥以及签名等信息; -
向客户端发送可选的消息
Certificate Request
,验证客户端的证书; -
向客户端发送
Server Hello Done
消息,通知服务端已经发送了全部的相关信息; -
客户端收到服务端的协议版本、加密方法、会话 ID 以及证书等信息后,验证服务端的证书;
-
向服务端发送
Client Key Exchange
消息,包含使用服务端公钥加密后的随机字符串,即预主密钥(Pre Master Secret
); -
向服务端发送
Change Cipher Spec
消息,通知服务端后面的数据段会加密传输; -
向服务端发送
Finished
消息,其中包含加密后的握手信息; -
服务端收到
Change Cipher Spec
和Finished
消息后; -
向客户端发送
Change Cipher Spec
消息,通知客户端后面的数据段会加密传输; -
向客户端发送
Finished
消息,验证客户端的Finished
消息并完成 TLS 握手;
TLS 握手的关键在于利用通信双发生成的随机字符串和服务端的证书公钥生成一个双方经过协商后的对称密钥,这样通信双方就可以使用这个对称密钥在后续的数据传输中加密消息数据,防止中间人的监听和攻击,保证通讯安全。
HTTPS连接 需要7次握手,3次TCP + 4次TLS。
第四部分 服务器处理请求并返回 HTTP 报文
每台服务器上都会安装处理请求的应用——Web Server。常见的Web Server 产品有 apache
、nginx
、IIS
或 Lighttpd
等。
HTTP请求一般可以分为两类,静态资源 和 动态资源。
请求访问静态资源,这个就直接根据url地址去服务器里找就好了。
请求动态资源的话,就需要web server把不同请求,委托给服务器上处理相应请求的程序进行处理(例如 CGI 脚本,JSP 脚本,servlets,ASP 脚本,服务器端 JavaScript,或者一些其它的服务器端技术等),然后返回后台程序处理产生的结果作为响应,发送到客户端。
服务器在处理请求的时候主要有三种方式:
-
第一种:是用一个线程来处理所有的请求,并且同时只能处理一个请求,但是这样的话性能是非常的低的。
-
第二种:是每一个请求都给他分配一个线程但是当链接和请求比较多的时候就会导致服务器的cpu不堪重负。
-
第三种:就是采用复用I/O的方式来处理例如通过epoll方式监视所有链接当链接状态发生改变的时候才去分配空间进行处理。
第五部分 浏览器渲染页面
DOM树
字节 → 字符 → 令牌 → 节点 → 对象模型。
Hello web performance students!
![](awesome-photo.jpg)
-
转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
-
令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“”、“”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
-
词法分析: 发出的令牌转换成定义其属性和规则的“对象”。
-
DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系: HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项,依此类推。
总结
技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
节 → 字符 → 令牌 → 节点 → 对象模型。
Hello web performance students!
![](awesome-photo.jpg)
-
转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
-
令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“”、“”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
-
词法分析: 发出的令牌转换成定义其属性和规则的“对象”。
-
DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系: HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项,依此类推。
总结
技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
[外链图片转存中…(img-rCtGEob8-1715469992586)]