数据结构
数据结构
逻辑结构:数据之间的逻辑关系
线性结构
- 数组:连续内存空间
- 队列:先进先出
- 链表:非连续、非顺序
- 栈:先进后出,后进先出
非线性结构
- 多维数组
- 广义表
- 树
物理结构:数据的逻辑结构在计算机存储空间的存放形式
冒泡排序
基础冒泡排序:两层循环,如果左边比右边大就换位置
升级冒泡排序:一次把最大的放到右边,第二次最右边的就不参与排序了
快排
快排:找一个数,循环数组,比他小的放左边,比他大的放右边,递归循环
设计模式
设计模式
- 观察者:观察者和被观察者直接的消息交互
- 发布订阅:在以上的基础上,增加了中转站,由发布订阅中心进行消息的交互
- 代理模式:Proxy
- 迭代器:generator
盒模型
- 标准:width 不包括 padding 和 border
- 怪异:包括
Set
Set
- set 内部判断是
===但是NaN是重复的 - 两个对象总是不相等
Array.from()可以把 set 转换为数组
WeakSet
- 成员只能是对象和 Symbol
- 内部的成员,不记入垃圾回收机制
Map
WeakMap
浏览器缓存
浏览器缓存
- 强缓存:通过请求头的字段判断是否需要缓存
Expires:http1.0 的字段,过期时间Cache-controlmaxage:最大有效时间must-revalidate:如果过了时间,必须发送请求nocache:每次都去服务端验证nostore:完全不缓存private:代理服务器不能缓存
- 协商缓存:协商缓存失效以后,去服务端验证缓存,如果命中返回 304
etag:hash 值last-modified
为什么 vue 没有 fiber
react 每次组件更新,不知道哪些子组件有过更新,所有需要所有组件都重新渲染,vue 是响应式的框架,它知道哪些组件发生了变化
useMemo 和 callback
useMemo:缓存数据useCallback:缓存函数,单独使用可能不会产生效果,需要配合 memo 或者 useEffect 等配合使用memo:缓存组件,但是只对参数进行浅层比较
react 优化
React 优化
- PureComponent
- scu
- memo
- 组件、路由懒加载
- Fragment 避免深层 dom 嵌套
- 不要使用内联函数、内联样式
- key
- react18 提出来的
useDeferredValue和useTransition
react 生命周期
React 生命周期
- 16.3 废弃了
componentWillMount、componentWillReceiveProps、componentWillUpdatecomponentWillMount:挂载之前调用,在一些场景下难以调适componentWillReceiveProps:初始化和参数更新调用,可以访问 this,难以调试componentWillUpdate:这个生命周期在组件接收到新的 props 或者 state 变化后,重新渲染之前被调用,只会在更新调用
挂载
constructor:用来执行构造函数初始化 state 或者绑定函数getDerivedStateFromProp:用来根据参数设置 staterender:渲染组件componentDidMount:组件第一次被挂载到 dom 后调用,适合请求数据,初始化插件等,组件更新不会再次触发
更新
getDerivedStateFromProp更新也会执行,在 scu 之前getSnapshotBeforeUpdate:组件更新前调用函数,获取 dom 的快照信息,结合ComponentDidUpdate一起使用,返回值会作为下面方法的第三个参数componentDidUpdate:组件更新后执行,第一次挂载不执行shouldComponentUpdate:根据参数判断组件是否应该更新
卸载
componentWillUnmount
父子兄弟生命周期
- 如果父子都是 class,更新前都是先执行父组件,然后子兄弟组件依次执行,更新后是子兄弟组件依次执行,最后所有子组件更新完执行父组件
- 父组件是 hooks,子组件是 class,useEffect 会在子组件更新完才执行
- 父子组件都是 hooks:先执行子组件
- 子兄弟组件也是 class 先执行,即使是 hooks 在前面
react diff
react diff 算法
- 只会做同级比较
- 组件类型不同,不再继续比较
- 同级比较的时候,仅右移,把左边的往右移动,新元素插入,没有的元素删除
- key 能大大提升性能,并且保证 react 正常运行
fiber 和调度器
任务调度
- 每一帧 16.6ms
- react15 只有协调器和渲染器,react16 增加了调度器
- 调度器实现对不同优先级的任务进行调度,保证高优先级的任务优先执行
- 引入了时间切片的概念,把一个大任务分成多个小任务,当一个任务超过了预定的时间(5ms),就会中断任务把线程让出去渲染页面
- 调度器里面存放任务,关键字段:回调函数、优先级、sortIndex(越小越紧急)
调度器有两个队列
timerQueue:存放未到开始时间的任务,任务进来的时间是开始时间,如果传了延迟时间,开始时间是两个时间的和taskQueue:存放已经到开始时间的任务,过期时间越早的排名越靠前- 调度器每次都是从
taskQueue里面执行最高优先级的任务,每次调度或者特定时间,都会判断timerQueue有没有过期任务,有就挪到taskQueue - 调度器设置了任务优先级(0-5),不同的优先级有不同的超时时间
- 调度器暴露
unstable_scheduleCallback方法,用来创建任务,同时发起一次调度,计算任务的开始时间和超时时间,判断当前任务是否需要马上调度- 任务没到开始时间会调用
requestHostTimeout - 任务到了开始时间会调用
requestHostCallback
- 任务没到开始时间会调用
调度器发起调度的方法
- node 和旧 IE:
localSetImmediate MessageChannel- 调用这个方法以后,监听到消息,会执行任务的
flushWork- 这个函数最终又会调用
workLoop(任务调度循环)workLoop实现了时间切片和 fiber 树可中断- 这个方法调用
taskQueue
- 这个方法调用
- 这个函数最终又会调用
- 调用这个方法以后,监听到消息,会执行任务的
- 都不支持选择
setTimeout因为有 4ms 延迟 - 不用
requestIdleCallback因为兼容性和执行效率
任务调度
浏览器每一帧中的事件循环,从任务的 taskQueue 里面拿出来优先级最高的任务执行
时间切片
在任务调度过程中,每执行完一个任务,判断当前帧是否有剩余时间,如果没有立即终止循环,并判断有没有没有执行完的任务,来判断是否需要发起一次新的任务调度
fiber
调度器注册的任务,就是构建一个 fiber 树的任务,在构建 fiber 单元,每次构建一个,都会判断是否超时,如果超时,就会退出构建,并且标记当前函数未执行完
- 我们知道 fiber 架构的核心就是为了实现在 Scheduler 调度的时候,单个任务内部也可以实现超时中止的判断
- jsx 通过
createElement/jsx.runtime编译成 vdom,vdom 在 render 过程中被构建为 fiber 树。最后在 commit 阶段将 fiber 树整个的渲染到浏览器的页面中
react18
react18
- 自动批处理,(原生事件、promise、定时器)合并处理
- 新增 root api
- 并发渲染器,不同优先级同时渲染
- 不支持 IE 了
useId生成唯一 ID
Redux
Redux
- view 界面,通过 dispatch 发送 action 和 payload
- reducer 函数,通过 action 和 payload 生成新的 store
- 通知 view 进行 render
- 异步用插件,react-saga(dva)
context
- 外层用 provider 包裹,把数据共享到所有子组件
- 缺点:每次 context 更新,所有子组件都会强制被更新,scu 无效,如果数据更新频繁,慎用
Webpack
webpack
打包流程
- 读取配置文件
- 确定入口
- 递归解析所有依赖模块
- 用相应的 loader 编译这些模块
- 打包成 bundle,输出到指定的文件夹内
常见优化
splitChunk:分包
treeShaking:移除未使用的代码,生产环境默认开启了TerserPlugin插件,用来压缩 js 代码(webpack5)mini-css-extract-plugin:单独提取 css 文件,optimize-css-assets-webpack-plugin压缩 csswebpack-bundle-analyzer插件,查看打包分析babel-loaderHardSourceWebpackPlugin:提供模块缓存,提升构建速度(webpack5 废弃了,改用 cache)- 使用
DllPlugin和DllReferencePlugin提前打包固定包,提升构建速度(webpack 改用 cache) IgnorePlugin:忽略不需要的模块,优化打包体积externals:排除某些包,不打包,使用 cdn 引用这些包thread-loader:多进程打包,提升打包速度clean-webpack-plugin:清除上次打包文件Eslint-PluginCompressionPlugingzip 压缩
常用 loader
css-loaderless-loaderscss-loaderpost-cssbabel-loaderfile-loaderurl-loaderassetimg-webpack-loader
Webpack5
webpack5 新特性
- 持久化缓存:把构建结果缓存到磁盘,显著提升打包速度
cache.typecache.cacheDirectory
- 模块联邦:允许多个应用之间共享模块,实现微前端架构
- 资源模块 Asset-module:代替
file-loader和url-loader和raw-loader,简化配置,无需安装额外的 loader - 内置 tree-shaking 的改进,降低产物的大小和代码生成逻辑
- 移除 nodejs 的 polyfill,减少打包体积,如果需要手动额外引入
- 改进算法能力,改进长期缓存
Vite 原理
- 利用现代浏览器支持 ES6 的直接加载,不需要打包整个应用,直接启动一个服务器
- 热更新的时候,也只会编辑改动的文件,不会整个重新编译
Flex
flex 属性
father
flex-direction:设置主轴方向,上下左右,四个属性flex-wrap:设置换不换行flex-flow:上面两个属性的缩写justify-content:主轴对齐方式flex-start:左对齐flex-end:右对齐center:居中space-between:两边挨着,中间平分space-around:项目两侧相等,所有项目两侧是到边框的两倍
align-items:交叉轴对齐方式flex-start:起点对齐flex-end:终点对齐center:居中baseline:第一行文字基准对齐stretch:默认占满整个容器高度
align-content:多根轴线对齐方式,只有一个不起作用
children
order:默认 0,数字越小越靠前flex-grow:默认 0,有剩余空间,等比例放大,0 不放大flex-shrink:默认 1,空间不够的时候,默认缩小,为 0 不缩小flex-basis:默认 auto,或者固定长度,在主轴有空余空间才生效flex:上面三个的缩写align-self:允许子元素有自己的对齐方式
Http
http 状态码
- 301 永久重定向
- 302 临时重定向
- 304 协商缓存
- 400 客户端错误,服务端无法理解
- 403 服务端理解,但是拒绝执行,比如没有权限
- 404 资源未找到
- 500 服务器错误
Promise
promise
then方法接受两个参数,第一个成功回调,第二个失败回调,更建议用 catch 方法,可以监控到成功回调里面错误all:所有成功返回成功,有一个失败返回失败race:有一个返回就会返回allSettled:返回所有的异步结果any:只要有一个成功就是成功,所有失败才是失败
执行顺序
- 带
async关键字的函数会返回一个 promise,没有 await,就是一个普通函数 - 带了
await的,await 右边的会立马执行,await 后面的相当于一个微任务
原理
then方法,创建并返回一个新的 Promise 实例
闭包
闭包
- 闭包就是能够获取其他函数内部变量的函数
- js 链式作用域,父对象的变量无条件暴露给子对象,反之不行
- 闭包让作用域内的变量,一直存储在内存中
- 闭包本身不会造成内存泄漏,不当的使用才会造成
var的变量会提升到顶端声明为undefined,函数会自己提升到顶端
Http 版本
http 版本
- 0.9:最开始的版本,只有一个最基本的 get 请求
- 1.0
- 新增 post 等
- 增加请求头和响应头的概念
- 扩充传输内容,文本、音频、二进制等
- 无状态,无连接,每次请求都建立 tcp
- 1.1
- 长链接,可以保持链接不断开
- 管道化,可以一次多个请求
- 缓存:
cache-control - 增加
delete和put等
- 2.0
- 二进制分帧
- 多路复用
- 头部压缩
- 服务端主动发起消息
三次握手四次挥手
三次握手四次挥手
- 第一次握手:客户端告诉服务端,我要开始连接了
- 第二次握手:服务端说可以的
- 第三次握手:客户端回复可以连接了,tcp 链接完成
- 第三次是为了确认客户端仍然要发送请求,避免服务端资源的浪费
- 第一次挥手:tcp 发出结束
- 第二次挥手:服务端返回确认
- 第三次挥手:服务端返回客户端一个结束
- 第四次挥手:客户端发出确认
react 闭包缺陷
React 闭包缺陷
useEffect,如果第二个参数不传,每次重新渲染都会执行- 是因为存放在链表里面 hooks 的数据,会判断 dep 的值,如果没有不执行函数,如果传入 null 或 undefined 会被判断为 false,就会每次渲染都执行一次
- 产生的原因就是 effect 或者其他 hooks 里面,用到了某个 state,但是没有把 state 放到 dep 里面,导致 state 发生了变化,但是没有执行新传入的函数,导致拿到的还是之前的
js 捕获错误
js 捕获错误
window.onerror:捕获运行时错误,无法捕获资源加载或者异步错误window.addEventListener('error'):可以捕获资源加载错误,需要设置第二个参数为 truetry...catch:捕获代码块同步错误unhandledrejection:捕获 reject 状态,但是没有 catch 的 Promise 错误- react:ErrorBoundaries 捕获组件错误
- vue:errorHandler 全局捕获
- axios 拦截器
useEffect 和 useLayoutEffect 的区别
- 组件挂载更新 →
useLayoutEffect→ 浏览器绘制 →useEffect(异步,所以不一定能准确的获取到 dom) layoutEffect适合调整 dom,但是也因此会阻塞浏览器绘制
hooks 为什么不能写到 if
hooks 为什么不能写到 if 里面
- react 通过链表结构(fiber)来追踪所有 hooks 的数据,所以 hooks 必须按照完全一致的调用顺序去执行,if 就会导致调用顺序不一致
乾坤
乾坤父子应用通信
- 主应用配置的时候,配置 props,可以配置信息和方法
- 基于
InitGlobalState,类似 eventbus - 基于浏览器的
CustomEvent事件,传递消息,类似于发布订阅 localStorage和sessionStorage- 可以直接 props 传递一个 redux 或者 vuex
乾坤隔离
- js 隔离默认用 proxy 代理,如果不支持,就用快照沙箱,加载子应用的时候,复制一个 window 对象,卸载的时候,再恢复
- qiankun 默认动态样式隔离,可以确保单例模式下,子应用之间的样式隔离,没办法保证父子应用和多个子应用之间的样式隔离
- 手动开启 shadow 或者 scoped css
iframe 的缺点
- 性能较差,资源重复加载
- 样式严格隔离,但是难以共享
- 没办法统一路由管理
- 通信麻烦
- 调试困难,还有跨域问题
不常用的 hooks
不常用 hooks
useRef- 每次更改不会引发重新 render,所以 effect 也不会监听到
useImperativeHandle- 需要结合
forwardRef一起使用 - 用来给父组件暴露一些属性和方法
- 父组件配合 ref,可以获取子组件的属性和方法
- 需要结合
useDebugValue:用来调试自定义 hooksuseDeferredValue(react18)- 使用场景:比如一个数据频繁变更,一个需要这个数据的子组件,渲染很慢
- 触发异步渲染
- 当变更数据以后,如果数据频繁变更,每次变更都会触发一个异步的虚拟 dom 渲染,中止上次渲染,直到最后一次变更完成,然后才会同步去渲染页面
useSyncExternalStore:订阅第三方状态管理useTransition(18):- 在 react18 之前,如果开始渲染了,就不能停止了
BFC
定义
块格式化上下文,一个独立的空间,里面的元素不会影响到外面
创建
- 根元素
- 浮动
- 绝对定位
overflow:除了 visible 的其他值display:Flex、table-cell、inline-block
作用
- 解决外边距塌陷
- 清除元素内部浮动的影响
动画
transform:通过指定不同的转换函数(如 translate、scale、rotate、skew 等)来控制元素的外观变化。transition:用于定义元素状态切换时的过渡效果animation:则用于创建完整的动画过程。在实际应用中,开发者可以根据具体需求选择合适的属性来实现所需的动画效果。
css 选择器和优先级
优先级从高到低
- 内联样式
- id 选择器:
#id - 类选择器:
.class - 属性选择器:
a[href="segmentfault.com"]{} - 伪类选择器:
:hover{} - 标签选择器:
span{} - 伪元素选择器:
::before
伪类和伪元素的区别
- 伪类是基于元素本身
- 伪元素通常在元素内容前后添加内容和样式
Vue3 生命周期
onMounted- 组件挂在完之后执行
- 所有子组件已经被挂载(不包含异步或者 suspense)
- 自身的 dom 树已经创建完成并插入到父容器当中
- 可以访问到 dom 元素
onUpdated- dom 更新后调用
- 父组件会在子组件调用后调用
onUnmounted- 卸载后调用
- 所有子组件都卸载后,父组件调用
onBeforeMount- 被挂载之前调用
- 此时还没有创建 dom 节点
onBeforeUpdateonBeforeUnmount- 卸载之前调用
onErrorCaptured- 捕获后代组件传递的错误
onRenderTriggered:仅开发环境,某个元素变动的时候onRenderTracked:仅开发环境,每个元素渲染的时候onActivated:如果组件是 keepAlive 的一部分,当组件被插入到 dom 中onDeactivated:如果组件是 keepAlive 的一部分,当组件被从 dom 移除的时候onServerPrefetch:服务端渲染
Vue3 通信方式
props和$emit- 子组件
expose和父组件ref,用来父组件获取子组件的属性和方法 - 父组件
v-model,子组件emit:update:key和update:value provide和injectvuexeventbus(mitt)
NextTick 的原理
nextTick 的实现原理也是利用事件循环来进行异步操作,然后等 vue 的事件循环结束之后,再执行回调函数。首先大多数情况下,nextTick 会通过 Promise.resolve() 来创建一个成功的 Promise,然后再通过 Promise.then() 来将回调函数添加入微任务队列。同时,nextTick 还设置了状态锁 pending,通过 pending 来判断当前队列当中是否已经存在一个 nextTick 的任务。这样就可以避免多次执行 nextTick 的任务,降低系统资源的使用。
前面我们说过,大多数情况下,我们使用的是 Promise.then() 将回调函数添加入事件队列。但是有些情况,我们无法使用这种方式。假如我们的浏览器不支持 Promise 的话,我们就无法使用 Promise 了。这种情况,我们会选择将 nextTick 加入到宏任务队列。另外,当 nextTick 的回调执行的时候,下一次事件循环还没有执行,这时候我们是获取不到更新渲染之后的数据的,因此,这时候我们会将 nextTick 的回调函数添加到宏任务队列当中。加入宏任务队列的方式有很多种,按照优先级来依次为 setImmediate、MessageChannel、setTimeout。
v-model 和 .sync 的区别
- 都是实现双向绑定的
title.sync=aa等价于:title=aa @update:a="val=>aa=val",子组件调用需要update:a事件,并且 sync 可以写多个v-model=aa等价于:value=aa @input=val=>aa=val,子组件调用 input 方法,v-model 只能有一个- vue3 里面,v-model 合并了 .sync 的方法,取消了 .sync