Jonnyan的原创笔记
alpine
alpine里python安装mssql笔记
Alpine linux如何配置和管理自定义服务
windows
window server2012远程授权重置
window获取本机所有IP
window远程桌面RDP加速方案
远程监控 Win10 资源占用
windows 下 mysql 区分大小写敏感问题
window下navicat无限试用脚本
win11恢复win10右键菜单样式
永久禁止windows更新
强制本地账户安装win10/11
Linux
解决openvpn的CRL has expired笔记
centos7.x配置时间服务器(chrony)
centos7.x下安装wireguard
解决influxdb的log日志输出位置
保存 iptable 规则并开机自动加载 | SA-Logs
kafka笔记
kafka的server.properties 配置文件参数说明
CentOS 和 RedHat 下 8 个最常用的 YUM 库
外网IP查询网站
VirtualBox Ubuntu20/centos7 命令行如何扩容分区磁盘
如何备份sqlite数据库
yum 安装 redis5/mq/consul
centos7.x 安装 docker-ce
zabbix4.2 的 yum+mariadb 方式部署安装
如何在 Linux 中查找最大的 10 个文件
mongodb 备份与还原操作
Linux 高频工具快速教程
yum 安装 influxdb/telegraf
ubuntu 14.04/16.04/18.04 yum 安装 zabbix-agent 教程
逃不掉的 mysql 数据库安装方式大全 yum rpm 源码
VIM 配置入门
find 命令结合 cp bash mv 命令使用的 4 种方式
Tomcat nginx log 日志按天分割切割
linux 和 pycharm 下终端彩色打印输出
centos5/6/7 下 yum 安装 zabbix-agent(被控端)
shell 脚本头,#!/bin/sh 与 #!/bin/bash 的区别.
electerm/tabby在执行screen命令后不显示滚动条
aws ec2 安装caddy2
No usable version of libssl was found
python
python virtualenv笔记
python配置文件INI/TOML/YAML/ENV的区别
python限制函数的执行时间
python里and和or的理解
SQLite is not a toy database | Anton Zhiyanov
四行代码实现 Python 管道 - Aber's blog
systemd管理虚拟环境Django+uwsgi+nginx配置教程
Linux shell命令创建python django用户
nginx子路径下反代运行多个django
django web 应用 runserver 模式下 cpu 占用高解决办法
解决 pip 安装模块报错 Cannot fetch index base URL http://pypi.python.org/simple/
docker
仅在首次启动时在Docker容器中运行命令
Docker多平台架构镜像构建
解决cadvisor监控内存值与docker stats命令值不一致问题
docker 清理指定日期之前的镜像
docker 部署 graylog 使用教程
docker 一键搭建 zerotier-moon 节点
alpine的docker镜像安装mysql/mariadb/redis
dockerfile 多阶段构建参考
Warning: Stopping docker.service, but it can still be activated by: docker.socket
nginx
Nginx限制并发连接数与下载速度
nginx仅允许域名访问禁止IP访问
Nginx 强制跳转 Https
nginx强制跳转https无限301循环问题
万字总结,带你全面系统的认识 Nginx
linux 下编译安装 nginx 完整版
解决 nginx 同端口强制跳转 https 配置 ssl 证书问题
nginx 关闭日志功能 access_log 关闭
基于 nginx 的 token 认证
杂记
小米手机MIUI12安装Google服务
使用sphinx+markdown+readthedocs+github来编写文档
N1由armbian直刷openwrt
N1安装docker版本的openwrt做旁路由
NUC10 i3/i5/i7系列开启局域网wol唤醒
威联通qnap安装nginx
威联通qnap配置开机自启动项
telegram bot python使用示例教程
四款paste临时文本分享平台
docker部署微力同步(verysync)
Android和IOS自部署通知程序
苹果M1如何科学上网
M1 mac iterm2配置lrzsz命令
漫威轮播
网件XR500/R7800刷机
DIY 编译 openwrt 固件
苹果 mac 版微软官方远程连接工具下载 Microsoft Remote Desktop For Mac
wireguard 实现 peer 互联, NAT to NAT
学习本来的样子
解决 aws ec2 的 centos7 设置时区无效
redis 问题优化
N1 如何完美刷入 armbian 系统教程
v2rayN 的 pac 简单规则
博客园 markdown 使用折叠语法和颜色标签
十年感悟之 python 之路
在浏览器输入 URL 回车后发生了什么?
grafana 里 prometheus 查询语法
国内开源镜像站点汇总
解决阿里云部署 office web apps ApplicationFailedException 报错问题
解决 mac 休眠睡眠异常耗电方法
jira 集成 fisheye 代码深度查看工具安装绿色版
阿里云 ecs 开启 x11 图形化桌面
markdown 完整语法规范 3.0 + 编辑工具介绍
pycharm 重置设置,恢复默认设置
[已解决]window 下 Can't connect to MySQL server on'localhost' (10061) 与无法启动 MYSQL 服务”1067 进程意外终止”
解决 xshell6 评估过期, 需采购问题
[已解决]pycharm 报错: AttributeError: module 'pip' has no attribute 'main'
[已解决]windows 下 python3.x 与 python2.7 共存版本 pip 使用报错问题
局域网共享工具总结
云策文档think配置https教程
MIUI12百度输入法小米版使用森林集皮肤办法
Jenkins 构建后通知到飞书
简易的openvpn安装
keychron V1键盘改键教程
caddy2配置SSE单向websock(How to proxy Server Sent Events caddy2)
机器监控告警
zabbix
yum / 编译安装 Zabbix 5.0 LTS
zabbix 监控 AWS-SQS 队列
Zabbix-agent 端配置文件说明
Prometheus+grafana
prometheus+grafana安装和配置
node_exporter主机监控
cadvisor容器监控
redis_exporter监控
rabbitmq_exporter监控
consul_exporter监控
windows_exporter
Open-Falcon
falcon 数据丢失处理方法参考
日志监控告警
graylog
graylog 通过 python 实现钉钉 / 微信 / webhook 告警
loki+grafana
Loki简介
Loki安装
Loki查询语法
grafana面板pannel语法
内网穿透
frp
zerotier
zerotier充当网关实现内网互联,访问其它节点内网
一分钟自建zerotier-plant
nps
anylink
N2N
OmniEdge
本文档发布于https://mrdoc.fun
-
+
首页
在浏览器输入 URL 回车后发生了什么?
> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [www.cnblogs.com](https://www.cnblogs.com/jonnyan/p/11468847.html) > 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [https://4ark.me/post/b6c7c0a2.html](https://4ark.me/post/b6c7c0a2.html) 这个问题已经是老生常谈了,更是经常被作为面试的压轴题出现,网上也有很多文章,但最近闲的无聊,然后就自己做了一篇笔记,感觉比之前理解更透彻了。 [](#%E5%89%8D%E8%A8%80 "前言")前言 ------------------------------ 这个问题已经是老生常谈了,更是经常被作为面试的压轴题出现,网上也有很多文章,但最近闲的无聊,然后就自己做了一篇笔记,感觉比之前理解更透彻了。 这篇笔记是我这两天看了数十篇文章总结出来的,所以相对全面一点,但由于我是做前端的,所以会比较重点分析浏览器渲染页面那一部分,至于其他部分我会罗列出关键词,感兴趣的可以自行查阅, **注意:**本文的步骤是建立在,请求的是一个简单的 HTTP 请求,没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上,尽管这是不切实际的。 [](#%E5%A4%A7%E8%87%B4%E6%B5%81%E7%A8%8B "大致流程")大致流程 ---------------------------------------------------- 1. URL 解析 2. DNS 查询 3. TCP 连接 4. 处理请求 5. 接受响应 6. 渲染页面 [](#%E4%B8%80%E3%80%81URL-%E8%A7%A3%E6%9E%90 "一、URL 解析")一、URL 解析 ---------------------------------------------------------------- **地址解析:** 首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。 **HSTS** 由于安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面。详见:[你所不知道的 HSTS](https://www.barretlee.com/blog/2015/10/22/hsts-intro/)。 **其他操作** 浏览器还会进行一些额外的操作,比如安全检查、访问限制(之前国产浏览器限制 996.icu)。 **检查缓存**  [](#%E4%BA%8C%E3%80%81DNS-%E6%9F%A5%E8%AF%A2 "二、DNS 查询")二、DNS 查询 ---------------------------------------------------------------- **基本步骤**  **1. 浏览器缓存** 浏览器会先检查是否在缓存中,没有则调用系统库函数进行查询。 **2. 操作系统缓存** 操作系统也有自己的 DNS 缓存,但在这之前,会向检查域名是否存在本地的 Hosts 文件里,没有则向 DNS 服务器发送查询请求。 **3. 路由器缓存** 路由器也有自己的缓存。 **4. ISP DNS 缓存** ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。 **根域名服务器查询** 在前面所有步骤没有缓存的情况下,本地 DNS 服务器会将请求转发到互联网上的根域,下面这个图很好的诠释了整个流程:  根域名服务器:[维基百科](https://zh.wikipedia.org/wiki/%E6%A0%B9%E7%B6%B2%E5%9F%9F%E5%90%8D%E7%A8%B1%E4%BC%BA%E6%9C%8D%E5%99%A8) **需要注意的点** 1. 递归方式:一路查下去中间不返回,得到最终结果才返回信息(浏览器到本地 DNS 服务器的过程) 2. 迭代方式,就是本地 DNS 服务器到根域名服务器查询的方式。 3. 什么是 DNS 劫持 4. 前端 dns-prefetch 优化 [](#%E4%B8%89%E3%80%81TCP-%E8%BF%9E%E6%8E%A5 "三、TCP 连接")三、TCP 连接 ---------------------------------------------------------------- TCP/IP 分为四层,在发送数据时,每层都要对数据进行封装:  ### [](#1-%E5%BA%94%E7%94%A8%E5%B1%82%EF%BC%9A%E5%8F%91%E9%80%81-HTTP-%E8%AF%B7%E6%B1%82 "1. 应用层:发送 HTTP 请求")**1. 应用层:发送 HTTP 请求** 在前面的步骤我们已经得到服务器的 IP 地址,浏览器会开始构造一个 HTTP 报文,其中包括: * 请求报头(Request Header):请求方法、目标地址、遵循的协议等等 * 请求主体(其他参数) 其中需要注意的点: * 浏览器只能发送 GET、POST 方法,而打开网页使用的是 GET 方法 ### [](#2-%E4%BC%A0%E8%BE%93%E5%B1%82%EF%BC%9ATCP-%E4%BC%A0%E8%BE%93%E6%8A%A5%E6%96%87 "2. 传输层:TCP 传输报文")**2. 传输层:TCP 传输报文** 传输层会发起一条到达服务器的 TCP 连接,为了方便传输,会对数据进行分割(以报文段为单位),并标记编号,方便服务器接受时能够准确地还原报文信息。 在建立连接前,会先进行 TCP 三次握手。 关于 TCP/IP 三次握手,网上已经有很多段子和图片生动地描述了。 相关知识点: 1. SYN 泛洪攻击 ### [](#3-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%9AIP%E5%8D%8F%E8%AE%AE%E6%9F%A5%E8%AF%A2Mac%E5%9C%B0%E5%9D%80 "3. 网络层:IP协议查询Mac地址")**3. 网络层:IP 协议查询 Mac 地址** 将数据段打包,并加入源及目标的 IP 地址,并且负责寻找传输路线。 判断目标地址是否与当前地址处于同一网络中,是的话直接根据 Mac 地址发送,否则使用路由表查找下一跳地址,以及使用 ARP 协议查询它的 Mac 地址。 注意:在 OSI 参考模型中 ARP 协议位于链路层,但在 TCP/IP 中,它位于网络层。 ### [](#4-%E9%93%BE%E8%B7%AF%E5%B1%82%EF%BC%9A%E4%BB%A5%E5%A4%AA%E7%BD%91%E5%8D%8F%E8%AE%AE "4. 链路层:以太网协议")**4. 链路层:以太网协议** **以太网协议** 根据以太网协议将数据分为以 “帧” 为单位的数据包,每一帧分为两个部分: * 标头:数据包的发送者、接受者、数据类型 * 数据:数据包具体内容 **Mac 地址** 以太网规定了连入网络的所有设备都必须具备 “网卡” 接口,数据包都是从一块网卡传递到另一块网卡,网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的,具备了一对一的能力。 **广播** 发送数据的方法很原始,直接把数据通过 ARP 协议,向本网络的所有机器发送,接收方根据标头信息与自身 Mac 地址比较,一致就接受,否则丢弃。 **注意**:接收方回应是单播。 相关知识点: 1. ARP 攻击 #### [](#%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%8E%A5%E5%8F%97%E8%AF%B7%E6%B1%82 "服务器接受请求")**服务器接受请求** 接受过程就是把以上步骤逆转过来,参见上图。 [](#%E5%9B%9B%E3%80%81%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%A4%84%E7%90%86%E8%AF%B7%E6%B1%82 "四、服务器处理请求")四、服务器处理请求 ----------------------------------------------------------------------------------------------------------- **大致流程**  **HTTPD** 最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx,以及 Windows 上的 IIS。 它会监听得到的请求,然后开启一个子进程去处理这个请求。 **处理请求** 接受 TCP 报文后,会对连接进行处理,对 HTTP 协议进行解析(请求方法、域名、路径等),并且进行一些验证: * 验证是否配置虚拟主机 * 验证虚拟主机是否接受此方法 * 验证该用户可以使用该方法(根据 IP 地址、身份信息等) **重定向** 假如服务器配置了 HTTP 重定向,就会返回一个 `301`永久重定向响应,浏览器就会根据响应,重新发送 HTTP 请求(重新执行上面的过程)。 关于更多:[详见这篇文章](https://www.cnblogs.com/workest/p/3891321.html) **URL 重写** 然后会查看 URL 重写规则,如果请求的文件是真实存在的,比如图片、html、css、js 文件等,则会直接把这个文件返回。 否则服务器会按照规则把请求重写到 一个 REST 风格的 URL 上。 然后根据动态语言的脚本,来决定调用什么类型的动态文件解释器来处理这个请求。 以 PHP 语言的 MVC 框架举例,它首先会初始化一些环境的参数,根据 URL 由上到下地去匹配路由,然后让路由所定义的方法去处理请求。 [](#%E4%BA%94%E3%80%81%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8E%A5%E5%8F%97%E5%93%8D%E5%BA%94 "五、浏览器接受响应")五、浏览器接受响应 ----------------------------------------------------------------------------------------------------------- 浏览器接收到来自服务器的响应资源后,会对资源进行分析。 首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。 如果响应资源进行了压缩(比如 gzip),还需要进行解压。 然后,对响应资源做缓存。 接下来,根据响应资源里的 [MIME](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types) 类型去解析响应内容(比如 HTML、Image 各有不同的解析方式)。 [](#%E5%85%AD%E3%80%81%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2 "六、渲染页面")六、渲染页面 -------------------------------------------------------------------------- **浏览器内核**  不同的浏览器内核,渲染过程也不完全相同,但大致流程都差不多。 **基本流程**  ### [](#1-HTML-%E8%A7%A3%E6%9E%90 "1.HTML 解析")**1.HTML 解析** 首先要知道浏览器解析是从上往下一行一行地解析的。 解析的过程可以分为四个步骤: **1. 解码(encoding)** 传输回来的其实都是一些二进制字节数据,浏览器需要根据文件指定编码(例如 UTF-8)转换成字符串,也就是 HTML 代码。 **2. 预解析(pre-parsing)** 预解析做的事情是提前加载资源,减少处理时间,它会识别一些会请求资源的属性,比如`img`标签的`src`属性,并将这个请求加到请求队列中。 **3. 符号化(Tokenization)** 符号化是词法分析的过程,将输入解析成符号,HTML 符号包括,开始标签、结束标签、属性名和属性值。 它通过一个状态机去识别符号的状态,比如遇到`<`,`>`状态都会产生变化。 **4. 构建树(tree construction)** 注意:符号化和构建树是并行操作的,也就是说只要解析到一个开始标签,就会创建一个 DOM 节点。 在上一步符号化中,解析器获得这些标记,然后以合适的方法创建`DOM`对象并把这些符号插入到`DOM`对象中。 ``` <html> <head> <title>Web page parsing</title> </head> <body> <div> <h1>Web page parsing</h1> <p>This is an example Web page.</p> </div> </body> </html> ```  **浏览器容错进制** 你从来没有在浏览器看过类似” 语法无效” 的错误,这是因为浏览器去纠正错误的语法,然后继续工作。 **事件** 当整个解析的过程完成以后,浏览器会通过`DOMContentLoaded`事件来通知`DOM`解析完成。 ### [](#2-CSS-%E8%A7%A3%E6%9E%90 "2. CSS 解析")2. CSS 解析 一旦浏览器下载了 CSS,CSS 解析器就会处理它遇到的任何 CSS,根据[语法规范](https://drafts.csswg.org/css-syntax-3/)解析出所有的 CSS 并进行标记化,然后我们得到一个规则表。 **CSS 匹配规则** 在匹配一个节点对应的 CSS 规则时,是按照从右到左的顺序的,例如:`div p { font-size :14px }`会先寻找所有的`p`标签然后判断它的父元素是否为`div`。 所以我们写 CSS 时,尽量用 id 和 class,千万不要过度层叠。 ### [](#3-%E6%B8%B2%E6%9F%93%E6%A0%91 "3. 渲染树")3. 渲染树 其实这就是一个 DOM 树和 CSS 规则树合并的过程。 注意:渲染树会忽略那些不需要渲染的节点,比如设置了`display:none`的节点。 **计算** 通过计算让任何尺寸值都减少到三个可能之一:`auto`、百分比、px,比如把`rem`转化为`px`。 **级联** 浏览器需要一种方法来确定哪些样式才真正需要应用到对应元素,所以它使用一个叫做`specificity`的公式,这个公式会通过: 1. 标签名、class、id 2. 是否内联样式 3. `!important` 然后得出一个权重值,取最高的那个。 **渲染阻塞** 当遇到一个`script`标签时,DOM 构建会被暂停,直至脚本完成执行,然后继续构建 DOM 树。 但如果 JS 依赖 CSS 样式,而它还没有被下载和构建时,浏览器就会延迟脚本执行,直至 CSS Rules 被构建。 所有我们知道: * CSS 会阻塞 JS 执行 * JS 会阻塞后面的 DOM 解析 为了避免这种情况,应该以下原则: * CSS 资源排在 JavaScript 资源前面 * JS 放在 HTML 最底部,也就是 `</body>`前 另外,如果要改变阻塞模式,可以使用 defer 与 async,详见:[这篇文章](https://github.com/xiaoyu2er/blog/issues/8) #### [](#4-%E5%B8%83%E5%B1%80%E4%B8%8E%E7%BB%98%E5%88%B6 "4. 布局与绘制")4. 布局与绘制 确定渲染树种所有节点的几何属性,比如:位置、大小等等,最后输入一个盒子模型,它能精准地捕获到每个元素在屏幕内的准确位置与大小。 然后遍历渲染树,调用渲染器的 paint() 方法在屏幕上显示其内容。 #### [](#5-%E5%90%88%E5%B9%B6%E6%B8%B2%E6%9F%93%E5%B1%82 "5. 合并渲染层")**5. 合并渲染层** 把以上绘制的所有图片合并,最终输出一张图片。 #### [](#6-%E5%9B%9E%E6%B5%81%E4%B8%8E%E9%87%8D%E7%BB%98 "6. 回流与重绘")**6. 回流与重绘** **回流 (reflow)** 当浏览器发现某个部分发现变化影响了布局时,需要倒回去重新渲染,会从`html`标签开始递归往下,重新计算位置和大小。 reflow 基本是无法避免的,因为当你滑动一下鼠标、resize 窗口,页面就会产生变化。 **重绘 (repaint)** 改变了某个元素的背景色、文字颜色等等不会影响周围元素的位置变化时,就会发生重绘。 每次重绘后,浏览器还需要合并渲染层并输出到屏幕上。 回流的成本要比重绘高很多,所以我们应该尽量避免产生回流。 比如: * `display:none` 会触发回流,而 `visibility:hidden` 只会触发重绘。 #### [](#7-JavaScript-%E7%BC%96%E8%AF%91%E6%89%A7%E8%A1%8C "7. JavaScript 编译执行")7. JavaScript 编译执行 **大致流程**  可以分为三个阶段: #### [](#1-%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90 "1. 词法分析")**1. 词法分析** JS 脚本加载完毕后,会首先进入语法分析阶段,它首先会分析代码块的语法是否正确,不正确则抛出 “语法错误”,停止执行。 几个步骤: * 分词,例如将`var a = 2`,,分成`var`、`a`、`=`、`2`这样的词法单元。 * 解析,将词法单元转换成抽象语法树(AST)。 * 代码生成,将抽象语法树转换成机器指令。 #### [](#2-%E9%A2%84%E7%BC%96%E8%AF%91 "2. 预编译")**2. 预编译** JS 有三种运行环境: * 全局环境 * 函数环境 * eval 每进入一个不同的运行环境都会创建一个对应的执行上下文,根据不同的上下文环境,形成一个函数调用栈,栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。 **创建执行上下文** 创建执行上下文的过程中,主要做了以下三件事: * 创建变量对象 * 参数、函数、变量 * 建立作用域链 * 确认当前执行环境是否能访问变量 * 确定 This 指向 #### [](#3-%E6%89%A7%E8%A1%8C "3. 执行")**3. 执行** **JS 线程**  虽然 JS 是单线程的,但实际上参与工作的线程一共有四个: 其中三个只是协助,只有 JS 引擎线程是真正执行的 * JS 引擎线程:也叫 JS 内核,负责解析执行 JS 脚本程序的主线程,例如 V8 引擎 * 事件触发线程:属于浏览器内核线程,主要用于控制事件,例如鼠标、键盘等,当事件被触发时,就会把事件的处理函数推进事件队列,等待 JS 引擎线程执行 * 定时器触发线程:主要控制`setInterval`和`setTimeout`,用来计时,计时完毕后,则把定时器的处理函数推进事件队列中,等待 JS 引擎线程。 * HTTP 异步请求线程:通过 XMLHttpRequest 连接后,通过浏览器新开的一个线程,监控 readyState 状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待 JS 引擎线程执行。 **注:浏览器对同一域名的并发连接数是有限的,通常为 6 个。** **宏任务** 分为: * 同步任务:按照顺序执行,只有前一个任务完成后,才能执行后一个任务 * 异步任务:不直接执行,只有满足触发条件时,相关的线程将该异步任务推进任务队列中,等待 JS 引擎主线程上的任务执行完毕时才开始执行,例如异步 Ajax、DOM 事件,setTimeout 等。 **微任务** 微任务是 ES6 和 Node 环境下的,主要 API 有:`Promise`,`process.nextTick`。 微任务的执行在宏任务的同步任务之后,在异步任务之前。  **代码例子** ``` console.log('1'); // 宏任务 同步 setTimeout(function() { console.log('2'); // 宏任务 异步 }) new Promise(function(resolve) { console.log('3'); // 宏任务 同步 resolve(); }).then(function() { console.log('4') // 微任务 }) console.log('5') // 宏任务 同步 ``` 以上代码输出顺序为:1,3,5,4,2 [](#%E5%8F%82%E8%80%83%E6%96%87%E6%A1%A3 "参考文档")参考文档 ---------------------------------------------------- * [what-happens-when-zh_CN](https://github.com/skyline75489/what-happens-when-zh_CN) * [Tags to DOM](https://alistapart.com/article/tags-to-dom/) * [彻底理解浏览器的缓存机制](https://heyingye.github.io/2018/04/16/%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/) * [浏览器的工作原理:新式网络浏览器幕后揭秘](https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#The_rendering_engine) * [深入浅出浏览器渲染原理](https://blog.fundebug.com/2019/01/03/understand-browser-rendering/) * [js 引擎的执行过程(一)](https://heyingye.github.io/2018/03/19/js%E5%BC%95%E6%93%8E%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89/#%E9%A2%84%E7%BC%96%E8%AF%91%E9%98%B6%E6%AE%B5) * 还有一些找不到了。。。。。
Jonny
Feb. 27, 2022, 1:55 p.m.
625
0 条评论
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
【腾讯云】爆款2核2G4M云服务器一年45元,企业首购最高获赠300元京东卡
【腾讯云】爆款2核2G4M云服务器一年45元,企业首购最高获赠300元京东卡
Markdown文件
PDF文档
PDF文档(打印)
分享
链接
类型
密码
更新密码