前端技术图谱
2021-11-16 15:23:54 1 举报
AI智能生成
前端技术图谱
作者其他创作
大纲/内容
框架
React 生态
React 基础
virtual dom
element
diff
dfs 遍历对比
patch
jsx 模板引擎
基础前端模板(如 ejs
Eval、Function
jsx(需要返回 virtualDom
词法分析
语法分析,生成 AST
遍历 AST,生成 virtualDom
setState 更新算法
1. 将更新状态添加到更新队列
2. 遍历更新队列,生成新的 state
3. 根据 shouldUpdate 标识判断是否需要渲染组件
2. 遍历更新队列,生成新的 state
3. 根据 shouldUpdate 标识判断是否需要渲染组件
pureComment 实现了 shouldComponentUpdate()
通过浅层对比state、props 判断组件是否需要刷新
通过浅层对比state、props 判断组件是否需要刷新
生命周期实现
Hook
useState
闭包 + 单链表存储闭包数据
useEffect
优势
重写不用移处或添加state,仅删除、添加行
用useEffects不用记生命周期方法
不像class组件的state可自定义和跨组件重用。
更干净的代码
更干净的代码
劣势
不擅长异步代码(settimeout 场景的闭包问题,或者循环里的闭包问题)
pureComponent
与 Component 几乎一样,通过浅比较实现基础的 shuouldComponentUpdate 功能
fiber 原理
生成一条链表
在已有的渲染树上进行 diff
跟渲染进程之间进行切换,直到生成完整的 diff tree
hook 原理
基础用法
高阶组件
Redux
实现思想
store、action、reducer
react-redux 实现
通过 hoc 封装组件
store 托管到 context 中
store 托管到 context 中
react-router
技术实现
hash
createHashHistory
1. location.hash=*
2. location.hash.replace()
3. hashChange
createHashHistory
1. location.hash=*
2. location.hash.replace()
3. hashChange
history
createBrowserHistory:
1. pushState
2. replaceState
3. popState
createBrowserHistory:
1. pushState
2. replaceState
3. popState
路径匹配:path-to-regexp
将link 路径转为正则
将link 路径转为正则
分支
react-router-dom
react-router-native
Vue 生态
Vue 基础
单向绑定
双向绑定
双向绑定
v-model只是v-bind:value 和 v-on:input的语法糖
单向绑定:插值形式{{data}},v-bind也是单向绑定
双向绑定:表单的v-model,用户对View层的更改会直接同步到Model层
双向绑定:表单的v-model,用户对View层的更改会直接同步到Model层
构建
webpack
HMR 实现
流程图
基本组成
loader
plugins
性能优化
工具
体积:webpack-bundle-analyzer
多线程
加载:thread-loader
压缩:parallel-uglify-plugin
缓存
babel-loader 开启缓存
使用 cache-loader
预编译资源模块
DllPlugin
包体积优化
作用域提升:Scope Hoisting
提速
减少文件搜索范围:resolve
缩小构建目标:exclude
最佳实践
external
防止将某些 import 的包(package)打包到 bundle 中,
而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
webpack@5
module federation
应用
微前端
资源复用,减少编译体积
基础
name:输出的模块名
filename:输出的文件名
remotes:远程引用的应用名
expose:被远程引用时可暴露的资源路径及其别名
shared:与其他应用之间可以共享的第三方依赖
filename:输出的文件名
remotes:远程引用的应用名
expose:被远程引用时可暴露的资源路径及其别名
shared:与其他应用之间可以共享的第三方依赖
Tree Shaking 原理
1. ES6 Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块
2. 静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码
2. 静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码
产物分析
webpack 原理
vite
原理
将文件分为依赖和源码
- 依赖:esbuild 构建(兼容 commonjs 和 UMD)
- 源码:在浏览器需要时进行转换提供到浏览器
将按需加载功能交给浏览器分配,需要什么引用什么
- 依赖:esbuild 构建(兼容 commonjs 和 UMD)
- 源码:在浏览器需要时进行转换提供到浏览器
将按需加载功能交给浏览器分配,需要什么引用什么
Node.js
Koa.js 洋葱模型
后台监控
多进程通信
进程管理 pm2
Node_module
npm
npm 2.x 层级嵌套
npm 扁平化
问题:小版本更新导致下载包
解决方案:package-lock 锁定版本
子主题
查找算法
- 优先读取最近的node_modules的依赖
- 递归向上查找node_modules依赖
- 递归向上查找node_modules依赖
网络
https
加密逻辑概览
1. 非对称加密用于证书生成/验证 & 客户端与服务器随机数 生成/验证
2. 对称加密用于服务器与客户端请求加解密
2. 对称加密用于服务器与客户端请求加解密
1. 服务器将「公钥、域名等数据」发给 CA
2. CA 将「公钥、域名等数据」通过 CA 的私钥进行加密,并返回给服务器
3. 客户端进行 HTTPS 请求时首先向服务器请求证书,服务端将 CA 下发的证书返回给客户端
4. 客户端在本地验证从服务端拿到的证书,并通过 CA 的公钥解密得到服务器的公钥
5. 客户端生成一段随机数,通过服务器的公钥加密,发给服务器,服务器解密获取随机数
6. 客户端与服务端通过随机数进行对称加密通信
2. CA 将「公钥、域名等数据」通过 CA 的私钥进行加密,并返回给服务器
3. 客户端进行 HTTPS 请求时首先向服务器请求证书,服务端将 CA 下发的证书返回给客户端
4. 客户端在本地验证从服务端拿到的证书,并通过 CA 的公钥解密得到服务器的公钥
5. 客户端生成一段随机数,通过服务器的公钥加密,发给服务器,服务器解密获取随机数
6. 客户端与服务端通过随机数进行对称加密通信
SSL/TLS 握手
1. "client hello"消息:客户端通过发送"client hello"消息向服务器发起握手请求,该消息包含了客户端所支持的 TLS 版本和密码组合以供服务器进行选择,还有一个"client random"随机字符串。
2. "server hello"消息:服务器发送"server hello"消息对客户端进行回应,该消息包含了数字证书,服务器选择的密码组合和"server random"随机字符串。
3. 验证:客户端对服务器发来的证书进行验证,确保对方的合法身份,验证过程可以细化为以下几个步骤:
3.1 检查数字签名
3.2 验证证书链 (这个概念下面会进行说明)
3.3 检查证书的有效期
3.4 检查证书的撤回状态 (撤回代表证书已失效)
4. "premaster secret"字符串:客户端向服务器发送另一个随机字符串"premaster secret (预主密钥)",这个字符串是经过服务器的公钥加密过的,只有对应的私钥才能解密。
5. 使用私钥:服务器使用私钥解密"premaster secret"。
6. 生成共享密钥:客户端和服务器均使用 client random,server random 和 premaster secret,并通过相同的算法生成相同的共享密钥 KEY。
7. 客户端就绪:客户端发送经过共享密钥 KEY加密过的"finished"信号。
8. 服务器就绪:服务器发送经过共享密钥 KEY加密过的"finished"信号。
9. 达成安全通信:握手完成,双方使用对称加密进行安全通信。
2. "server hello"消息:服务器发送"server hello"消息对客户端进行回应,该消息包含了数字证书,服务器选择的密码组合和"server random"随机字符串。
3. 验证:客户端对服务器发来的证书进行验证,确保对方的合法身份,验证过程可以细化为以下几个步骤:
3.1 检查数字签名
3.2 验证证书链 (这个概念下面会进行说明)
3.3 检查证书的有效期
3.4 检查证书的撤回状态 (撤回代表证书已失效)
4. "premaster secret"字符串:客户端向服务器发送另一个随机字符串"premaster secret (预主密钥)",这个字符串是经过服务器的公钥加密过的,只有对应的私钥才能解密。
5. 使用私钥:服务器使用私钥解密"premaster secret"。
6. 生成共享密钥:客户端和服务器均使用 client random,server random 和 premaster secret,并通过相同的算法生成相同的共享密钥 KEY。
7. 客户端就绪:客户端发送经过共享密钥 KEY加密过的"finished"信号。
8. 服务器就绪:服务器发送经过共享密钥 KEY加密过的"finished"信号。
9. 达成安全通信:握手完成,双方使用对称加密进行安全通信。
网络缓存
强缓存
Expire/Cache-Control
协商缓存
Last-modified/if-modified-since
ETag/if-none-match
ETag/if-none-match
Etag 为了解决 last-modified 问题
周期性更新文件,但内容不变
last-modified 检查是秒级单位,秒以下变更无法获取
一些服务器无法获取到文件更新时间
流程
1)浏览器在加载资源时,根据请求头的expires和cache-control判断是否有缓存且未过期。
2)如果缓存已过期
2.1)判断是否有 ETag,若有,带着 if-none-match 访问服务器
2.2)判断是否有 last-modified,若有,带着if-modified-since 访问服务器
2. 3)如果前面两者都没有命中,向服务器请求资源
2.4)如果服务器响应 304,读取本地缓存
2)如果缓存已过期
2.1)判断是否有 ETag,若有,带着 if-none-match 访问服务器
2.2)判断是否有 last-modified,若有,带着if-modified-since 访问服务器
2. 3)如果前面两者都没有命中,向服务器请求资源
2.4)如果服务器响应 304,读取本地缓存
cache-contron
no-cache
max-age=0
must-revalidate
max-age=0
must-revalidate
每次请求前需要重新验证
不走强缓存,直接走协商缓存
不走强缓存,直接走协商缓存
注意:如果服务器断了,max-age=0,状态可能会读取本地缓存
cache-contron:no-store
不存储任何缓存
public
表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存
private
子主题
DNS
路径算法
CDN
socket
HTTP2
特点
单一 TCP 连接
请求 & 响应:流
1. 多路复用
2. 拥有优先级
1. 多路复用
2. 拥有优先级
二进制分帧层
1. 优先级
2. 流量控制
3. 服务端推送
1. 优先级
2. 流量控制
3. 服务端推送
头部压缩
优化
加密套件协商
RSA 握手
ECDHE 握手
减少证书层级
减少证书大小
优化加密方式
TCP
三次握手
TCP 头部阻塞
在 TCP 传输过程中,由于单个数据包的丢失而造成的阻塞称为 TCP 上的队头阻塞。
如何保障数据安全
检验和
计算:TCP 首部、TCP 数据、TCP 伪首部
如果数据有差错或异常,丢弃数据,重新发送
序列号/确认应答
发送端发送给接收端,接受端会回应一个应答包
如果未回应,发送端会重发
超时重传
时间超过 RTT(往返时间)+抖动值
多次重发仍然未接收到响应包,发送端会认为接收端异常,关闭连接
最大消息长度
TCP 连接时,约定最大长度(MSS)作为发送单位,该长度刚好不被网络层分块
滑动窗口控制
子主题
拥塞控制
避免两台主机传输速率可能引起的丢包问题,在启动初期进行慢启动
慢启动:在启动初期以指数增长方式增长;设置一个慢启动的阈值,当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加至拥塞窗口;线性增长达到网络拥塞时立即把拥塞窗口置回1,进行新一轮的“慢启动”,同时新一轮的阈值变为原来的一半。
HTTP 优化
减少 DNS 查询
1. 减少域名
2. 开启预读,dns-prefetch
1. 减少域名
2. 开启预读,dns-prefetch
减少重定向
使用 CDN
压缩资源(图片、代码文件压缩,开启 gZip
缓存:Expire/Cache-control、Last-modified/ETag
计算机基础
编码
编码规则
utf-8
utf-16
utf-32
字符集
Unicode
ascii
JavaScript string.length
返回的是当前字符串的编码单元,2 bytes 为一个单位
内存分配
算法
复杂度
时间复杂度
空间复杂度
浏览器
渲染流程
dom 树
cssom 树
layout
回流
重绘
阻塞场景
性能优化
JavaScript 预解析
async
加载完立即执行
defer
在 DOMcontentloaded 事件之前执行
加载阶段
指标
关键资源个数
关键资源大小
请求资源需要多少个 RTT
方案
内联 js、css
压缩 js、css
交互
指标
原则:让单个帧的生成速度变快
方案
减少 JavaScript 脚本执行时间
避免强制同步执行
避免布局都抖动
合理使用 CSS 合成动画
避免频繁的垃圾回收
整体流程
1. 构建 DOM 树
2. 样式计算
将 css 转换为 styleSheet 中的数据
标准化样式的属性值
计算每个 dom 节点的具体样式
继承规则
层叠规则
3. 布局阶段
DOM 树 + computed Style 构造布局树
注意:仅包括可见节点,不包括 head 类或 display:none 一类不可见元素
JS 和 CSS 都可能会阻塞渲染
外部 JS,加载完文件并执行结束才会继续渲染
外部 CSS,如果下文 JS 中获取了元素样式,
则等 CSS 下载完才会继续渲染
则等 CSS 下载完才会继续渲染
4. 分层
分层逻辑
1. 拥有层叠上下文属性的元素会被提升为单独的一层
2. 需要剪裁(clip)的地方
5. 绘制
渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分成很多小的绘制指令,
然后再把这些指令按照顺序组成一个待绘制列表
然后再把这些指令按照顺序组成一个待绘制列表
6. 分块/栅格化raster
原因:图层较大,视口显示不全,如果绘制所有内容,会导致大的开销
因此,合成线程会将图层划分为图块(tile)
7. 合成&显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
重排、重绘、合成
重排:更新元素的几何属性
重绘:更新元素的绘制属性
更改了一个既不要布局也不要绘制的属性
避免重排&重绘的操作
1. 使用 class 操作样式,而不是频繁操作 style
2. 避免使用 table 布局
3. 批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
4. Debounce window resize 事件
5. 对 dom 属性的读写要分离
6. will-change: transform 做优化
2. 避免使用 table 布局
3. 批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
4. Debounce window resize 事件
5. 对 dom 属性的读写要分离
6. will-change: transform 做优化
触发重排或重绘
增删改 DOM 节点
隐藏 DOM节点,display:none,触发重排和重绘,visibility:hidden 仅触发重绘
用户操作:浏览器窗口 resize、修改字体大小、滚动页面(出现滚动条时)待存疑?
Javascript 执行机制
调用栈
块级作用域
var的创建和初始化被提升,赋值不会被提升。
let的创建被提升,初始化和赋值不会被提升。
function的创建、初始化和赋值均会被提升。
let的创建被提升,初始化和赋值不会被提升。
function的创建、初始化和赋值均会被提升。
作用域链 & 闭包
闭包
闭包:在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是 foo,那么这些变量的集合就称为 foo 函数的闭包
避免内存泄漏:
1. 如果一直使用,可作为全局变量(默认存在到页面关闭)
2. 如果使用频率不高,让其作为局部变量
1. 如果一直使用,可作为全局变量(默认存在到页面关闭)
2. 如果使用频率不高,让其作为局部变量
作用域链
JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。
this 机制
this 机制与作用域链是两套系统
this 解决的是JavaScript 在对象内部调用内部属性的需求
this 解决的是JavaScript 在对象内部调用内部属性的需求
指向
全局执行上下文
window
函数执行上下文
全局环境调用函数:函数内部的 this 指向的是全局变量 window。
通过对象调用内部方法:则方法内 this 指向对象本身
通过构造函数设置:this 指向新对象
function CreateObj(){
this.name = "极客时间"
}
var myObj = new CreateObj()
function CreateObj(){
this.name = "极客时间"
}
var myObj = new CreateObj()
缺陷 & 方案
1. 嵌套函数中的 this 不会从外层函数中继承
1. 通过箭头函数,箭头函数中的 this 由外层作用域决定
2. 将 this 传入,即 let self = this(this 体系更改为作用域体系)
2. 将 this 传入,即 let self = this(this 体系更改为作用域体系)
2. 普通函数 this 默认指向 window
开启严格模式,则this 为 undefined
更改指向
call
bind
apply
上下文
全局执行上下文
函数执行上下文
eval 执行上下文
原型链
定义
概念
原型对象
实例
构造函数
关系
构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。
原型链继承
1. 原型链继承
原理:覆写原型对象
SubType.prototype = new SuperType();
缺点:多实例引用造成数据污染
2. 构造函数继承
原理:使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)
function SuperType(){
this.color=["red","green","blue"];
}
function SubType(){
//继承自SuperType
SuperType.call(this);
}
this.color=["red","green","blue"];
}
function SubType(){
//继承自SuperType
SuperType.call(this);
}
缺点:
1. 只能继承父类的实例属性和方法,不能继承原型属性/方法
2. 无法实现复用,每个子类都有父类实例函数的副本,影响性能
1. 只能继承父类的实例属性和方法,不能继承原型属性/方法
2. 无法实现复用,每个子类都有父类实例函数的副本,影响性能
3. 组合继承
原理:用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
// 继承属性,第二次调用SuperType()
SuperType.call(this, name);
this.age = age;
}
// 继承方法,构建原型链,第一次调用SuperType()
SubType.prototype = new SuperType();
// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
// 继承属性,第二次调用SuperType()
SuperType.call(this, name);
this.age = age;
}
// 继承方法,构建原型链,第一次调用SuperType()
SubType.prototype = new SuperType();
// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
缺点:
实例对象instance1上的两个属性就屏蔽了其原型对象SubType.prototype的两个同名属性。
所以,组合模式的缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。
实例对象instance1上的两个属性就屏蔽了其原型对象SubType.prototype的两个同名属性。
所以,组合模式的缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。
4. 原型式继承/Object.create()
原理:利用空对象作为媒介,将某个对象设置为空对象构造函数的原型
function object(obj){
function F(){}
F.prototype = obj;
return new F();
}
function F(){}
F.prototype = obj;
return new F();
}
缺点:
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数
5. 寄生式继承
原理:在原型式继承的基础上,增强对象,返回构造函数
function createAnother(original){
var clone = object(original); // 通过调用 object() 函数创建一个新对象
clone.sayHi = function(){ // 以某种方式来增强对象
alert("hi");
};
return clone; // 返回这个对象
}
var clone = object(original); // 通过调用 object() 函数创建一个新对象
clone.sayHi = function(){ // 以某种方式来增强对象
alert("hi");
};
return clone; // 返回这个对象
}
缺点:同原型式继承
6. 寄生组合式继承
V8 工作原理
数据存储
代码空间
栈空间
原始类型数据都保存在栈中
栈中函数直接结束会被回收,保障栈空间内上下文切换效率
堆空间
引用类型存放在 “堆”中
闭包中,内部引用的外部函数的数据,会在编译时被存入堆空间
闭包产生过程:
第一步是需要预扫描内部函数;
第二步是把内部函数引用的外部变量保存到堆中。
闭包产生过程:
第一步是需要预扫描内部函数;
第二步是把内部函数引用的外部变量保存到堆中。
垃圾回收
基础判断
局部变量(函数内部的变量),当函数执行结束,没有其他引用(闭包),该变量就会被回收
全局变量的生命周期直到浏览器卸载页面才会结束,也就是说全局变量不会被垃圾回收
算法
引用计数(旧方案)
引用计数是以前的垃圾回收算法,该算法定义"内存不再使用"的标准很简单,就是看一个对象是否有指向它的引用,如果没有其他对象指向它,就说明该对象不再需要了
缺陷 Case:循环引用
分代式垃圾回收机制
新生代(副垃圾回收器)
特点
新生代中通常只支持1~8M的容量,所以主要存放生存时间较短的对象
机制
1、将新分配的对象存入对象区域中,当对象区域存满了,就会启动GC算法
2、对对象区域内的垃圾做标记,标记完成之后将对象区域中还存活的对象复制到空闲区域中,已经不用的对象就销毁。这个过程不会留下内存碎片
3、复制完成后,再将对象区域和空闲互换。既回收了垃圾也能让新生代中这两块区域无限重复使用下去
2、对对象区域内的垃圾做标记,标记完成之后将对象区域中还存活的对象复制到空闲区域中,已经不用的对象就销毁。这个过程不会留下内存碎片
3、复制完成后,再将对象区域和空闲互换。既回收了垃圾也能让新生代中这两块区域无限重复使用下去
缺点
1、经历过两次垃圾回收依然还存活的对象会被移到老生代空间中
2、如果空闲空间对象的占比超过25%,为了不影响内存分配,就会将对象转移到老生代空间
2、如果空闲空间对象的占比超过25%,为了不影响内存分配,就会将对象转移到老生代空间
老生代(主垃圾回收器)
特点
老生代特点就是占用空间大,所以主要存放存活时间长的对象
算法
标记清除算法
应对场景
1、某一个空间没有分块的时候
2、对象太多超过空间容量一定限制的时候
3、空间不能保证新生代中的对象转移到老生代中的时候
2、对象太多超过空间容量一定限制的时候
3、空间不能保证新生代中的对象转移到老生代中的时候
流程
1、从根部(js的全局对象)出发,遍历堆中所有对象,然后标记存活的对象
2、标记完成后,销毁没有被标记的对象
2、标记完成后,销毁没有被标记的对象
标记算法
全停顿(stop-the-world)标记
由于垃圾回收阶段,会暂停JS脚本执行,等垃圾回收完毕后再恢复JS执行,这种行为称为全停顿(stop-the-world)
比如堆中数据超过1G,那一次完整的垃圾回收可能需要1秒以上,这期间是会暂停JS线程执行的,这就导致页面性能和响应能力下降
比如堆中数据超过1G,那一次完整的垃圾回收可能需要1秒以上,这期间是会暂停JS线程执行的,这就导致页面性能和响应能力下降
增量标记
使用增量标记算法,GC 可以将回收任务分解成很多小任务,穿插在JS任务中间执行,这样避免了应用出现卡顿的情况
并发标记
让 GC 扫描和标记对象时,允许JS同时运行
标记压缩算法
清除后会造成堆内存出现内存碎片的情况,当碎片超过一定限制后会启动标记压缩算法,将存活的对象向堆中的一端移动,到所有对象移动完成,就清理掉不需要的内存
内存泄露
场景
1. 没有声明而意外创建的全局变量
2. 被遗忘的定时器和回调函数,没有及时关闭定时器中的引用会一直留在内存中
3. 闭包
4. DOM操作引用(比如引用了td却删了整个table,内存会保留整个table)
2. 被遗忘的定时器和回调函数,没有及时关闭定时器中的引用会一直留在内存中
3. 闭包
4. DOM操作引用(比如引用了td却删了整个table,内存会保留整个table)
避免内存泄露
1. 减少不必要的全局变量,比如使用严格模式避免创建意外的全局变量
2. 减少生命周期较长的对象,避免过多的对象
3. 使用完数据后,及时解除引用(闭包中的变量,DOM引用,定时器清除)
4. 组织好逻辑,避免死循环造成浏览器卡顿,崩溃
2. 减少生命周期较长的对象,避免过多的对象
3. 使用完数据后,及时解除引用(闭包中的变量,DOM引用,定时器清除)
4. 组织好逻辑,避免死循环造成浏览器卡顿,崩溃
V8 如何执行 JavaScript
1. 生成抽象语法树(AST)和执行上下文
阶段一:分词(此法分析
阶段二:解析(语法分析
2. 生成字节码
字节码可以减少系统的内存使用
3. 执行代码
热点代码被转换为机器码
JavaScript 性能优化
1. 提升单次脚本的执行速度,避免 JavaScript 的长任务霸占主线程,这样可以使得页面快速响应交互;
2. 避免大的内联脚本,因为在解析 HTML 的过程中,解析和编译也会占用主线程;
3. 减少 JavaScript 文件的容量,因为更小的文件会提升下载速度,并且占用更低的内存。
2. 避免大的内联脚本,因为在解析 HTML 的过程中,解析和编译也会占用主线程;
3. 减少 JavaScript 文件的容量,因为更小的文件会提升下载速度,并且占用更低的内存。
事件循环
在线程运行过程中,接受并执行新的任务,就需要采用事件循环机制
在线程运行过程中,接受并执行新的任务,就需要采用事件循环机制
消息队列 & 事件循环
宏任务 & 微任务
定义
消息队列中的任务为 宏任务
渲染事件:DOM 解析、计算布局、绘制
用户交互事件:鼠标点击、滚动页面、放大缩小
JavaScript 脚本执行事件
网络请求完成、文件读写完成事件
微任务就是一个需要异步执行的函数,执行时机在主函数执行结束之后、当前宏任务结束之前
每个宏任务中包含一个 微任务队列
每个宏任务中包含一个 微任务队列
产生方式
1. MutationObserver 监控某个 DOM 节点,通过 JavaScript 修改这个节点,会产生 DOM 变化记录的微任务
2. Promise,调用 Promise.resolve() 或 Promise.reject() 的时候,产生微任务
案例
setTimeOut
除消息队列外,存在延迟队列
每次消息循环都会执行一次延迟队列的到期任务
注意事项
1. 当前任务执行时间过久,会影响定时器任务执行
2. 如果 setTimeout 存在嵌套调用,那么系统会设置最短时间间隔 4 毫秒
3. 未激活页面,setTimeout 执行最小间隔为 1000毫秒
4. 延迟执行时间有最大值,目前主流浏览器使用 32 bit 存放延时值,即 (2^31 -1) 毫秒,约 24.8 天
5. this 指向 window
与 requestAnimationFrame 对比
requestAnimationFrame 提供一个原生的API去执行动画的效果,它会在一帧(一般是16ms)间隔内根据选择浏览器情况去执行相关动作。
setTimeout是在特定的时间间隔去执行任务,不到时间间隔不会去执行,这样浏览器就没有办法去自动优化。
setTimeout是在特定的时间间隔去执行任务,不到时间间隔不会去执行,这样浏览器就没有办法去自动优化。
XMLHttpRequest
机制
网络进程接受响应数据后,利用 IPC 通信通知渲染进程
渲染进程收到后,将 xhr 的回调函数封装为任务添加到消息队列中
事件循环到该任务时,回调函数被调用
网络进程接受响应数据后,利用 IPC 通信通知渲染进程
渲染进程收到后,将 xhr 的回调函数封装为任务添加到消息队列中
事件循环到该任务时,回调函数被调用
封装请求
Promise
目标
1. 消灭嵌套调用
2. 合并多个任务的错误处理
实现
回调函数的延时绑定
回调函数 onResolve 的返回值穿透到最外层
async/await
generator
协程
跑在线程上的任务
一个线程可以存在多个协程序
但线程同时仅能执行一个协程
如果从 A 协程启动 B 协程,我们就把 A 协程称为 B 协程的父协程。
子主题
消息队列
延迟执行消息队列
普通消息队列
worker
web worker
service worker
网络请求
流程:
浏览器中的 HTTP 请求从发起到结束一共经历了如下八个阶段:
构建请求、查找缓存、
准备 IP 和端口、等待 TCP 队列、
建立 TCP 连接、发起 HTTP 请求、
服务器处理请求、服务器返回请求和断开连接。
浏览器中的 HTTP 请求从发起到结束一共经历了如下八个阶段:
构建请求、查找缓存、
准备 IP 和端口、等待 TCP 队列、
建立 TCP 连接、发起 HTTP 请求、
服务器处理请求、服务器返回请求和断开连接。
输入域名
查找文件
命中缓存
无法命中,则继续请求
与服务器建立 TCP 连接
获取 IP + 端口
DNS 获取 IP
DNS 缓存
根域名服务器
权威域名服务器
HTTP 默认 80 端口
HTTPS 默认 443 端口
Chrome 同一个域名同时只能建立 6 个 TCP连接
超出则进入排队等待状态
超出则进入排队等待状态
请求
GET 与 POST 区别
语法区别:
参数:get 通过 url,post 通过 request body
缓存:get 请求会被缓存,post 不会(可手动设置)
安全:浏览器回退不会触发 get,post 会被触发
参数:get 通过 url,post 通过 request body
缓存:get 请求会被缓存,post 不会(可手动设置)
安全:浏览器回退不会触发 get,post 会被触发
语义区别:从 RFC 规范角度
get 是请求获取指定的资源
post 是请求对指定的资源做出处理
get 是请求获取指定的资源
post 是请求对指定的资源做出处理
http1
http2
特性
多路复用
支持设置请求的优先级
服务器推送(客户端获取 html 后,服务端推送 js、css
头部压缩
实现
二进制分帧层
http3
quic
流量控制、传输可靠
继承 tls 加密
实现 http/2 的多路复用
进程管理
渲染进程
插件进程
浏览器主进程
网络进程
GPU 进程
输入 URL 到页面出来,发生了什么
1. 首先,浏览器进程接收到用户输入的 URL 请求,浏览器进程便将该 URL 转发给网络进程。
2. 然后,在网络进程中发起真正的 URL 请求。接着网络进程接收到了响应头数据,便解析响应头数据,并将数据转发给浏览器进程。
3. 浏览器进程接收到网络进程的响应头数据之后,发送“提交导航 (CommitNavigation)”消息到渲染进程;
4. 渲染进程接收到“提交导航”的消息之后,便开始准备接收 HTML 数据,接收数据的方式是直接和网络进程建立数据管道;
5. 最后渲染进程会向浏览器进程“确认提交”,这是告诉浏览器进程:“已经准备好接受和解析页面数据了”。
6. 浏览器进程接收到渲染进程“提交文档”的消息之后,便开始移除之前旧的文档,然后更新浏览器进程中的页面状态。
2. 然后,在网络进程中发起真正的 URL 请求。接着网络进程接收到了响应头数据,便解析响应头数据,并将数据转发给浏览器进程。
3. 浏览器进程接收到网络进程的响应头数据之后,发送“提交导航 (CommitNavigation)”消息到渲染进程;
4. 渲染进程接收到“提交导航”的消息之后,便开始准备接收 HTML 数据,接收数据的方式是直接和网络进程建立数据管道;
5. 最后渲染进程会向浏览器进程“确认提交”,这是告诉浏览器进程:“已经准备好接受和解析页面数据了”。
6. 浏览器进程接收到渲染进程“提交文档”的消息之后,便开始移除之前旧的文档,然后更新浏览器进程中的页面状态。
安全
CSP
内容安全策略
CORS
应用场景
http 请求
web 字体(css @font-face 使用跨源字体)
webgl 贴图
drawImage 将 images/video 画面绘制到 canvas
服务端配置
Access-Control-Allow-Origin:<origin>|*
Access-Control-Max-Age:<delta-seconds>;配置一次 preflight 请求的结果能够被缓存多久
Access-Control-Allow-Credentials: true;预检请求响应会返回给发起者(即开发者能拿到
浏览器端使用
options 预检请求
预检内容:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
简单请求可跳过预检
GET、HEAD、POST
Header 中支持自定义集合:Accept、Accept-language、content-language、content-type
Content-Type 仅限: text/plagin,multipart/form-data,application/x-www-form-urlencoded
请求中的任意 XMLHttpRequestUpload 对象没有注册任何监听器
请求中没有使用 ReadableStream
同源策略
cookie
配置 http only,则 js 无法读取
XSS 跨站攻击
策略
服务器对入参过滤或者转码
充分利用 CSP
限制加载其他域下的资源
禁止向第三方域提交数据
禁止执行内联脚本和未授权的脚本
使用 HttpOnly
CSRF 跨站伪造请求攻击
前提
目标站点有 CSRF 漏洞
用户在目标站点为登陆状态
策略
cookie 的 samesite 设置为 strict,第三方服务器不会获取到
验证请求的来源站
小程序
运营
技术
跨端
Taro
动态加载
基于虚拟机
小程序实现
待分类
webComponent
微前端
定义
无技术栈限制
应用独立开发
多应用整合
实现方案
基座模式
single spa
qiankunjs
自组织模式
通过约定进行互调
去中心模式
基于 webpack5 的 module federation
解决方案
iframe
优点:符合微前端需要的所有要求
缺点:上下文无法共享,开发、体验存在问题
web component
核心技术
HTML templates
shadow DOM
custom element
缺点:兼容问题
ESM
缺点:兼容问题
qiankunjs
公共依赖方案
webpack external
webpack 5 module fedetation
Javascript
ES6
Promise
Proxy
Reflect
装饰器
模块
commonjs
循环加载
仅加载已执行部分
esModule
机制
ES6模块遇到模块加载命令import时,不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值。
循环加载
ES module 不关心是否发生了"循环加载",只是生成一个指向被加载模块的引用,需开发者自己保证,真正取值的时候能够取到值。
event loop
微任务
宏任务
Nodejs 和 浏览器的异同
CSS
BFC
应用
margin 重叠(父子、兄弟)
包含浮动元素(清除浮动)
阻止元素被浮动元素覆盖
触发
1. body 根元素
2. 浮动元素:float 除 none 以外的值
3. 绝对定位元素:position (absolute、fixed)
4. display 为 inline-block、table-cells、flex
5. overflow 除了 visible 以外的值 (hidden、auto、scroll)
6. 弹性元素(display 为 flex 或 inline-flex 的元素的直接子元素)
7. 网格元素(display 为 grid 或 inline-grid 的元素的直接子元素)
2. 浮动元素:float 除 none 以外的值
3. 绝对定位元素:position (absolute、fixed)
4. display 为 inline-block、table-cells、flex
5. overflow 除了 visible 以外的值 (hidden、auto、scroll)
6. 弹性元素(display 为 flex 或 inline-flex 的元素的直接子元素)
7. 网格元素(display 为 grid 或 inline-grid 的元素的直接子元素)
样式
position
样式污染
css in js
reactCSS
styled-components
styled-components
css-module
webpack ,配置 css-loader 的 options modules: true。
loader 会用唯一的标识符(identifier)来替换局部选择器。所选择的唯一标识符以模块形式暴露出去。
loader 会用唯一的标识符(identifier)来替换局部选择器。所选择的唯一标识符以模块形式暴露出去。
vue 的 scope
BOM
事件机制
事件冒泡:当前元素逐级冒泡
事件委托
事件捕获:根元素 -> 当前元素
请求
XMLHttpRequest
Fetch
0 条评论
下一页