前端进阶面试大全
2025-08-31 19:09:28 1 举报
AI智能生成
前端导图
作者其他创作
大纲/内容
基础
JS
异步
callback
promise
设计 scheduler
设计 retry
event loop
手写 promise
手写 promise.all
手写 promise.race
promisify
generator
co函数
thunk 函数
async await
手写
手写 call
手写 apply
手写 bind
手写 new
手写防抖
手写节流
手写 instanceof
打乱顺序
shuffle
sort(小于10时是插入排序,否则为快排)
继承
原型链继承
Child.prototype = new Parent()
所有属性会被实例共享,所以引用类型时候会有问题
创建子类实例时,不能传参
构造函数继承(经典继承)
在Child类中 使用call或apply来调用Parent类
避免了引用类型被所有实例共享
可以通过子类向父类传参
不能访问父类的原型上方法和属性
组合继承
组合继承是原型链继承和经典继承的双剑合璧。
父类的构造函数会执行两次
原型式继承
和原型链继承一样,引用类型的属性会被所有的实例共享
寄生式继承
和借用构造函数(经典继承)一样,每次创建对象都会把方法创建一遍
寄生组合继承
理想继承
class 继承
必须要调用 super(),才能继承父类
编译成一个IFFE自执行函数,内部通过 _createClass 对 class 里的方法进行循环处理,其内部通过 Object.defineProperty 将方法挂载到原型上
CSS
权重
早期囿于浏览器厂商采用8字节存储,所以当255个class的权重会超过id选择器
现阶段,采用16字节存储,即CSS的权重比较只存在于同级比较 【权重、权级 的概念】
权重规则:!important > 内联 > id > class > 标签 【权重相等看先后顺序 】
BFC
层叠上下文
常见问题
flex 布局中左边定宽,右边自适应宽度时,右边内容溢出时挤压左边宽度【width: 0】
常见的 flexable 布局中,根元素设置的 font-size 影响子节点空白(inline-block)节点的大小 【height: 0】
fixed
参照物不一定是视窗,如果父元素有 transform / filter / perspective / backdrop-filter / will-change 这样的 CSS 属性(即会创建新的 格式化上下文或图形上下文),那么 fixed 元素的包含块就不再是视口,而是最近的那个具有这些属性的祖先元素
TS
enum
as const
把对象的属性变成只读的
不支持反向映射
如何实现反向映射
freshness(新鲜度概念)
严格的对象字面量类型检查
变量声明空间、类型声明空间
d.ts
如果已有类型推断,比如 window:Window 可以对 Window这个类型进行类型补充
如果没有类型推断,需要赋值给一个变量来进行类型补充
面试常见
interface 、type 的区别
never、unknow、any、void 的区别
泛型
实现内置工具类型
Record
type Record<K extends keyof any, T> = { [P in K]: T}
Partial
type Partial<T> = { [P in keyof T]?: T[P]; }
Required
type Required<T> = { [P in keyof T]-?: T[P]; }
Readonly
type Readonly<T> = { readonly [P in keyof T]: T[P]; }
Pick
type Pick<T, K extends keyof T> = { [P in K]: T[P] }
Exclude(针对于字面量类型)
type Exclude<T, K> = T extends K ? never : T
Extract(针对于字面量类型)
type Extract<T, U> = T extends U ? T : never;
Omit
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
类型体操练习题
插件化机制
ahooks
初始化的时候注册插件,把 service 实例和 params 传给插件,然后在 run 函数内部,通过完整的生命周期来循环调用插件中相关生命周期的方法
vue
通过 vue.use 注册自定义插件,初始化调用插件内 install 方法,并传入 Vue 实例,方便进行 directive、mixins、method、component 等全局挂载
vite-cli
webpack
如何设计一个插件机制
杂项
精度丢失
采用 IEEE754 64位双精度运算规则,1(正负)+11(指数)+52(小数) 0.1+0.2 因为在二进制转换后都是无限循环小数,但尾数部分最多只能存52 位,截断后相加,最后从二进制变成十进制后变成 0.30000000000000004
CORS
Map、WeakMap
WeakMap 只能接受对象作为 key,Map 可以接受任何值作为 key
WeakMap 引用是弱引用,不影响垃圾回收
WeakMap 不能被遍历,Map 可以被遍历
proxy、reflect
框架
Vue3
Feature
源码优化
Monorepo (更好的代码结构,社区的二次开发)
Typescript (开发维护)
性能优化
包体积(移除API、Tree-shaking)
ESM、/*#__PURE__*/
数据劫持 (Proxy 代替 Object.defineProperty)
Reflect
get 第三个参数 receiver 处理 this 指向
has 拦截 in 操作
ownKeys 拦截 for in 操作
合理触发响应【不限于 v3,在新旧值不等情况下才 trigger】
深浅响应
编译优化
Block-Tree
API
Composition
优化逻辑组织
优化逻辑复用
新的变化
vue3.5 依赖收集结构变为双向链表
原理
编译阶段(Template => VNode)
分类
Runtime + Compiler
Runtime only (借助 vue-loader)
过程
parse (Template => AST)
和Vue2不同之处:多了一个虚拟根节点,从而支持 Fragment
transform (AST 转换)
创建 transform 上下文
遍历 AST 节点(核心:处理各种指令、表达式等,并给每种节点打上标签)
静态提升 (每次 render 的时候可以重用之前的 VNode)
创建根代码生成节点
generate (AST => VNode)
VNode => DOM (组件初始化的过程)
响应式
Vue2
Vue3
响应式丢失问题
toRef
toRefs
reactivity
reactive 使用 proxy
ref 对 value 进行 setter
对比React(或者问:为什么Vue不需要fiber架构)
在Vue1中,因为依赖收集了全部数据的watcher,导致项目庞大时候的性能问题
在Vue2中,依赖收集仅仅收集了组件级别的render watcher,和用户设置的 user wacher(computed、watch),而组件内部的更新,是通过引入的虚拟dom的diff来处理,这样就极大减小了 wacher 的监听数量
React 中在 React16 之前通过 diff 对整个dom数进行diff比较,导致页面卡顿
React 16之后采用 fiber 机制来解决页面卡顿问题
diff (组件更新的过程中,新旧节点都是 vnode 数组时)
Vue3 (利用编译阶段的BlockTree,来减少比对数量)
同步头部节点
同步尾部节点
处理剩余节点
添加新增节点
删除多余节点
处理未知子序列
寻找最长递增子序列
Vue2
旧的首节点和新的首节点
旧的尾节点和新的尾节点
旧的首节点和新的尾节点
旧的尾节点和新的首节点
零碎知识点
父子通信
Provide/Inject
props/$emit
Vuex 等类状态管理
$parent/$children
自定义广播
eventBus
$refs
teleport
组件的API形式调用
v-model 语法糖(props为 value,emit 为 input)
.sync 语法糖
nextTick
keep-alive(LRU)
观察者模式、发布-订阅模式
v-bind 的css透传
虚拟根节点
setup
浅层解包
顶层的绑定暴露给模板
生态
Vue-Router
Vuex
pinia
去掉了 vuex 的 namespace ,因为字符串拼接形式不太好做类型推导,采用组合式
自动代码分割,因为store是拆分开的,打包进 chunk 的体积会小
去掉 mutation
不再是 flux 架构
缺陷
typescript 的支持度(组件props的type定义不能引入) 【v3.3 已支持】
语法糖过多
watch的语法糖
废弃的 $ref 等
响应式的解构(toRef,toRefs) 【v3.3 已支持】
React
架构
Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
时间切片(本质还是模拟 requestIdleCallback),使用MessageChannel
优先级划分:根据用户对交互的预期顺序为交互产生的状态更新赋予不同的优先级
技术选型
rIC
在主线程,“不太阻塞”线程(利用空闲时间,可中断,切为 5ms )
通常用于低优先级,比如数据上报等
兼容性差,safari 不支持
rAF
高优先级,每一帧渲染之前都会执行,和 fps 一致
通常用于动画,因为要操作 dom,所以是同步阻塞渲染
web worker
独立后台线程,不占用 js 主线程
无法访问 DOM,因为不在主线程
通常用于复杂计算场景,需要通过 postMessage 来通知到主线程
MessageChannel
让出主线程控制权(利用空闲时间,可中断,切为 5ms )
实现任务优先级
比 setTimeout 及时,没有 4ms 问题
Promise
微任务,阻塞主线程且不适合调度
Reconciler(协调器)—— 负责找出变化的组件(为变化的虚拟DOM打上标记,
当所有组件都完成Reconciler的工作,才会统一交给 Renderer)
当所有组件都完成Reconciler的工作,才会统一交给 Renderer)
React15 中递归处理虚拟DOM
React16 可以中断的循环处理
通过调用 shouldYield 判断当前是否还有剩余时间
Fiber
定义:React 内部实现的一套状态更新机制。支持任务不同优先级,
可中断与恢复,并且恢复后可以复用之前的中间状态
可中断与恢复,并且恢复后可以复用之前的中间状态
架构层面:Fiber 调和器
数据结构:Fiber 结构 类似于链表结构
return (父节点)
child (子节点)
sibling (兄弟节点)
更新任务单元:Fiber 保存了本次更新中该组件改变的状态、要执行的工作
工作原理:双缓存
与JSX 的区别
diff
预设条件
同级比较
不同的类型元素直接替换更新,不比较内部
设置 key 来更大化复用
递增法 —— 找递增子序列 O(m*n)
key 与 index 构成一个Map O(n)
Renderer(渲染器)—— 负责将变化的组件渲染到页面上
API
setState
同步or异步
只有在生命周期函数中、React的合成事件中,才会触发令 isBatchingUpdate 为 true
useEffect 和 useLayoutEffect
修改 DOM ,改变布局就用 useLayoutEffect ,其他情况就用 useEffect 。主要是执行时机不同
都发生在 commit 阶段,
useLayoutEffect 发生在DOM 更新后,绘制前,且是同步,会阻塞绘制,防止用户层面的闪烁;
useEffect 发生在绘制后,已经看到了新的 DOM
useLayoutEffect 发生在DOM 更新后,绘制前,且是同步,会阻塞绘制,防止用户层面的闪烁;
useEffect 发生在绘制后,已经看到了新的 DOM
React.forwardRef
事件系统
合成事件(绑定的事件会冒泡到document处,然后交由专门的处理函数处理)
抹平各浏览器间的兼容问题
跨平台
性能(相当于事件委托)
更新机制
触发时机:this.setState、this.forceUpdate、ReactDOM.render
million.js
采用类似于vue3 block tree 的思想,但侧重于运行时对虚拟dom进行动静区分
RFC
useEvent
forget
solid
低 runtime【无 virtual dom】
细粒度更新【signal 机制来追踪变化】
通过 getter 来进行依赖收集
和vue一样,在对响应式数据解构时可能会丢失其响应式,这点没有react方便,当然,react就没有响应式所以不存在丢失的问题
框架对比
为什么 createSignal 没有类似 hooks 的顺序限制
为什么 useEffect 存在闭包问题
浏览器
页面至少有4个进程
浏览器进程
渲染进程
该域名下的所有页面共享一个
GPU进程
网络进程
插件进程
网络协议
网络层 IP 协议 (IP头:传送到目标主机)
传输层 TCP/UDP 协议(传送到目标应用程序)
TCP头:数据可靠、具有重发机制
三次握手
四次挥手
UDP头:快、不可靠、无重发机制
应用层 HTTP协议
http1.1
连接持久化,减少TCP的连接和断开次数
每个域名下可同时维护6个TCP持久连接
Cookie 和 安全机制
队头阻塞
慢启动
多条TCP连接竞争带宽
https(解决中间人攻击)
在TCP和HTTP之间增加SSL/TLS安全层
作用
对发起HTTP请求的数据进行加密
对接收的HTTP数据进行解密操作
采用非对称+对称加密
浏览器向服务器发送对称加密套件列表、非对称加密套件列表、客户端随机数
服务器向浏览器发送对称加密套件、非对称加密套件、服务端随机数、公钥
浏览器生成新的随机数,并用公钥加密发送给服务器
服务器用自己的私钥对接收的随机数进行解密,并返回确认消息
至此,浏览器和服务器端都有了三个随机数,就可以以此生成对称秘钥
黑客可以在用户计算机上安装假数字证书
存在DNS劫持安全问题(服务器端发送给浏览器端的公钥换成数字证书)
数字证书的申请
网站向 CA 机构提交公钥、公司、站点等信息并等待认证
认证成功后,CA会向网站下发数字证书,包含公钥及公司相关信息
数字证书的认证
验证签名是否可信
浏览器和系统都会内置一些根证书,当浏览器收到服务端发送的数字证书会查看证书链里是否包含根证书,如果没找到,则通过中间证书向上继续找
验证信息是否匹配
比如 证书中包含的域名必须与客户端请求的域名一致
验证证书是否被篡改
浏览器读取证书中相关的明文信息,采用 CA 签名时相同的 Hash 函数来计算并得到信息摘要 A;
利用对应 CA 的公钥解密签名数据,得到信息摘要 B;
对比信息摘要 A 和信息摘要 B,如果一致,则可以确认证书是合法的;
性能短板
SSL/TLS 的会话重用
使用轻量级加密算法
服务器配置专门的加密芯片
CDN加速,相当于负载均衡了
HTTP2来多路复用等特性
http2
每个域名只使用一个TCP长连接,减少慢启动
多路复用(将请求分成一帧一帧的数据去传输)
头部压缩
TCP 的头部阻塞
Server Push
和 web socket 协议的区别
TCP/IP 四层模型
OSI 七层模型
应用层(HTTP、TFTP, FTP, NFS, WAIS、SMTP)主要是终端应用,比如说是FTP(各种文件下载),web浏览器,QQ
表示层(Telnet, Rlogin, SNMP, Gopher)主要是对接收的数据进行解释 加密与解密压缩与解压缩等
会话层(SMTP, DNS)通过传输层(端口号:传输端口与接受端口)建立数据传送的通路
传输层(UDP/TCP)定义了一些传输数据的协议和端口号(www端口 80等)
网络层(IP, ICMP, ARP, RARP, AKP, UUCP)主要从下层接受到的数据进行IP地址(192.168.0.1)的封装与解封装,主要设备是路由器
数据链路层(FDDI, Ethernet, Arpanet, PDN, SLIP, PPP)将物理层接受的数据进行MAC地址(网卡地址)的封装与解除封装,主要设备是交换机
物理层(IEEE 802.1A, IEEE 802.2到IEEE 802.11)定义物理设备的标准,包括网线、wifi、光缆等
安全
同源策略(协议、域名、端口都相同)
CSP,满足安全地引入第三方资源,防止XSS攻击
CORS,满足跨域请求
postMessage,满足跨文档通信
注意:document.domain 在chrome112不可用,只读属性
XSS 跨站脚本攻击
服务器对输入脚本进行过滤或者转码
充分利用CSP
利用 HttpOnly 属性
CSRF 跨站资源伪造(不需要将恶意代码注入用户的页面,利用服务器的漏洞和用户的登录状态来实施攻击)
利用 SameSite 属性
利用 Origin 或者 Referer 属性
CSRF Token
密码加密
好处
HTTPS 因为各种原因失效时,攻击者无法取得明文密码
在 Server 端,没有任何人知道使用者的明文密码
明文密码不会因为人为失误被记录到 log 中
坏处
考虑成本,在已经有HTTPS的情况下,中小公司确实不需要去对密码进行加密
中间人攻击
sign 机制(防中间人篡改)
客户端一般对 token、时间戳、参数 进行 md5 加密,把该 sign 值和时间戳一并传给 server
server 端会一般会把 sign 存在缓存中,防止二次提交
server 端一般会增加一个时间有效期机制,来防止超时提交(防止ddos攻击)
server 端一般会用相同规则进行md5加密和客户端的md5值进行对比验证
https
ddos
sign 数据签名机制中的 时间戳
高防
通过数据中心内的边界路由器对黑名单中的ip等防御策略进行流量清洗
垃圾回收机制
栈内存中数据
利用 ESP(当前执行状态的指针) 的下移,来销毁栈内存中的数据
堆内存中数据(根据代际假说分成两类)
大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存,很快就变得不可访问;(新生代)
副垃圾回收器
不死的对象,会活得更久(旧生代)
主垃圾回收器
算法
栈
队列
链表
删除链表中节点
判断是否是环形链表
链表反转
链表两数相加
删除重复链表
集合
字典
树
BFS(栈)
DFS(递归)
二叉树
前序(根-左-右)
中序(左-根-右)
后序(左-右-根)
排序
冒泡排序:双层循环不断前后对比
复杂度:O(n^2)
复杂度:O(n^2)
选择排序:双层循环每次找到最小的
复杂度:O(n^2)
复杂度:O(n^2)
插入排序:双层循环,从索引1的数字依次向前比较
复杂度:O(n^2)
复杂度:O(n^2)
快速排序:找到一个基准数,进行分区操作,再递归依次处理下去
复杂度:分区O(n) * 递归O(logN) = O(n * logN)
复杂度:分区O(n) * 递归O(logN) = O(n * logN)
二分法
分治
动态规划
回溯
LeetCode HOT100
滑动模块
无重复字符的最长子串
双指针
最长回文子串
三数之和等于0(排序后双指针,考虑重复问题)
栈
寻找两个正序数组的中位数(拆解为两个数组的合并)
有效的括号
链表
链表合并
删除链表的倒数第N个节点(链表反转+删除链表某个节点)
BFS、DFS
括号的生成
字符串
找字串
Sunday
KMP
Node
架构
单线程
事件驱动
非阻塞IO
常用模块
fs
path
url
buffer
child_process
spawn:适用于图片、文件等,返回二进制流
execFile:适用于用第三方包处理东西的时候
exec:适用于用shell命令处理东西的时候
fork:接受一个node文件来创建子进程,并创建一个IPC进程来和主进程通信
cluster
类似于 pm2 的作用,起到进程保护,负载均衡,提高应用的吞吐量和并发能力
web开发
性能优化
输入 URL
浏览器准备工作
对用户输入的URL进行判断,如果是URL,浏览器进程通过IPC(进程间通信)把URL传递给浏览器的网络进程
DNS解析拿到IP
建立TCP连接
因为一些浏览器的TCP并发限制,可能需要等待
从传输层、网络层、数据链路层到物理层 -> NAT网关 -> 目标服务器
全双工机制的三次握手
后端服务器
如果是操作资源:Controller 层会通过 RPC 调用这个组合服务层
如果获取资源:则把相关资源返回给前端
客户端
浏览器网络进程把数据包传给渲染进程
解析HTML,构建DOM树
解析CSS,构建CSS树
渲染树
通过一些 translate3d 开启GPU加速在此起作用
渲染进程通知浏览器进程渲染完毕,停止加载动画
网络层面
通过link标签的dns-prefetch属性对项目中其他域名去预解析
CSS 雪碧图(合并)、base64(file-loader 转换)
base64
base64 是把8bit字节码用6bit字节码,所以体积会至少增加1/3
其实是base65,因为用=进行补足,a-zA-Z0-9+/
一组是4个单元(4x6),可以表达之前3个单元的内容(3x8)
代码压缩
路由懒加载
合理使用图片格式
webp
利用 nginx 进行转发,根据客户端的支持情况来对请求资源通过 location 命中来转发(有时候甚至会通过 lua 来在服务器端创建webp资源)
客户端通过 picture 来处理html里的webp图片,通过嗅针来增加.webp类来处理css文件中的图片
jpg
有损压缩,适用于不需要图片高保真并且图片色彩丰富的(图片比较大)
png(无损压缩)
png8
支持的色彩只有256种,8代表的8 bit,也即 2^8种颜色。索引色透明和 Alpha 透明,常用作logo
png24
24代表的8 bit x 3,也即 2^24 = 16777216 种颜色
png32
相比png24,多了个 Alpha 透明管道
图片显示
懒加载
getBoundingClientRect(性能不好,引起重绘)
IntersectionObserver
预加载
利用 jpg 的Progressive JPEG(渐进式压缩) 配合 http2 ,另外一种是Baseline JPEG(基本线性压缩)
利用 LQIP 来生成一个低质量图片来占位,也有其他方式 SQIP 等技术(利用svg来占位)
视频格式
MP4、OGG、WebM 是封装概念,兼容性看的是编码格式:H264、h265(hevc)
大部分视频转码软件导出来的视频都不支持边下边播,需要使用 ffmpeg 处理
代码层面
组件合理拆分
利用 Template 的 block tree 特性
for 循环唯一 key
合理使用computed(lazy watch)
重绘、回流
class 代替频繁操作 style
批量操作dom
回流【需要重新计算位置,cpu】
width/height/padding/margin/display:none
重绘
color/background/box-shadow/visibility:hidden
合成【gpu】
transform/opacity/filter
transform不一定只会触发合成,主要看是否单独在一个合成层,所以通常使用 will-change: transform/transform3d 来强制一个合成层
渲染顺序,计算样式 -> Layout(回流) -> Paint(重绘) -> Composite(合成)
rAF,执行契机是在绘制前
缓存层面
协商缓存 (304)
ETag
Last-Modified
强缓存(200 from cache)
Cache-Control
Expires
from memory cache(不请求网络资源,资源在内存当中,一般脚本、字体、图片会存在内存当中)
from disk cache(不请求网络资源,在磁盘当中,一般非脚本会存在内存当中,如css等)
渲染层面
HTML 渲染过程特点
顺序执行、并发加载
CSS阻塞
css haed 中阻塞页面的渲染(保证有样式,也就是说页面的渲染会等到 css加载完成)
css 阻塞js执行
css 不阻塞js加载
CSS下载时异步,不会阻塞浏览器构建DOM树
js 阻塞
直接引入的 js 会阻塞页面的渲染
js 不阻塞资源的加载
js 顺序执行,阻塞后续js逻辑的执行
依赖关系
页面渲染依赖css加载
js的执行顺序有依赖关系
js 逻辑对dom节点有依赖关系
js 引入方式
直接引入(阻塞页面渲染)
defer(不阻塞页面渲染,按顺序执行)
async(不阻塞页面渲染,不按顺序执行)
异步动态引入js
策略
css 样式置顶
link 代替 @import
js 脚本置底
合理使用 js 的异步加载能力
打包层面
优化解析时间
thread-loader(创建 worker pool 来多进程地对每个loader处理)
happypack 多进程处理 loader
利用打包缓存
cache-loader
hardSourceWebpackPlugin
优化压缩时间
terser-webpack-plugin 启动多进程压缩优化代码
缩小搜索时间
利用 loader 的test、include、exclude 等配置项
利用 resolve 的 alias、modules、extensions 等配置项
减少请求次数
url-loader 对小图片进行 base64 转换
雪碧图
减少包体积
使用 externals 来避免第三方包打入到 verdor 中
使用懒加载(路由懒加载、组件懒加载)
tree shaking (lodash-es 包)
scope hoisting 作用域提升
html、css、js 压缩
利用浏览器缓存
optimization.splitChunks
miniCss 工具来提取 css 并配置 content-hash
SSR
特点
提高 FCP 性能
一般来说无法提高 TTI,因为需要进行 hydrate
原理
服务器(koa、next 等)根据路由把对应的路由组件 renderToString 成字符串【renderToString不支持流式渲染,renderToPipeableStream/renderToReadableStream】
针对于loadData等挂载在组件上的静态方法,则在server执行一次,并通过window传递给客户端
客户端拿到html后(带有 window 数据,js 文件),执行其中js脚本进行 hydrate,绑定对应的事件
客户端接管后续渲染,执行其生命周期
next
next13 之后增加流式渲染,可以缩短 TTI
SSG/ISR/SSR
ISR 的缓存策略【需要考虑身份不同的缓存】
App Router 和 Page Router
qwik
动机:传统的SSR方案的TTI指标比较差,主要在于为了绑定dom事件等需要重新在客户端run一遍
特点
Resumability:尽可能的延迟下载和执行JS代码
在服务端序列化应用和状态的执行状态,从而在客户端恢复执行
身份转发
React18 的 RSC
如何回退到 CSR
工具指标
lighthouse
FCP 首次渲染时间
TTI 页面可以开始交互的时间
LCP 页面中最大的图片或文本块渲染的时间
SEO
rust 生态
lightingcss
主要是代替postcss,内置 autoprefixer
对于 iconfont 支持度不够,会乱码
biome
swc
工程化
webpack
定位:静态模块打包工具
工作流程
初始化阶段
初始化参数:从配置文件、shell参数、配置对象中读取,与默认配置合并生成最终参数
初始化编译器对象:用上一步参数创建 Compiler 对象
初始化编译环境:包括注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配置的插件等
开始编译:执行 compiler 的 run 方法
确定入口:根据 entry 找出所有的入口文件,调用 compilition.addEntry 将入口文件转换为 dependence 对象
构建阶段
编译模块:根据 entry 对应的 dependence 创建 module 对象,loaders 逐个执行,将对应文件转译为有效模块。
并调用 JS 解释器将其转化为 AST 对象,从中找到该模块依赖的模块,再递归处理。
并调用 JS 解释器将其转化为 AST 对象,从中找到该模块依赖的模块,再递归处理。
完成模块编译:通过模块间的依赖关系,得到依赖关系图
生成阶段
输出资源:根据入口和模块之间的依赖关系,组装一个个包含多个模块的 chunk,再把每个 chunk 转换成一个单独的文件
在生成好输出内容后,根据配置的输出路径和文件名,把文件内容写入到文件系统
自定义 plugin
在执行new Plugin() 的时候执行插件原型上的 apply 方法
通过进行钩子函数的监听来执行不同的操作(compiler.hooks.xxxx.tap)
场景:在 build 的时候生成一个 md 文件,统计打包的文件名和数量
自定义 loader
场景:自定义 md-loader,渲染出 md 文件
名词概念
module(源码中的文件)
chunk(webpack构建时的文件)
bundle(输出后的文件)
entry(编译入口,webpack 编译的入口)
compiler(编译管理器,webpack启动后会创建一个 compiler 对象,该对象一直存在)
plugin(打包优化,资源管理,注入环境变量等)
loader(loader 用于转换某些类型的模块)
tapable(是 webpack 用来创建钩子的库,为 webpack 提供了插件接口的支柱)
hash(所有文件哈希值相同,只要改变内容跟之前的不一致,所有哈希值都改变,没有做到缓存意义)
chunkhash(同一个模块,就算将js和css分离,其哈希值也是相同的,修改一处,js和css哈希值都会变,同hash,没有做到缓存意义)
contenthash(只要文件内容不一样,产生的哈希值就不一样,抽离出的CSS文件可以使用)
模块联邦
场景:微前端的公共模块拆分
vite(bundle less)
启动速度
对于依赖,使用 Esbuild 进行预构建
对于源码,利用浏览器ESM特性,让浏览器接管打包程序的部分工作,Vite 只需要在浏览器请求源码的时候进行转换输出
更新速度
对于源码模块会利用协商缓存
对于依赖模块会利用强缓存
webpack 的HMR还需要重新构建并重载页面,而 vite 因为没有经过 bundle,所以可以只替换编辑的文件即可
为什么生产环境仍然需要打包(默认 rollup 进行生产构建,也是基于 ESM)
嵌套导入带来额外的网络往返
打包可以带来 tree-shaking、懒加载、chunk 分割
缺点
生态不如webpack
生产环境使用 rollup,可能会开发环境、生产环境不同
微前端
定位
技术栈无关
独立开发、独立部署
增量升级
独立运行时
方案选择
iframe
技术成熟
支持页面嵌入
自带沙箱隔离、独立运行
通讯机制孱弱
应用的加载、渲染、缓存可控性不足
页面可以不同域名带来鉴权问题
web component
支持自定义元素
支持 shadow dom
支持模板 template 和插槽 slot
接入时候需要对原项目进行重写
兼容性不好
Single SPA及qiankun
EMP
利用 webpack5 的模块联邦进行模块共享
接入成本高、兼容性差
qiankun
子应用需要支持 script、common.js、AMD 形式引用
webpack 的 output 中,libraryTarget: 'umd'
子应用需要支持CORS
webpack 的 devServer 中,增加 headers: { 'Access-Control-Allow-Origin': '*' }
子应用需要增加声明周期钩子函数供主应用调用
增加 bootstrap、mount、unmount 等钩子函数,并在 mount 中调用 render
防止多个子应用的全局变量冲突
webpack 的 output 中,修改 jsonpFunction 配置一个自定义的名称(webpack5 中已移除)
JS隔离
快照沙箱:性能差,适用于老旧浏览器,只能单实例,即一个页面只能有一个子应用
代理沙箱:可以支持单实例、多实例,利用 proxy 实现
CSS隔离【不仅于qiankun】
css modules
shadow dom
minicss
postcss-selector-namespace
css in js
优势
无全局样式冲突
于js代码一起,容易模块化
方便使用JS变量
缺点
基本都是 runtime,增加运行时性能压力
包体积会大,因为增加运行时代码
调试时候 devtools 会更加复杂,嵌套层级更多
原子化css
tailwind
v1:全量生成后再按需清除:在 build 阶段利用 purgecss 来删除没被使用的 css
v2:利用 JIT 引擎实现按需生成
灵活度不如 unocss,因为 tw 的预设是个固定的字典,而 unocss 是通过正则
scss/sass 的区别
,scss 是sass 的一种实现,node-sass 和 dart-sass 是 scss 的编译器,node-sass 是基于 lib-node(c++)
通信
CustomEvent
props
计算机相关
线程和进程
进程是资源分配的最小单位
线程是CPU调度的最小单位
进程可以比作“火车”,线程可以比较“车厢”,即一个进程可以有多个线程,线程必须在进程中运行。
数据结构
哈希表
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构
Javascript 通常用 Map 结构进行代替,查询时间复杂度为 O(1)
内部还是通过Array来进行数据存储,中间通过对一个hash函数来对key进行转换拿到数组的index
哈希冲突
原因:一般会通过对key进行ASCII编码,再进行10进制转换后,拿到对应的索引,但是容易形成长度很大的数组且里面的数据很少,造成资源浪费,需要用到hash算法(取模散列、斐波那契散列、平方散列)等拿到新的index,这样极大减小创建的数组长度,但会造成哈希冲突,即多个key映射到同一个地址上
方法:通过链式地址【在同一个索引里通过链表来存多条数据】、开放寻址【扩容】来解决
数组
数组(Array)是有序的元素序列。
访问的时间复杂度O(1)、搜索的时间复杂度O(n)
V8
Slow 模式,类似字典形式,在内存中是离散存储
Fast 模式,在内存中是连续存储
二叉树
时间复杂度O(logn)
0 条评论
下一页