Vue的响应式原理用到了什么设计模式,发布订阅跟观察者模式的区别
Vue 的响应式原理核心用到了 观察者模式(Observer Pattern) ,并且借助了一些 发布-订阅模式(Publish-Subscribe) 的思想
Vue 响应式原理用到的设计模式
1 观察者模式(Observer Pattern)
○ Vue 在响应式系统中使用了 数据劫持 + 依赖收集 的机制。
○ 当数据发生变化时,触发 setter ,通知对应的 Watcher (观察者)执行更新。
○ 核心角色 :
■ Subject(被观察者) :响应式数据对象
■ Observer(观察者) :Watcher(组件渲染函数或计算属性)
○ 当依赖发生变化时,所有订阅该依赖的观察者都会收到通知并更新。
2 发布-订阅模式(思想层面)
○ Vue 中的 Dep (依赖管理器)更像一个 中间调度中心 ,收集依赖(Watcher)并在数据变化时统一通知。
○ 这种做法和发布-订阅模式类似: 数据变化 = 发布事件,Watcher = 订阅者 。
发布-订阅模式 vs 观察者模式 区别
| 特性 | 观察者模式 | 发布-订阅模式 |
|---|---|---|
| 依赖关系 | 观察者直接订阅目标对象(Subject),耦合度较高 | 发布者和订阅者 不直接联系 ,通过事件中心解耦 |
| 实现方式 | Subject 维护 Observer 列表,状态变化时逐个通知 | 通过事件总线(Event Bus)或消息队列,发布者只发消息到中介 |
| 场景 | Vue 响应式系统、DOM 事件监听 | Node.js EventEmitter、消息总线、系统解耦 |
| ● 观察者模式: 一对多,直接通知 (Vue 响应式数据 → Watcher) | ||
| ● 发布订阅模式: 一对多,通过第三方调度中心 (发布者 → 中间事件中心 → 订阅者) | ||
| Vue 2 的实现亮点 | ||
| ● 通过 Object.defineProperty 劫持属性,拦截 getter 收集依赖,拦截 setter 通知更新。 | ||
| ● Dep 类作为 调度中心 ,存储当前依赖的 Watcher。 | ||
| ● Watcher 订阅响应式属性,当属性变化时被 Dep 通知。 | ||
| Vue 3 的变化 | ||
| ● 改用 Proxy + Reflect 实现响应式,核心依旧基于观察者模式思想,但细节更灵活。 | ||
| ● 引入 effect 和 依赖追踪(track)/触发(trigger) ,优化依赖收集。 | ||
| 前端开发中经常会用到哪些设计模式?说个具体例子 | ||
| 1 观察者模式(Observer Pattern) : | ||
| ○ 观察者模式用于实现对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 | ||
| ○ 实际开发中,可以用来实现事件监听和订阅-发布机制。比如,在前端框架中,Vue 的数据响应式系统就是基于观察者模式实现的。 | ||
| ○ 示例:DOM 事件的监听和处理、Redux 中的状态管理等。 | ||
| 2 单例模式(Singleton Pattern) : | ||
| ○ 单例模式用于确保一个类只有一个实例,并提供一个全局访问点。 | ||
| ○ 实际开发中,可以用来管理全局状态、资源池、配置对象等。比如,管理全局的数据缓存、路由管理器等。 | ||
| ○ 示例:全局状态管理库(如 Redux、Vuex)、路由管理器(如 React Router、Vue Router)等。 | ||
| 3 工厂模式(Factory Pattern) : | ||
| ○ 工厂模式用于创建对象,但将对象的创建过程封装在工厂类中,客户端无需知道具体的创建细节,只需要通过工厂类来创建对象。 | ||
| ○ 实际开发中,可以用来创建具有相似结构的对象,提高代码的灵活性和可维护性。 | ||
| ○ 示例:创建不同类型的 UI 组件、创建 XMLHttpRequest 对象等。 | ||
| 4 适配器模式(Adapter Pattern) : | ||
| ○ 适配器模式用于解决两个接口不兼容的情况,通过适配器将一个接口转换为另一个接口,使得原本不兼容的类可以协同工作。 | ||
| ○ 实际开发中,可以用来兼容不同版本的接口、封装第三方库等。 | ||
| ○ 示例:兼容不同浏览器的事件处理、封装不同的数据格式转换器等。 | ||
| 5 装饰器模式(Decorator Pattern) : | ||
| ○ 装饰器模式用于在不改变原有对象的结构的情况下,动态地给对象添加额外的功能。 | ||
| ○ 实际开发中,可以用来实现插件化的功能、动态扩展对象的功能等。 | ||
| ○ 示例:Vue 中的 mixin、React 中的 Higher Order Components(HOC)等。 | ||
| 各种http请求头及其作用 | ||
| 1. 通用头(General Headers) | ||
| 用于请求和响应的通用信息。 | ||
| 头字段 | 作用 | 示例 |
| --- | --- | --- |
| Cache-Control | 缓存控制,如是否缓存、缓存时长 | Cache-Control: no-cache |
| Connection | 管理连接方式(保持或关闭) | Connection: keep-alive |
| Date | 请求发送的时间 | Date: Wed, 27 Aug 2025 12:00:00 GMT |
| 客户端向服务器发送请求时附带的信息。 | ||
| (1)客户端信息 | ||
| 头字段 | 作用 | 示例 |
| --- | --- | --- |
| Host | 指定请求的主机名和端口(HTTP/1.1 必须) | Host: www.example.com |
| User-Agent | 客户端信息(浏览器、系统等) | User-Agent: Mozilla/5.0… |
| Referer | 表示请求的来源页面 URL | Referer: https://example.com/page |
| Accept | 告诉服务器客户端能接收的 MIME 类型 | Accept: text/html,application/json |
| Accept-Language | 客户端期望的语言 | Accept-Language: zh-CN, en-US |
| Accept-Encoding | 支持的压缩方式 | Accept-Encoding: gzip, deflate, br |
| 头字段 | 作用 | 示例 |
| --- | --- | --- |
| Authorization | 认证信息,如 Token、Basic Auth | Authorization: Bearer |
| Cookie | 携带客户端的 Cookie,用于会话保持 | Cookie: sessionId=abc123 |
| Origin | 表示请求的来源(跨域时使用) | Origin: https://example.com |
| X-CSRF-Token | 防止 CSRF 攻击的安全令牌 | X-CSRF-Token: abc123 |
| 头字段 | 作用 | 示例 |
| --- | --- | --- |
| If-Modified-Since | 校验资源是否在指定时间后修改过 | If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT |
| If-None-Match | 配合 ETag 校验资源是否变更 | If-None-Match: “etag12345” |
| Pragma | HTTP/1.0 缓存控制,通常 no-cache | Pragma: no-cache |
| 头字段 | 作用 | 示例 |
| --- | --- | --- |
| Content-Type | 请求体的 MIME 类型 | Content-Type: application/json |
| Content-Length | 请求体的字节长度 | Content-Length: 123 |
| 头字段 | 作用 | 示例 |
| --- | --- | --- |
| X-Requested-With | 标识 AJAX 请求 | X-Requested-With: XMLHttpRequest |
| X-Forwarded-For | 记录客户端真实 IP(经过代理时) | X-Forwarded-For: 203.0.113.195 |
| X-Real-IP | 表示客户端真实 IP | X-Real-IP: 203.0.113.195 |
| 头字段 | 作用 | |
| --- | --- | |
| :method | 请求方法(GET、POST 等) | |
| :path | 请求路径 | |
| :authority | 主机和端口 | |
| :scheme | 协议(http/https) | 请求头 面试常问问题总结 |
| ● Content-Type 和 Accept 区别? Content-Type 表示请求体的格式, Accept 表示期望服务器返回的格式。 | ||
| ● If-Modified-Since 和 If-None-Match 区别? 前者基于时间(Last-Modified),后者基于资源标识(ETag),后者更精确。 | ||
| ● Referer 的作用? 表示请求来源页面 URL,用于防盗链、流量分析,但受 Referrer-Policy 控制,HTTPS → HTTP 时可能被移除。 | ||
| 前端当中图片格式各自的使用场景,在项目中如何压缩图片来优化性能 | ||
| 一、前端常见图片格式及使用场景 | ||
| 1. JPEG / JPG | ||
| ● 特点 :有损压缩,体积小,色彩丰富,支持 24 位真彩色,不支持透明。 | ||
| ● 使用场景 : 照片、色彩丰富的背景图 (如 Banner、大图)。 | ||
| ● 优点 :压缩率高,体积小。 | ||
| ● 缺点 :有损压缩导致细节丢失,不适合图标或文字。 | ||
| 2. PNG | ||
| ● 特点 :无损压缩,支持透明(Alpha 通道),支持 8 位、24 位、32 位色。 | ||
| ● 使用场景 : 图标、Logo、透明背景图片 。 | ||
| ● 优点 :清晰度高,支持透明。 | ||
| ● 缺点 :体积大,不适合大图。 | ||
| 3. GIF | ||
| ● 特点 :支持动画,256 色,支持简单透明。 | ||
| ● 使用场景 : 动图、表情包 。 | ||
| ● 优点 :支持动画,兼容性强。 | ||
| ● 缺点 :色彩少,文件较大,效果有限。 | ||
| 4. WebP | ||
| ● 特点 :Google 推出的新格式,支持有损/无损压缩,支持透明和动画。 | ||
| ● 使用场景 : 替代 JPEG / PNG ,特别是在移动端和性能敏感场景。 | ||
| ● 优点 :比 JPEG、PNG 体积更小(30%-80%)。 | ||
| ● 缺点 :部分旧版浏览器不支持(可通过 polyfill)。 | ||
| 5. SVG | ||
| ● 特点 :基于 XML 的矢量图,可无限缩放,代码描述图形。 | ||
| ● 使用场景 : Logo、Icon、简单图形 。 | ||
| ● 优点 :体积小,支持 CSS、JS 操作,可缩放不失真。 | ||
| ● 缺点 :不适合复杂图像(照片)。 | ||
| 6. AVIF | ||
| ● 特点 :新一代图片格式,压缩比优于 WebP,支持透明。 | ||
| ● 使用场景 : 极致性能优化的场景 。 | ||
| ● 优点 :更小体积,图像质量更好。 | ||
| ● 缺点 :浏览器兼容性尚未完全普及。 | ||
| 二、项目中压缩图片优化性能的策略 | ||
| 1. 构建时图片优化 | ||
| ● 使用工具 : | ||
| ○ image-webpack-loader (Webpack 插件) | ||
| ○ gulp-imagemin (Gulp 任务) | ||
| ○ svgo (SVG 压缩) | ||
| ● 做法 : | ||
| ○ JPEG 使用 mozjpeg , PNG 使用 pngquant ,开启压缩选项。 | ||
| ○ 在打包时自动压缩,减少体积。 | ||
| 2. 格式优化 | ||
| ● 优先使用 WebP 或 AVIF 代替 JPEG/PNG(配合 | ||
| 3. 按需加载(懒加载) | ||
| ● 使用 loading=“lazy” 或 Intersection Observer API。 | ||
| 4. 压缩传输 | ||
| ● 开启 Gzip 或 Brotli 压缩 ,减少网络传输体积。 | ||
| 5. CDN 处理 | ||
| ● 使用 CDN 按需裁剪 + 压缩 + WebP 转换 ,比如阿里云 OSS、七牛云。 | ||
| ● 可根据 Accept 头自动返回最佳格式。 | ||
| 6. 运行时压缩(上传前) | ||
| ● 使用 前端压缩库 : | ||
| ○ compressorjs (JPEG 压缩) | ||
| ○ pica (图片缩放) | ||
| ○ browser-image-compression (支持多格式) | ||
| 示例: | ||
| 实践总结 : | ||
| ● 大图:WebP / AVIF 优先,JPEG 次选。 | ||
| ● 图标:SVG 优先,小图用 PNG。 | ||
| ● 动图:用 video 或 Lottie 替代 GIF。 | ||
| ● 构建阶段压缩 + CDN 转码 + 懒加载。 | ||
| interface & type | ||
| 1 合并: | ||
| ○ interface 可以多次声明,会自动合并成一个接口。这使得可以在不同地方扩展同一个接口。 | ||
| ○ type 不能被多次声明合并,如果尝试合并多次相同名称的 type 会导致编译错误。 | ||
| 2 可选属性: | ||
| ○ 在 interface 中,你可以使用 ? 来标记属性为可选。 | ||
| ○ 在 type 中,你可以使用联合类型 | undefined 来标记属性为可选。 | |
| 3 扩展: | ||
| ○ interface 可以扩展其他接口或类,通过 extends 关键字。 | ||
| ○ type 可以使用交叉类型 & 来组合多个类型。 | ||
| 4 类的实现: | ||
| ○ interface 可以被类实现(使用 implements 关键字),以确保类拥有特定的结构。 | ||
| ○ type 不能被类实现。 | ||
| 5 类型声明与赋值: | ||
| ○ interface 可以被用于声明函数、类、变量等,但不能直接赋值给一个变量。 | ||
| ○ type 可以被用于声明函数、类、变量等,也可以直接赋值给一个变量。 | ||
| 6 适用场景: | ||
| ○ 使用 interface 来描述对象的形状、类的实现和拓展,以及合并多个接口。 | ||
| ○ 使用 type 来定义复杂的类型,如联合类型、交叉类型、类型别名等。 | ||
| 说一说websokect | ||
| HTTP/2 服务器推送功能的工作原理是基于客户端对某个资源的请求触发的,服务器根据这个请求主动推送相关资源。这种模式适用于单向通信,即服务器向客户端推送资源,而客户端无法直接向服务器发起推送请求。 | ||
| 在用户相互通信的场景中,通常是需要双向通信的,即客户端与客户端之间或客户端与服务器之间都可以发起通信。而 HTTP/2 的服务器推送功能并不支持客户端向服务器发起推送请求,因此无法满足用户之间的相互通信需求。 | ||
| addevent监听事件。send发送给服务器,最后.close()关闭 | ||
| WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议。与传统的 HTTP 协议不同,WebSocket 允许客户端和服务器之间实时地双向通信,而不需要频繁地发起请求和响应。 | ||
| 以下是 WebSocket 的一些关键特点和用途: | ||
| 1 全双工通信: WebSocket 允许客户端和服务器之间同时进行双向通信,客户端可以发送消息给服务器,服务器也可以主动向客户端推送消息,实现实时性通信。 | ||
| 2 持久连接: WebSocket 连接一旦建立,会保持持久连接,不需要每次通信都重新建立连接,这减少了连接和关闭的开销。 | ||
| 3 低延迟: 由于连接是持久的,消息传递的延迟相对较低,适用于实时应用场景,如聊天应用、实时游戏等。 | ||
| 4 较小的数据帧开销: WebSocket 的数据帧相对于 HTTP 请求和响应的开销要小,因为不需要像 HTTP 那样包含大量的头信息。 | ||
| 5 跨域支持: 与普通的 HTTP 请求不同,WebSocket 允许跨域通信,但仍然需要服务器端支持 CORS。 | ||
| 6 应用场景: WebSocket 适用于需要实时性交互的应用,如在线聊天、多人实时游戏、实时数据监控、推送通知等。 | ||
| setTimeout 不准确的情况: | ||
| 1 事件循环机制: JavaScript 是单线程的,基于事件循环机制。如果在主线程上有其他耗时的任务,会影响 setTimeout 回调的执行。例如,如果在主线程上有大量计算或同步代码,定时器的回调可能会被延迟。 | ||
| 2 最小延迟时间: 浏览器或环境可能对定时器的最小延迟时间进行了限制,通常为 4 毫秒。即使你设置更短的延迟,定时器也会被约束在这个最小值。 | ||
| 3 页面隐藏或后台运行: 如果页面被切换到后台、最小化或者浏览器标签被隐藏,浏览器可能会降低对定时器的处理优先级,导致定时器执行的不准确。 | ||
| 4 系统资源限制: 定时器的准确性还受系统资源限制的影响,包括 CPU 使用率、内存等。 | ||
| 协商缓存和强缓存的使用场景: | ||
| ● 强缓存使用场景: 强缓存用于在一段时间内完全避免与服务器通信,直接使用本地缓存。这对于静态资源(如图片、CSS、JS 文件等)非常有用,因为它们通常不会频繁变化。 Cache-Control 和 Expires 都可以用来设置强缓存。 | ||
| ● 协商缓存使用场景: 协商缓存用于在资源可能发生变化时,检查是否需要获取更新的资源。这通过向服务器发送请求并使用 If-None-Match (基于 ETag)或 If-Modified-Since (基于 Last-Modified 时间)头部来实现。如果服务器返回状态码为 304(Not Modified),则表示客户端的缓存仍然有效。协商缓存通常适用于一些可能会变动但不频繁变动的资源,如网页内容。 | ||
| 1 Cache-Control 和 Expires 的作用: | ||
| ○ Cache-Control :这是一个现代的、相对较为灵活的头部字段,用于控制缓存的行为。它可以指定缓存的最大存储时间( max-age ),是否允许缓存被重新验证( must-revalidate ),是否允许共享缓存( public 或 private ),是否允许缓存的内容被修改( immutable ),等等。 | ||
| ○ Expires :这是一个较早的头部字段,指定了资源的过期时间。服务器通过返回的时间戳告诉客户端,资源将在这个时间之后过期,从而客户端可以决定是否继续使用缓存或从服务器获取新资源。 | ||
| 2 为什么同时使用这两个字段: | ||
| ○ Cache-Control 为了提供更精细的缓存控制,可以根据各种情况指定不同的策略,比如设置不同的 max-age 、 no-cache 、 no-store 等。它的优势是可以更灵活地控制缓存的行为。 | ||
| ○ Expires 可以提供一个绝对的过期时间点,相对来说更简单。但它的一个问题是,如果客户端和服务器时间不同步,可能会导致缓存无法正常工作。因此, Cache-Control 更被推荐使用,特别是在现代的网络环境中。 | ||
| qiankun的js隔离 | ||
| 它是把 js 代码包裹了一层 function,然后再把内部的 window 用 Proxy 包一层,这样内部的代码就被完全隔离了,这样就实现了一个 JS 沙箱。 | ||
| 原理就是 function 包裹了一层,所以代码放在了单独作用域跑,又用 with 修改了 window,所以 window 也被隔离了。 | ||
| 三个沙箱:快照、单应用代理,多应用代理 | ||
| 快照沙箱:在沙箱激活的时候记录window当时的状态(也称为快照) | ||
| ● 恢复上一次沙箱失活时记录的沙箱运行过程中对window做的状态改变,也就是上一次沙箱激活后对window做了哪些改变,现在也保持一样的改变。 | ||
| ● 在沙箱失活的时候: | ||
| ● 记录window上有哪些状态发生了变化(沙箱自激活开始,到失活的这段时间); | ||
| ● 清除沙箱在激活之后在window上改变的状态,从代码可以看出,就是让window此时的属性状态和刚激活时候的window的属性状态进行对比,不同的属性状态就以快照为准,恢复到未改变之前的状态。 | ||
| ● 两个问题,一个会污染window,无法多应用,会出现状态紊乱,另一个是需要通过for in去遍历window的所有属性,所以慢。 | ||
| 单应用代理沙箱通过三个变量来记住沙箱激活后window发生变化过的所有属性,这样在后续的状态还原时候就不再需要遍历window的所有属性来进行对比,提升了程序运行的性能。但是这仍然改变不了这种机制仍然污染了window的状态的事实,因此也就无法承担起同时支持多个微应用运行的任务。 | ||
| 多应用用了proxy解决这个问题 | ||
| 微前端原理,怎么实现js隔离、样式隔离。 | ||
| 那微前端怎么实现呢? | ||
| 其实也简单,一句话就可以说明白: 当路由切换的时候,去下载对应应用的代码,然后跑在容器里。 | ||
| single-spa 可以根据 URL 路由加载对应的子应用。这样用户访问不同的路由时,只需要加载需要的子应用 | ||
| qiankun 做了样式隔离,有 shadow dom 和 scoped 两种方案,但都有问题: | ||
| ● shadow dom 自带样式隔离,但是 shadow dom 内的样式和外界互不影响,导致挂在弹窗的样式会加不上。父应用也没法设置子应用的样式。 | ||
| ● scoped 的方案是给选择器加了一个 data-qiankun=‘应用名’ 的选择器,这样父应用能设置子应用样式,这样能隔离样式,但是同样有挂在 body 的弹窗样式设置不上的问题,因为 qiankun 的 scoped 不支持全局样式 | ||
| 而 react 和 vue 项目本身都会用 scoped css 或者 css modules 的组件级别样式隔离方案,这俩方案都支持传递样式给子元素、设置全局样式等,只是实现和使用方式不同。 | ||
| 现在的 vue、react 项目基本都做了组件样式隔离了,有点全局样式也是可控的,真没必要用 qiankun 的那个。 | ||
| 页面1点击歌曲a,打开页面2播放歌曲a,然后页面1更换歌曲b,页面2更换播放歌曲b。如何实现? | ||
| 页面1携带参数请求分页第三页数据,用户一进来就点击请求第2页,第三页数据请求比较慢,导致先看到第2页的数据,然后第三页的数据请求完后又看到第三页的数据。如何解决? | ||
| 如何取消请求 | ||
| git代码冲突解决 | ||
| 1 使用 git status 检查冲突: 在冲突发生后,运行 git status 查看冲突文件。 | ||
| 2 编辑冲突文件: 打开冲突文件,你会看到类似以下的内容: | ||
| 在 <<<<<<< HEAD 和 >>>>>>> branch-name 之间,是你的更改和对方(其他分支)的更改。你需要手动编辑这些内容,保留你认为正确的部分。 | ||
| 3 解决冲突: 编辑文件,删除多余的标记(如 <<<<<<< 、 ======= 和 >>>>>>> ),只保留你认为正确的代码。 | ||
| 4 添加和提交解决后的文件: 在解决冲突后,将文件添加到暂存区并提交: | ||
| 5 推送到远程仓库: 如果你的冲突解决是在本地进行的,你需要将解决后的代码推送到远程仓库: | ||
| 如何确保websocket连接性 | ||
| 微前端解决了什么问题(增量升级) | ||
| 微前端的实现方式 | ||
| react调度器怎么判断事件优先级 | ||
| 实现一个轮播图组件 | ||
| 获取dom的原生方法,怎么创建dom元素和修改元素 | ||
| 手写遍历dom所有节点,返回标签名 |
2个页面之间通信的方法
1 URL 参数传递:可以通过 URL 的查询字符串(query string)传递参数,例如 http://example.com/page2?param1=value1¶m2=value2。在第一个页面中可以通过链接或 JavaScript 跳转到第二个页面,并在链接或 JavaScript 中添加查询参数。在第二个页面中可以通过解析 URL 的查询参数来获取参数值。
2 Local Storage:可以使用 HTML5 的 Local Storage API 在浏览器中存储一些数据,这些数据可以被同一域名下的其它页面访问到。在第一个页面中可以将数据存储到 Local Storage 中,然后在第二个页面中读取该数据。
3 Session Storage:与 Local Storage 类似,Session Storage 也是 HTML5 提供的 API ,不同的是 Session Storage 中保存的数据只能在同一个会话(session)中共享,如果关闭浏览器窗口或标签页,数据就会被清除。在第一个页面中可以将数据存储到 Session Storage 中,然后在第二个页面中读取该数据。
4 Cookies:可以使用 Cookies 在浏览器中存储一些数据,这些数据可以被同一域名下的其它页面访问到。在第一个页面中可以设置一个 Cookie,然后在第二个页面中读取该 Cookie。
5 postMessage API:可以使用 postMessage API 在两个页面之间进行跨域通信。在第一个页面中可以调用 window.postMessage() 方法发送一条消息,然后在第二个页面中通过监听 message 事件来接收该消息。
聊重构收益
迁移后项目整体功能应与迁移前保持一致,但依赖的组件包、打包模式、内部逻辑的部分写法等会进行升级重构。
新项目将不会使用kfx静态部署,因为kfx不支持ssr,暂时还使用老的部署流水线,后续升级为ssr流水线。即使在老的流水线下,webpack迁移至vite后,流水线整体时间也降低了 50% 。
wbp的流程
Webpack是一个现代的前端构建工具,它的主要功能是将多个模块(JavaScript、CSS、图片等)打包成一个或多个静态资源文件。下面是Webpack的基本流程:
1 入口(Entry):指定Webpack开始构建的入口文件。Webpack根据入口文件来构建依赖图谱,找到所有需要打包的模块。
2 模块解析(Module Resolution):Webpack根据入口文件中的依赖关系,逐步解析模块之间的依赖关系。它会根据配置中的规则,确定如何解析不同类型的模块,例如JavaScript、CSS、图片等。
3 加载器(Loaders):对于非JavaScript类型的模块,Webpack使用加载器来处理它们。加载器可以将这些模块转换成JavaScript模块,或者在加载前对它们进行处理。例如,可以使用Babel加载器将ES6语法转换为ES5语法,或使用CSS加载器处理CSS文件。
4 代码转换(Transformation):Webpack将通过加载器获取到的模块进行代码转换。这些转换可以包括语法转换、压缩、优化等。例如,使用Babel转换器将ES6+代码转换为兼容性更好的ES5代码。
5 模块打包(Module Bundling):在解析和转换完成后,Webpack将所有模块打包成一个或多个静态资源文件。这些文件包含了经过处理的模块代码和其他资源文件,如CSS、图片等。
6 代码分割(Code Splitting):Webpack支持将代码分割成多个块(chunks),以优化加载速度。可以根据配置和需求,将代码分割成不同的块,使得按需加载成为可能。
7 资源输出(Output):Webpack根据配置指定输出的目录和文件名,将打包好的静态资源文件输出到指定位置。这些输出文件可以被部署到服务器上,供浏览器加载和使用。
8 插件(Plugins):Webpack的插件系统可以执行更广泛的任务,如代码压缩、资源优化、生成HTML文件等。插件可以在Webpack构建过程的不同阶段介入,对打包过程进行定制和扩展。
spa是什么
SPA(Single Page Application,单页应用)是一种应用程序或网站的设计模式,其特点是在单个 HTML 页面上加载所有必要的代码(如 HTML、CSS、JavaScript),并根据用户与应用的交互动态更新页面内容。相对于传统的多页面应用,SPA 通过减少页面加载次数和服务器请求,提高了用户体验和应用性能。
SPA 的优点包括:
● 更快的页面加载速度:只需加载一次 HTML、CSS 和 JavaScript,后续页面跳转时只需要通过 AJAX 请求获取数据并更新页面内容。
● 更好的用户体验:页面间切换无需重新加载,提供了类似于桌面应用的体验。
● 减轻服务器负担:客户端负责渲染页面,服务器只需提供数据。
SPA 的缺点包括:
● 首次加载时间较长:需要加载整个应用的代码和资源。
● 不利于 SEO:由于 SPA 的内容是动态生成的,搜索引擎可能无法正确索引页面内容。
● 框架依赖:通常需要使用前端框架(如 Angular、React 或 Vue)来实现 SPA。
hash和history的原理
hash 和 history 都是实现前端路由的技术。前端路由使得 SPA 可以在不请求服务器的情况下实现页面内容的更新。
hash 是 URL 中 # 后面的字符串,它的变化不会导致浏览器向服务器发送请求。前端路由通过监听 hashchange 事件,根据 hash 的变化来动态更新页面内容。例如,URL http://example.com/#/home 和 http://example.com/#/about 其中的 #/home 和 #/about 是 hash 值,当 hash 值改变时,前端路由会根据新的 hash 值加载对应的组件。
history 是 HTML5 中新增的一个 API,它允许开发者在不触发页面刷新的情况下修改浏览器的历史记录。前端路由通过监听 popstate 事件,根据浏览器历史记录的变化来动态更新页面内容。与 hash 相比,history 能够实现更友好的 URL。
history 模式,是利用了 HTML5 中 history 的 API,history.pushState 和 history.replaceState 这两个方法,可以在不刷新页面的情况下,操作浏览器的历史记录,前者为新增一条记录,后者为替换最后一条记录。同时通过监听 popState 事件或注册 onpopstate 回调函数来监听 url 的变化。
浏览器引擎的种类
浏览器引擎(也称为浏览器内核)负责解析网页内容并呈现给用户。以下是一些主要的浏览器引擎:
WebKit: WebKit 是一个开源的浏览器引擎,由苹果公司开发。它是 Safari 浏览器的内核,同时也被很多其他浏览器所采用。
Blink: Blink 是一个由谷歌公司开发的开源浏览器引擎,它是 Chrome、Edge 和 Opera 等浏览器的内核。Blink 是从 WebKit 分离出来的,因此与 WebKit 有很多相似之处。
Gecko: Gecko 是 Mozilla 基金会开发的开源浏览器引擎,它是 Firefox 浏览器的内核。
Trident: Trident 是微软开发的浏览器引擎,它曾经是 Internet Explorer 的内核。在微软推出基于 Blink 内核的新版 Edge 浏览器后,Trident 的使用逐渐减少。
静态资源怎么储存
静态资源(如 HTML、CSS、JavaScript、图片等)是不需要服务器动态生成的文件。静态资源存储的方式有很多种,以下是一些常见的方法:
文件系统: 静态资源可以直接存储在服务器的文件系统中。当服务器收到请求时,它会根据请求的 URL 查找对应的静态文件并返回给客户端。
CDN: CDN(Content Delivery Network,内容分发网络)是一种分布式网络,它可以将静态资源缓存到离用户较近的服务器上。这样,当用户请求静态资源时,CDN 会从离用户最近的服务器上提供资源,从而减少延迟和提高加载速度。
对象存储: 对象存储是一种云存储服务,它允许开发者将静态资源上传到云端,并通过 URL 访问这些资源。对象存储的优点包括高可用性、可扩展性和低成本。常见的对象存储服务有 Amazon S3、Google Cloud Storage 和 Microsoft Azure Blob Storage 等。
缓存: 缓存是一种存储技术,它可以将常用的静态资源存储在客户端(如浏览器缓存)或者服务器端(如代理服务器缓存、Redis 等)。通过使用缓存,静态资源的加载速度可以得到显著提升,同时还可以减轻服务器的负担。
纯函数
1 长列表渲染实现
在上述代码中,我们通过创建一个容器元素来显示数据列表,并通过监听容器元素的滚动事件来动态更新可见区域的数据。同时,我们还需要根据可见区域的高度和滚动距离计算出当前可见区域的起始和结束元素下标,以便只渲染当前可见区域的数据。
长列表渲染和虚拟滚动都是在滚动时只渲染当前可见区域的数据,从而避免将整个列表都渲染出来,从而提高性能。其中,长列表渲染通常是通过设置容器元素的高度和滚动事件来实现的,而虚拟滚动则是将整个列表分成若干个块,只渲染当前可见的块,并且在滚动时动态更新块的内容。
懒加载则是一种在用户需要时才加载数据的技术,可以用于优化页面加载速度。例如,在图片展示中,可以只加载当前可见区域的图片,而不是将整个页面中的所有图片都加载出来。
虚拟列表和懒加载可以结合使用,例如在一个虚拟滚动的长列表中,可以采用懒加载的方式,只在用户滚动到一定区域时才加载该区域的数据,从而避免一次性加载所有数据时造成的性能问题。这种方式可以在提高性能的同时,也可以节省带宽和资源开销。
2 关键渲染路径
html,css,
3 axios封装
4 数据结构有哪些
数组,链表,队列、堆,栈,树,图,散列表
5 复杂度分析
6 红绿灯组件
某个路口的红绿灯,会按照红灯亮5s,黄灯亮2s,绿灯亮3s这样的顺序无限循环。要求:每一秒打印当前在亮的灯。
● 说一下响应式布局
● 现在有四个盒子,要实现在可见区域较大的时候一列排列,在可见区域较小的时候两列排列
● 媒体查询
● 怎么实现字体大小随页面缩放 —设置根元素字体大小 然后底下rem 根用媒体查询
H5 适配方案
UI 库自带、flex布局
sass / less 中的自定义函数 pxToRem
另外一种是直接写 px ,编译过程利用插件全部转成 rem 。这样 dom 中元素的大小,就会随屏幕宽度变化而变化了。
在 vue-cli3 中装 postcss-pxtorem 插件就可以了,其他平台也是大致差不多的思路。
吃透移动端 1px
归根结底有两种方案,一种是利用 css 中的 transfrom:scaleY(0.5) ,另一种是设置 媒体查询根据不同 DPR 缩放
利用 css 的 伪元素::after + transfrom 进行缩放
为什么用伪元素? 因为伪元素::after 或::before 是独立于当前元素,可以单独对其缩放而不影响元素本身的缩放
职业规划
为什么想来XX?平台大,希望学习到技术,对技术热爱。规划是什么?第一年向前辈们学习,能做好自己的事情;三年后在技术深度和广度上都有发展,能独立完成较复杂的业务需求;五年后不局限于技术,而且有一种项目管理的思维,能带项目。有考公考研的打算吗?没有,我大二时便做好就业的规划,打算本科就业。什么时候到岗?五一后。能实习多久?长期实习。能接受加班吗?接受加班,但不接受无意义的内卷。
● 首先表示考虑过这个问题(有规划),如何谈一谈自己的现状(结合实际).
● 接着从工作本身出发,谈谈自己会如何出色完成本职工作,如何对团队贡献、如何帮助带领团队其他成员创造更多的价值、如何帮助团队扩大影响力.
● 最后从学习出发,谈谈自己会如何精进领域知识、如何通过提升自己专业能力,如何反哺团队.
react 不可变数据
在React中,不可变数据(Immutable Data)是指数据一旦创建后,就不能再被直接修改或改变其值。任何对不可变数据的修改都会返回一个新的数据副本,而不是在原有数据上进行修改。
React鼓励使用不可变数据的概念,因为它带来了一些重要的优势:
1 性能优化: 使用不可变数据可以帮助React更高效地进行虚拟DOM的比较,从而减少不必要的渲染操作,提升性能。
2 跟踪变化: 在React中,通过对比前后两个不同的数据副本,React可以轻松地跟踪数据的变化,从而在需要时精确更新DOM。
3 数据稳定性: 不可变数据可以确保数据在多个组件之间传递时保持稳定,不被意外修改。
4 撤销与重做: 不可变数据可以方便地实现撤销与重做功能,因为你可以简单地保存历史数据副本。
在JavaScript中,可以通过多种方式实现不可变数据,比如使用Object.assign、展开运算符(…)、数组的map和filter方法等。此外,还可以使用第三方的不可变数据库(如Immutable.js)来处理不可变数据。
示例代码,展示了如何使用展开运算符来创建新的不可变数据:
浏览器缓存
浏览器缓存就是将已经请求过的资源存储在本地上,然后再次请求的时候就可以直接从本地获取,这样可以节省服务器的资源而且可以加快页面加载的速度
在 服务器端设置响应头的Cache-Control和Expires字段来控制浏览器缓存。
css省略号
text-overflow、white-space
white-space属性用于控制元素内文本的空格和换行处理方式。
text-overflow属性用于控制元素内容超出部分的处理方式。
pnpm跟npm和yarn最主要的区别
幽灵依赖捏
幽灵依赖是指在package.json文件中没有直接声明,但在项目中被使用的依赖。这种情况通常发生在项目中使用了另一个依赖,而该依赖本身又依赖于某些其他的依赖,但在项目中并没有直接使用这些依赖的情况下。由于这些依赖没有在package.json文件中声明,在使用PNPM等包管理工具时可能会被视为无用依赖而被删除,导致项目无法正常运行。
依 赖安装方式
NPM和Yarn都会在node_modules目录下为每个包单独安装一份依赖,而PNPM则会共享依赖,将所有包的依赖都安装在全局目录中,并通过符号链接的方式链接到各个包中。这种方式可以大大减少磁盘空间的占用,加快依赖的安装速度。
安装速度
PNPM相对于NPM和Yarn来说,会更快一些,因为它能够更高效地利用磁盘空间和缓存,减少重复下载和解压依赖的时间。
对于本地包的支持
npm会将本地包的完整拷贝放置在项目的node_modules目录下。而PNPM通过符号链接的方式将本地包链接到项目中,只需要在全局目录中安装一次本地包,就可以在多个项目中共享使用。
PNPM相对于NPM和Yarn来说,更容易支持本地包。PNPM支持将已经存在于本地文件系统中的包链接到项目中,这些包可以是已经存在于其他项目中的,或者是手动下载的。这种方式可以方便地进行本地包的开发和测试。
PNPM在处理本地包依赖关系时,可以自动检测并安装本地包所依赖的其他包,而NPM需要手动将本地包的依赖添加到package.json文件中。
css方案
总体来说,如果是进行基础组件的开发,那么使用“有规范约束”的原生css(比如遵守BEM规范的css),或者less之类的预处理语言会比较合适,这能最大幅度地减小组件库的体积,也能为业务方提供样式覆盖的能力。
如果是进行业务开发,个人比较推荐css-in-js的方案,因为它不仅能够做到在组件中直接编写css,同时也能够直接使用组件中的js变量,能有效解决“组件样式随着数据变化”的问题。另外,在业务开发中,由于迭代速度快,开发人员流动性相对大一些,我们直接使用规范对css进行约束会有一定的风险,当项目规模逐渐变大后代码的可读性会很差,也会出现css互相影响的情况。
另外使用CSS Module在业务开发中也是一种不错的方案,但一般大部分前端开发者会使用“ - ”来为样式命名,但放到组件中就只能使用 style[‘form-item’] 这样的方式去引用了,我个人是不太喜欢这种风格的写法的。
不过没有一项技术是能解决全部问题的,针对不同的场景选择最合适的技术才是最优解。如果团队中还未使用这些技术,并且在开发组件的样式时遇到了文中所述的“传统css在组件开发中的痛点”,那么我建议去尝试一下css-in-js或者css module,具体选择何种方案就看团队成员更能接受哪种写法了。如果已经使用了css-in-js或者css module,那么继续使用即可,这些都是能够cover现有组件开发的场景的。
V8 垃圾回收机制
副垃圾回收器对半分为两个区域对象区域和空闲区域,通过标记清除算法把存活的对象复制到空闲的区域,然后进行角色翻转,如果两次存活就会被置入老生区
主GC: 标记 - 整理(Mark-Compact) ,这个标记过程仍然与标记 - 清除算法里的是一样的,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
全停顿
由于 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来, 待垃圾回收完毕后再恢复脚本执行 。我们把这种行为叫做 全停顿 (Stop-The-World)。
为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为 增量标记(Incremental Marking)算法 。
使用增量标记算法,可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样当执行动画效果时,就不会让用户因为垃圾回收任务而感受到页面的卡顿了。
1 分代垃圾回收:V8使用了一种分代垃圾回收机制,将内存分为新生代和老生代两个区域。新生代区域用于存储新创建的对象,老生代区域则用于存储长期存在的对象。由于新生代区域的对象很快就会被释放,因此可以采用更轻量级的垃圾回收策略,而老生代区域则需要采用更严格的垃圾回收策略,以确保内存不会被过多占用。
2 标记-清除算法:V8使用一种标记-清除算法来进行垃圾回收。在标记阶段,V8会标记所有仍然活跃的对象,并对它们进行标记。在清除阶段,V8会清除所有未被标记的对象,并将它们的内存空间释放回系统。
3 内存压缩:在老生代区域中,V8还使用了一种内存压缩技术,将不连续的内存空间合并为连续的内存块。这样可以避免内存碎片的产生,提高内存使用效率。
V8 中有两个垃圾收集器。主要的 GC 使用 Mark-Compact (标记整理)垃圾回收算法,从整个堆中收集垃圾。小型 GC 使用 Scavenger 垃圾回收算法,收集新生代垃圾。
Scavenge算法的工作过程如下:
1 将堆内存划分为两个区域:From空间和To空间。From空间用于存放当前正在使用的对象,To空间是空闲的。
2 当一个对象被创建时,它会被分配到From空间。
3 当From空间快要满时,就会触发垃圾回收。这时候会执行Scavenge算法。
4 Scavenge算法的过程是将From空间中的存活对象复制到To空间,同时将非存活对象清理掉。
5 完成复制后,From空间和To空间的角色会互换,成为新的From空间和To空间。
由于新生代中的对象生命周期短,大多数对象都是垃圾,因此Scavenge算法能够快速清理掉非存活对象,并将存活对象复制到新的空间,从而大大减少了堆内存的碎片化。
两种不同的算法应对不同的场景:
● 使用 Scavenger 算法主要处理 存活周期短 的对象中的可访问对象。
● 使用 Mark-Compact 算法主要处理 存活周期长 的对象中的不可访问的对象。
因为新生代中存活的可访问对象占少数,老生代中的不可访问对象占少数,所以这两种回收算法配合使用十分高效。
介绍hooks
在函数组件中,可以使用 useState 来定义函数组件的状态。使用 useState 来创建状态
● 接收一个参数作为初始值
● 返回一个数组,第一个值为状态,第二个值为改变状态的函数
useEffect 又称副作用 hooks 。作用:给没有生命周期的组件,添加结束渲染的信号。执行时机:在渲染结束之后执行 layouteff是渲染结束之前
使用 useMemo 可以传递一个创建函数和依赖项 复杂计算逻辑的优化
useMemo 对比。
● 可以简单这样看作, useMemo(() ⇒ Fn,deps) 相当于 useCallback(Fn,deps)
useCallback 是对传过来的回调函数优化,返回的是一个函数; useMemo 返回值可以是任何,函数,对象等都可以
useRef 就是返回一个子元素索引,此索引在整个生命周期中保持不变。作用也就是:长久保存数据。注意事项,保存的对象发生改变,不通知。属性变更不会重新渲染
Fiber 实现原理
Fiber 把一个渲染任务分解为多个渲染任务,而不是一次性完成,把每一个分割得很细的任务视作一个”执行单元”,React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去,故任务会被分散到多个帧里面,中间可以返回至主进程控制执行其他任务,最终实现更流畅的用户体验。
即是实现了”增量渲染”,实现了可中断与恢复,恢复后也可以复用之前的中间状态,并给不同的任务赋予不同的优先级,其中每个任务更新单元为 React Element 对应的 Fiber 节点。
实现的方式是requestIdleCallback这一 API,但 React 团队 polyfill 了这个 API,使其对比原生的浏览器兼容性更好且拓展了特性。
React团队polyfill了requestIdleCallback API,并将其命名为requestIdleCallbackPolyfill,在原生API的基础上做了一些拓展,增加了以下几个特性:
1 支持快速回调:原生的requestIdleCallback API中,回调函数只会在空闲时间内执行一次。而React团队的polyfill增加了一个名为startRendering的方法,用于在需要立即执行回调函数时,使其 立即 执行 。
2 支持 取消 回调:React团队的polyfill增加了一个名为cancelIdleCallback的方法,用于取消尚未执行的回调函数。
3 支持多个回调函数:React团队的polyfill增加了一个名为 requestIdleCallbackUntil 的方法,用于在空闲时间内执行多个回调函数,直到某个回调函数返回false为止。
4 支持超时:React团队的polyfill增加了一个名为 requestIdleCallbackWithTimeout 的方法,用于在空闲时间内执行回调函数,并设置一个超时时间,在超时时间内未能执行完回调函数时,自动取消回调函数的执行。
这些拓展特性使得React团队的requestIdleCallback polyfill更加实用和方便,可以在实际开发中更加灵活地应用空闲时间执行一些任务,例如后台数据同步、图片加载等。同时,React团队的polyfill也兼容了原生API的所有主要浏览器,并提供了更加稳定和可靠的性能表现。
js 数据类型
分为基本数据类型和引用数据类型
基本数据类型存储在栈中,被引用或拷贝时,会创建一个完全相等的变量,因为占据空间小、大小固定、频繁被使用的数据,所以被放入栈中存储
引用数据类型存储在堆内存中,存储的是地址,占据空间大,大小不固定,应用类型在栈中存了指针,指向堆中改内存的起始地址,解释器寻找引用值时, 会首先检索其在栈中的地址,取得地址后从堆中获得实体。
● JavaScript 一共有8种数据类型,其中有7种基本数据类型: Undefined 、 Null 、 Boolean 、 Number 、 String 、 Symbol ( es6 新增,表示独一无二的值)和 BigInt ( es10 新增);Object
CSS in JS
优点:
● 不需要额外的配置来支持 css mudules
● 避免全局样式的污染
● 动态样式
● 会自动添加auto-prefix
缺点:
1 css-in-js 运行时解析的实现版本增加了运行时性能压力
2 增加了包体积。
SPA首屏优化方式
1 代码分割:将SPA应用程序的代码分割成多个小块,在需要时动态加载。这样可以将加载时间分散到多个时间点,减少首屏加载时间。
2 懒加载:仅在需要时动态加载组件和资源,减少初始加载量和首屏加载时间。
3 预加载:提前加载下一个页面所需的资源,减少页面切换时的加载时间,提高用户体验。
4 使用CDN:将静态资源(例如JavaScript、CSS、图片等)放到CDN上,可以减少网络延迟和带宽占用,提高资源加载速度。
5 SSR(Server Side Rendering):使用服务端渲染技术,在服务器端生成HTML页面,减少客户端的加载时间,并提高SEO效果。
6 静态化页面:将SPA应用程序的某些页面转化为静态页面,通过CDN进行分发,可以减少服务器的压力和流量消耗,提高页面加载速度。
总之,SPA的首屏优化是一个复杂的问题,需要根据应用程序的实际情况和场景选择合适的优化方式。需要综合考虑网络延迟、资源大小、缓存策略、渲染性能等多个因素,以达到最佳的用户体验和性能表现。
● 减小入口文件体积
○ 常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加
● 静态资源本地缓存
○ 后端返回资源问题:
■ 采用HTTP缓存,设置Cache-Control,Last-Modified,Etag等响应头
○ 前端合理利用localStorage
● UI框架按需加载
● 组件重复打包
○ 修改 CommonsChunkPlugin 的配置 minChunks: 3
● 图片资源压缩
● 开启Gzip压缩
● 使用SSR
常规的性能优化手段
● 减少HTTP请求:合并文件、CSS精灵( background-position )
● 减少DNS查询:DNS缓存、将资源分布到恰当数量的主机名,平衡并行下载和DNS查询
● 非必须组件延迟加载
● 使用CDN 将静态资源放到cdn上
● 添加Expires或者Cache-Control响应头
● 对组件使用Gzip压缩
● 配置ETag
内存泄漏
意外的全局变量
闭包 给返回的那个内部函数置为null
事件监听未被移除
useHttp
useRef做错误重试, useEffect监听缓存的key有没有变,useState存请求的结果和状态
diff算法
Diff算法是React用于实现高效的虚拟DOM更新的核心算法之一。它用于比较前后两个虚拟DOM树的差异,并且只更新发生变化的部分,以避免不必要的DOM操作,提高渲染性能和用户体验。
Diff算法的实现流程如下:
1 比较两个根节点,如果节点类型不同,则直接替换整个节点。
2 如果节点类型相同,则比较节点属性,将有变化的属性更新到DOM上。
3 比较子节点,如果子节点数量不同,则直接替换整个节点。
4 如果子节点数量相同,则逐个比较子节点。
5 如果子节点类型相同,则递归执行Diff算法。
6 如果子节点类型不同,则直接替换整个节点。
在执行Diff算法时,React还会使用一些优化策略来提高性能,例如将相邻的文本节点合并为一个节点、设置key值来优化列表更新等。
需要注意的是,虽然Diff算法可以有效地减少DOM操作,但也不是万能的。在某些情况下,Diff算法的性能可能不如手动操作DOM。因此,需要根据具体的场景和需求来选择合适的优化策略,以达到最佳的性能和用户体验。
如何用webpack来优化前端性能
压缩代码 如 UglifyJsPlugin
利用CDN加速: 在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。 Webpack 可以通过配置 output.publicPath 选项来指定静态资源的访问路径,将静态资源的路径指向 CDN 上的资源地址,以实现静态资源的加速和优化。
Tree Shaking
Code Splitting: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存
谈谈对 window.requestAnimationFrame 的理解
让浏览器在下次重绘之前调用指定的回调函数更新动画
优点:
● CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
● 函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。
React.memo() 和 useMemo() 的主要区别
● React.memo() 是一个高阶组件,我们可以使用它来包装我们不想重新渲染的组件,除非其中的 props 发生变化
● useMemo() 是一个 React Hook,我们可以使用它在组件中包装函数。 我们可以使用它来确保该函数中的值仅在其依赖项之一发生变化时才重新计算
sessioncookiedengdeng
● Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在数据库、文件中;因为http无状态
● Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
● 由于浏览器的安全策略,localstorage是无法跨域的,也无法让子域名继承父域名的localstorage数据,使用 postmessage支持跨域
● sessionStorage在同源的情况下a跳转b会把a的拷贝一份作为b的初始缓存值
为什么B站的弹幕不会挡住视频中的人物
mask-image
-webkit- mask-image: url (“mask.svg”); -webkit- mask-size: 668px376px;
React的Fiber的原理,知道怎么实现的吗,是否了解Hook的实现原理
实现多个接口并发请求,按顺序返回结果。
先发送异步请求,返回的结果也是promise ,对结果进行一个链式调用就行
面试的意思是你按顺序获取到结果,就是拿到一个结果打印一下再去拿下一个,要求是不用promise.all,这里的串行调用是对结果的串行调用
你是怎么理解React的?(答得非常烂,建议从数据驱动 ui = f(state)、响应式 fiber + 异步可中断更新、组件化 component,hooks、跨平台 scheduler;reconciler;render;vdom,合成事件系统等方面答)
vue和react diff算法的区别
手写一个轮播图
SSR和CSR的区别
SSR优点:首屏渲染,安全性,SEO,可访问性
缺点:额外的开发要求,服务器压力
服务稳定&降级方案
之前使用CSR加载页面的方式,资源加载方式比较简单,服务挂载到Nginx上就可以运行,基本无需担心服务的稳定性。切换到服务端渲染后,新增了服务端的Node层,而服务端的稳定性就更需要关注,需要通过以下方式来确保服务可用性以及可降级为CSR渲染的能力。
Node服务挂掉时,整体降级:
● 在bpm-approval-redirect项目内,通过Kconf配置降级为CSR渲染
● Nginx层处理降级
单次请求页面异常:
● Next.js框架处理:当服务端渲染有异常报错时(包括但不限于接口、逻辑异常),页面会直接停止服务端渲染切换为客户端渲染
细嗦闭包
闭包(closure)指有权访问另一个函数作用域中变量的函数。 ----- JavaScript 高级程序设计
闭包有什么用?
1 延伸变量作用域范围,读取函数内部的变量
2 让这些变量的值始终保持在内存中
标记清除
这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。
容易在循环引用时出现问题的策略。因为计数记录的是被引用的次数,所以循环引用时计数并不会消除。导致无法释放内存。
如果要解决循环引用带来的内存泄露问题,我们只需要把循环引用中的变量设为null即可。
react路由模块的原理
webpack构建优化
React 素质 N 连
1 useRef 主要的功能就是帮助我们获取到DOM元素或者组件实例,它还可以保存在组件生命周期内不会变化的值。作用:操作DOM元素,集成第三方Dom库,DOM元素内容设置及媒体播放,焦点控制(计算坐标)。
2 之所以会将事件委托从 document 中移到 id = root 的DOM元素,是为了 可以更加安全地进行新旧版本 React 树的嵌套 。
3 每一个 hooks 方法都会生成一个类型为 Hook 的对象,用来存储一些信息,前面提到过函数组件 fiber 中的 memoizedState 会存储 hooks 链表,每个链表结点的结构就是 Hook。
4 16 版本先执行原生事件,当冒泡到 document 时,统一执行合成事件, 17 版本在原生事件执行前先执行合成事件捕获阶段,原生事件执行完毕执行冒泡阶段的合成事件,通过根节点来管理所有的事件
CommomJS & ESmodule
使用模块化可以给我们带来以下好处
● 解决命名冲突
● 提供复用性
● 提高代码可维护性
CommonJS规范加载模块是同步的,只有加载完成,才能执行后面的操作。
CommonJS规范中的module、exports和require
● 每个文件就是一个模块,有自己的作用域。每个模块内部,module变量代表当前模块,是一个对象,它的exports属性(即module.exports)是对外的接口。
● module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。
● 为了方便,Node为每个模块提供一个exports变量,指向module.exports。
let exports = module.exports;
● require命令用于加载模块文件。
● 为 CommonJS 的require语法是同步的,所以就导致了 CommonJS 模块规范只适合用在服务端,而ES6模块无论是在浏览器端还是服务端都是可以使用的,但是在服务端中,还需要遵循一些特殊的规则才能使用 ;用babel, Webpack
● CommonJS 模块输出的是一个值的拷贝,而ES6 模块输出的是值的引用;
● CommonJS 模块是运行时加载,而ES6 模块是编译时输出接口,使得对JS的模块进行静态分析成为了可能
● 因为两个模块加载机制的不同,所以在对待循环加载的时候,它们会有不同的表现。 CommonJS 遇到循环依赖的时候,只会输出已经执行的部分,后续的输出或者变化,是不会影响已经输出的变量。而ES6模块相反,使用import加载一个变量,变量不会被缓存,真正取值的时候就能取到最终的值;
● 关于模块顶层的this指向问题,在 CommonJS 顶层,this指向当前模块;而在ES6模块中,this指向undefined;
● 关于两个模块互相引用的问题,在ES6模块当中,是支持加载 CommonJS 模块的。但是反过来, CommonJS 并不能requireES6模块,在NodeJS中,两种模块方案是分开处理的。
1 CommonJS模块是加载时执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,没有执行的部分不会输出。
2 ES6模块对导出模块,变量,对象是动态引用,遇到模块加载命令import时不会去执行模块,只是生成一个指向被加载模块的引用。CommonJS模块规范主要适用于后端Node.js,后端Node.js是同步模块加载,所以在模块循环引入时模块已经执行完毕。推荐前端工程中使用ES6的模块规范,通过安装Babel转码插件支持ES6模块引入的语法。
TCP UDP
bigint proxy reflect
BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 Number 表示的最大数字。 BigInt 可以表示任意大的整数。
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如: 10n ,或者调用函数 BigInt() (但不包含 new 运算符)并传递一个整数值或字符串值。
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。作为构造函数使用 传入两个参数,第一个是所要拦截的目标对象,第二个是handle拦截属性(set、get)
Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API。 Reflect 对象的设计目的有这样几个。
(1) 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty ),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。
(2) 修改某些 Object 方法的返回结果,让其变得更合理。比如, Object.defineProperty(obj, name, desc) 在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc) 则会返回 false 。
(3)Reflect对象的方法与Proxy对象的方法一一对应
为什么setState异步
保证内部一致性、启用并发更新
0.5px的线如何实现
webpack常用插件及优化方式
1 HtmlWebpackPlugin:用于生成HTML文件,并自动将打包后的JS、CSS等文件注入到HTML中。
2 CleanWebpackPlugin:用于清理打包后的文件,避免文件夹中出现无用的文件。
优化:
1 Tree Shaking:通过静态分析代码,找到不会被使用到的代码,并将其从bundle文件中删除,减小文件大小。
2 代码分割 图片压缩 打包是个非常棒的技术,但随着你的应用增长,你的代码包也将随之增长。尤其是在整合了体积巨大的第三方库的情况下。你需要关注你代码包中所包含的代码,以避免因体积过大而导致加载时间过长。
css预处理器的作用(sass、less)
增强了 CSS 语法,让你可以在 CSS 中使用变量、循环、嵌套等功能
HTML5语义化标签及为何要使用
如何实现同源下多页面通信
LocalStorage
react怎么做diff
什么时候会触发预检请求
跨域
为什么要做这些项目
实现大量数据渲染:分页、懒加载、定时器
设备兼容问题解决
rem、em、vh、vw的区别
根元素 父元素 视口高度 视口宽度
fetch和xhr的区别
fetch默认不发cookie 要通credentials include设置 防止csrf
400 500都是走resolve逻辑 只有网络错误才会reject 只能通过response.ok来判断
1 语法简洁,更加语义化
2 基于标准promise实现,支持async/await
3,更加底层,提供的APl丰富(request,response)
4.脱离了XHR,是Es规范里新的实现方
缺点:
1支持网络请求报错,也就是说只有网络错误这些导致请求
不能完成时他才会调用reject,而对400,500这种错误它
并不会reject
2.默认不会带cookie,需要添加配置项:fetch(url,
{credentials: ‘include’})
3.不支持abort,不支持超时控制,使用setTimeout及
promise.reject的实现的超时控制不能阻止请求过程继续在
后台运行,会造成流量的浪费
4.fetch没办法原生检测请求的进度,XHR可 xhr.onprogress
fetch支持跨域请求, XHR(XMLHttpRequest)默认是支持跨域请求的,但是浏览器中存在同源策略(Same-Origin Policy),这意味着默认情况下,XHR 在发送跨域请求时会受到一些限制。
session和cookie的区别
Session和Cookie都是在Web开发中用来保存用户状态的技术,但是它们的实现和使用有些不同。
Session和Cookie的主要区别如下:
1 数据存储位置:Cookie是在客户端浏览器中存储,而Session是在服务器端存储。
2 存储容量:Cookie存储容量较小,通常为4KB左右,而Session可以存储大量数据。
3 安全性:Cookie的数据可以被用户查看、修改和删除,因此对于敏感数据需要进行加密处理,而Session的数据存储在服务器端,用户无法直接访问,因此相对更安全。
4 生命周期:Cookie可以设置过期时间,即在指定时间后自动失效,而Session则在用户关闭浏览器或一定时间内没有访问网站后自动失效。
5 使用方式:Cookie可以在客户端通过JavaScript进行操作,而Session需要在服务器端通过编程语言进行操作。
6 当用户第一次访问 Web 应用程序时,服务器会创建一个会话并分配一个唯一的会话 ID。随后,服务器会将该会话 ID 存储在用户的浏览器 cookie 中或者将其作为查询参数发送给客户端。客户端每次向服务器发送请求时,都会将该会话 ID 一并发送给服务器,服务器根据该 ID 找到对应的会话,并从中获取存储在其中的用户数据。
7 Session 的主要作用是在用户和服务器之间维护一个状态,因此它通常用于以下场景:
8 记录用户的登录状态和权限信息,以便用户在多个页面之间保持登录状态,而无需每次都输入用户名和密码。
9 保存用户的个性化设置,例如语言偏好、主题等。
10 存储用户的购物车或订单信息等,在用户浏览网站时保留这些信息,以便用户可以随时查看或修改。
ES6中5种遍历对象属性的方法
for-in——自身和继承的可枚举属性(除Symbol)
Object.keys()——自身非继承的可枚举属性(除Symbol)
Object.getOwnPropertyNames()——自身所有属性键名(包括不可枚举、除Symbol)
Object.getOwnPropertySymbols()——自身的所有 Symbol 属性的键名
Reflect.ownKeys()——自身的所有键名
调度器,diff,filber优先级打断
useEffect中模拟卸载前如何模拟
useEffect如果第二个参数为空数组如何更新组件
useEffect中模拟卸载前如何模拟
useEffect如果第二个参数为空数组如何更新组件
http的无状态含义
HTTP(超文本传输协议)的无状态性指的是协议本身不保存任何关于客户端请求的状态信息。每个客户端请求被视为一次独立的、无关的请求,服务器不会保留前一次请求的信息。这意味着在每个请求之间,服务器不会记住客户端的状态,也不会保存会话信息。
简单来说,无状态性意味着每个HTTP请求都是相互独立的,服务器不会从一个请求到另一个请求保留任何状态。每个请求都必须包含所有必要的信息,以便服务器可以理解和处理请求,而无需依赖之前的请求。
为了维护状态,HTTP 应用程序通常使用一些机制,例如:
1 Cookies: 通过在客户端保存一些标识信息,服务器可以在每个请求中识别客户端。这些标识信息存储在客户端的 Cookie 中,并在每个请求中被发送到服务器。
2 会话(Session): 服务器可以通过会话机制来在多个请求之间保存状态信息。在客户端第一次访问时,服务器创建一个唯一的会话标识,并将其发送到客户端的 Cookie 中。后续请求中,客户端会带上这个会话标识,服务器根据标识来识别客户端和保存状态信息。
无状态性是 HTTP 协议的一个重要特性,它使得服务器的处理变得简单和高效。每个请求都是独立的,服务器不需要保留之前的状态信息,这降低了服务器的负担并提高了性能。然而,有时候需要在应用程序中维护一些状态信息,这时可以使用 Cookies、会话或其他技术来实现。
对JSON的理解
JSON是一种轻量级的数据交换格式,他可以被任何的语言读取和作为数据格式传递。
在项目开发中,可作为前后端数据交换的一种方式。
js中提供两个函数处理JSON
HTTP&HTTPS
http:无连接,无状态,不安全,明文传输
报文组成部分:请求报文,响应报文
浏览器的渲染过程
● 解析HTML,生成DOM树,解析CSS,生成CSSOM树
● 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
● Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
● Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
● Display:将像素发送给GPU,展示在页面上。
回流重绘
回流大于重绘
造成回流:
● 改变宽高、内外边距,添加删除DOM
● 查询一些属性如offset、scroll
● 改变字体大小、激活伪类
重绘
color、text-decoration
如何优化回流和重绘?
1 使用CSS3动画 使用 CSS3 动画代替JavaScript动画(例如使用 transform 和 opacity 属性),可以利用 GPU 加速,减少回流和重绘的开销,性能更好。
2 避免频繁访问布局信息 避免在 JavaScript 中频繁访问元素的布局信息(例如 offsetTop、offsetLeft、clientWidth、clientHeight 等),因为这会强制浏览器进行回流操作。
3 批量修改样式 对需要修改样式的元素进行批量操作,将多个样式的修改合并为一次操作,避免频繁操作样式,减少回流和重绘的次数。
4 使用transform属性 使用 transform 属性进行位移、缩放和旋转操作,因为它不会引起回流,只会触发重绘。
浏览器进程线程
浏览器包含了哪些进程
● 主进程
○ 协调控制其他子进程(创建、销毁)
○ 浏览器界面显示,用户交互,前进、后退、收藏
○ 将渲染进程得到的内存中的Bitmap,绘制到用户界面上
○ 处理不可见操作,网络请求,文件访问等
● 第三方插件进程
○ 每种类型的插件对应一个进程,仅当使用该插件时才创建
● GPU进程
○ 用于3D绘制等
● 渲染进程 ,就是我们说的 浏览器内核
○ 负责页面渲染,脚本执行,事件处理等
○ 每个tab页一个渲染进程
那么浏览器中包含了这么多的进程,那么对于普通的前端操作来说,最重要的是什么呢?
答案是 渲染进程 ,也就是我们常说的 浏览器内核
浏览器内核(渲染进程)
从前文我们得知,进程和线程是一对多的关系,也就是说一个进程包含了多条线程。
而对于 渲染进程 来说,它当然也是多线程的了,接下来我们来看一下渲染进程包含哪些线程。
● GUI渲染线程
○ 负责渲染页面,布局和绘制
○ 页面需要重绘和回流时,该线程就会执行
○ 与js引擎线程互斥,防止渲染结果不可预期
● JS引擎线程
○ 负责处理解析和执行javascript脚本程序
○ 只有一个JS引擎线程(单线程)
○ 与GUI渲染线程互斥,防止渲染结果不可预期
● 事件触发线程
○ 用来控制事件循环(鼠标点击、setTimeout、ajax等)
○ 当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中
● 定时触发器线程
○ setInterval与setTimeout所在的线程
○ 定时任务并不是由JS引擎计时的,是由定时触发线程来计时的
○ 计时完毕后,通知事件触发线程
● 异步http请求线程
○ 浏览器有一个单独的线程用于处理AJAX请求
○ 当请求完成时,若有回调函数,通知事件触发线程
当我们了解了渲染进程包含的这些线程后,我们思考两个问题:
1 为什么 javascript 是单线程的
2 为什么 GUI 渲染线程为什么与 JS 引擎线程互斥
主要区别就是,如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到
为什么 javascript 是单线程的
首先是历史原因,在创建 javascript 这门语言时,多进程多线程的架构并不流行,硬件支持并不好。
其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。
而且,如果同时操作 DOM ,在多线程不加锁的情况下,最终会导致 DOM 渲染的结果不可预期。
为什么 GUI 渲染线程与 JS 引擎线程互斥
这是由于 JS 是可以操作 DOM 的,如果同时修改元素属性并同时渲染界面(即 JS线程 和 UI线程 同时运行), 那么渲染线程前后获得的元素就可能不一致了。
因此,为了防止渲染出现不可预期的结果,浏览器设定 GUI渲染线程和JS引擎线程为互斥关系, 当JS引擎线程执行时GUI渲染线程会被挂起,GUI更新则会被保存在一个队列中等待JS引擎线程空闲时立即被执行。
什么是 Event Loop
● JS 分为同步任务和异步任务
● 同步任务都在JS引擎线程上执行,形成一个 执行栈
● 事件触发线程管理一个 任务队列 ,异步任务触发条件达成,将回调事件放到 任务队列 中
● 执行栈中所有同步任务执行完毕,此时JS引擎线程空闲,系统会读取任务队列,将可运行的异步任务回调事件添加到执行栈中,开始执行
● 执行一个 宏任务 (栈中没有就从 事件队列 中获取)
● 执行过程中如果遇到 微任务 ,就将它添加到 微任务 的任务队列中
● 宏任务 执行完毕后,立即执行当前 微任务队列 中的所有 微任务 (依次执行)
● 当前 宏任务 执行完毕,开始检查渲染,然后 GUI线程 接管渲染
● 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
redux相关
数据如何通过 Redux 流动?
1 首先,用户(通过View)发出Action,发出方式就用到了dispatch方法。
2 然后,Store自动调用Reducer,并且传入两个参数:当前State和收到的Action,Reducer会返回新的State
3 State一旦有变化,Store就会调用监听函数,来更新View。
Redux 由以下组件组成:
1 Action – 这是一个用来描述发生了什么事情的对象。
2 Reducer – 这是一个确定状态将如何变化的地方。
3 Store – 整个程序的状态/对象树保存在Store中。
4 View – 只显示 Store 提供的数据。
解释 Reducer 的作用
Reducers 是纯函数,它规定应用程序的状态怎样因响应 ACTION 而改变。Reducers 通过接受先前的状态和 action 来工作,然后它返回一个新的状态。它根据操作的类型确定需要执行哪种更新,然后返回新的值。如果不需要完成任务,它会返回原来的状态。
react18和17、16有啥区别
1、批处理
在 React 18 中,批处理支持处理的操作范围扩大了:Promise,setTimeout,native event handlers 等这些非 React 原生的事件内部的更新也会得到合并:
2、Transitions 是 React 中一个用于区分高优更新和非高优更新的新概念。
当用户拉动滚动条时,下方的树的每一个节点都会重新渲染,这会带来明显的卡顿,不仅是下方树的渲染卡顿,上方的滚动条也会无法实时跟着用户的交互移动,这会给用户带来明显的卡顿感。
类似场景下常见的做法应该是 debounce 或 throttle ,React 18 为我们提供了原生的方式来解决这个问题:使用 starTransition 和 useTransition 。
● starTransition :用于标记非紧急的更新,用 starTransition 包裹起来就是告诉 React,这部分代码渲染的优先级不高,可以优先处理其它更重要的渲染。
● useTransition:除了能提供 starTransition 以外,还能提供一个变量来跟踪当前渲染的执行状态:
在勾选了 Use startTransition 后 ,滑动条的更新渲染不会再被树的渲染阻塞了
3、
● createRoot
○ 新的 root API,在 React 旧版本中都是通过 ReactDom.render 将应用组件渲染到页面的根元素,在 React 18 中,只有使用 ReactDom.createRoot 才能使用新特性。
react18不再支持ie
架构演进
React 15 时期还没有 concurrent 的概念。它主要分为 Reconciler 和 Renderer 两部分:Reconciler 负责生成虚拟 DOM 并进行 diff,找出变动的虚拟 DOM,然后 Renderer 负责将变化的组件渲染到不同的宿主环境中。
React 16 的架构改动较大,多了一层 Scheduler,并且 Reconciler 的部分基于 Fiber 完成了重构。
React 17 相较先前并没有在架构上有大的改动,它是一个用以稳定 concurrent mode 的过渡版本,另外,它使用 Lanes 重构了优先级算法。
和 = 有什么区别,分别在什么情况使用?
对AJAX的理解
ajax的全称就是异步的JS和XML,是一种创建交互式网页的开发技术方案,它的出现使得传统的web应用程序从一次请求得到完整的被渲染后的网页文件到可以进行局部刷新的一次改变
应用:用户上传头像的预览功能,注册、登录的验证操作,比如用户名是否重复登入,密码是否正确
原生的ajax的实现依赖于XMLHttpRequest
z-index属性在什么情况下会失效?
通常 z-index 的使用是在有两个重叠的标签,在一定的情况下控制其中一个在另一个的上方或者下方出现。z-index值越大就越是在上层。z-index元素的position属性需要是relative,absolute或是fixed。
z-index属性在下列情况下会失效:
● 父元素position为relative时,子元素的z-index失效。解决:父元素position改为absolute或static;
● 元素没有设置position属性为非static属性。解决:设置该元素的position属性为relative,absolute或是fixed中的一种;
● 元素在设置z-index的同时还设置了float浮动。解决:float去除,改为display:inline-block;
redux三大原则
单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
State 是只读的
唯一改变 state 的方法就是触发 action ,action 是一个用于描述已发生事件的普通对象。
使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers 。
范式化state: https://www.redux.org.cn/docs/recipes/reducers/NormalizingStateShape.html
多准备一下项目
generator 说了协程
Generator是ES6提出的一种异步编程的方案。ES6推出了generator,用于更方便的创建iterator。也就是说,Generator就是一个返回值为iterator对象的函数。
● ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费
怎么同步拿到usestate
自定义hook 结合useRef通过回调函数去同步拿到最新值
hook模拟生命周期
怎么进行异步编程
1、回调函数,这是异步编程最基本的方法;2、事件监听;3、发布或订阅;4、Promises对象。
怎么捕获await async的错误
1、try catch
2. then()
因为返回的是一个Promise,那我们首先想到的就是.then() 和.catch() ,于是很快就能写出以下代
3、使用 await-to-js js 库来处理异常
js数组方法 改变原数组不改变原数组
改变: pop push shift unshift reverse sort
concat join filter reduce find findindex slice( 返回一个索引和另一个索引之间的字符串 )
响应式布局
媒体查询 rem 百分比布局 vwvh视口单位
原生js方法拿到ul下的li
getElementsByTagName
类数组转数组
Array.prototype.slice.call()
Array.from()
扩展运算符(…)
ts使用
状态码301 302
hook怎么模拟生命周期
instanceof 判断不了的 (百度一面问了)
null instanceof null
VM91:1 Uncaught TypeError: Right-hand side of ‘instanceof’ is not an object
{} instanceof Object
VM358:1 Uncaught SyntaxError: Unexpected token ‘instanceof’
因为instanceof 的前操作数 需要是一个对象, 但是{} 同时也是空的代码块,所以js无法识别{}是代码块还是空对象,
JSON 深拷贝缺点
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
cookie sessionStorage localStorage 区别
共同点:都是保存在浏览器端、且同源的
区别:
1 cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
2 存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
3 数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
4 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
所谓同源是指:域名、协议、端口相同。
Map和WeakMap的区别
● Map的键可以是任意类型,WeakMap只接受对象作为键,不接受其它类型的值作为键
● Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键;WeakMap的键是弱引用,键所指向的对象是可以被垃圾回收,此时键是无效的。
● Map可以被遍历,WeakMap不能被遍历
● 正由于这样的弱引用,WeakMap 的 key 是不可枚举的 (没有方法能给出所有的 key)。如果 key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,你应该使用 Map 。
为啥 useState 返回的是 array 而不是 object
如果 useState 返回数组,那么你可以顺便对数组中的变量命名,代码看起来也比较干净。而如果是对象的话返回的值必须和 useState 内部实现返回的对象同名,这样你只能在 function component 中使用一次 useState,想要多次使用 useState 必须得重命名返回值。
http 2 优点
二进制协议 多路复用 数据流 头信息压缩 服务器推送
HTTP 1.0 和 HTTP 1.1 之间有哪些区别
持久连接 资源请求方面引入range头域 允许只请求资源的某个部分 引入了更多的缓存控制策略 etag if-match
新增host字段用来指定服务器的域名
http 和 https
TLS/SSL的功能实现主要依赖三类基本算法: 散列函数hash 、 对称加密 、 非对称加密 。
GET和POST的请求的区别
get请求一般是去取获取数据( 其实也可以提交,但常见的是获取数据 ); post请求一般是去提交数据。
get的参数是在url中 所以数据类型和长度都受限
Get 请求的报文中实体部分为空,Post 请求的报文中实体部分一般为向服务器发送的数据
get请求可以被缓存,post请求不会被缓存不会被保存在浏览器记录中
Tree Shaking
Tree Shaking 在去除代码冗余的过程中,程序会从入口文件出发,扫描所有的模块依赖,以及模块的子依赖,然后将它们链接起来形成一个 “抽象语法树” (AST)。随后,运行所有代码,查看哪些代码是用到过的,做好标记。最后,再将“抽象语法树”中没有用到的代码“摇落”。经历这样一个过程后,就去除了没有用到的代码。
函数组件和类组件的区别
设计思想
● 类组件的根基是 OOP(面向对象编程),所以它会有继承,有内部状态管理等
● 函数组件的根基是 FP(函数式编程)
CSS 实现三角形
border width: 0 height: 0 三边 transparent
数组的方法 哪些es6新增
和 = 的区别 哪个比较快
flex: 1
flex-grow: 1 flex-shrink: 1 flex-basis: 0%
undefined 和 null
undefined 的字面意思就是:未定义的值
当需要释放一个对象时,直接赋值为 null 即可。
说说 httponly
HttpOnly 是Set-Cookie HTTP响应头中包含的附加标志。 生成cookie时使用 HttpOnly 标志有助于降低客户端脚本访问受保护cookie的风险(如果浏览器支持它)
async await
es7提出的异步解决方案 他是generate函数的语法糖 返回值是一个promise对象 比generate返回的iterator更方便 常和promise搭配使用 ascyn把promise包装了一下 使用await就不用写then
bind apply call 区别
作用:
都可以改变函数内部的this指向。
区别点:
1 call 和 apply 会调用函数,并且改变函数内部this指向。
2 call 和 apply 传递的参数不一样,call 传递参数arg1,arg2…形式 apply 必须数组形式[arg]
3 bind 不会调用函数,可以改变函数内部this指向。
前端登入注册的具体实现
登录和注册功能是很常见的功能,通常会在 Web 应用或网站的前端实现。这里是一些关于前端登录注册的具体实现的提示:
1 在 HTML 中创建登录和注册表单,包括输入用户名和密码的输入框以及提交按钮。
2 使用 JavaScript 在前端验证表单输入的内容,例如验证用户名和密码的格式是否正确。
3 使用 AJAX 技术在后端与服务器进行通信,发送登录或注册的请求并处理服务器返回的响应。
4 在服务器端使用后端语言(例如 PHP、Python、Java 等)编写登录和注册的逻辑,处理请求并返回响应。
5 在前端处理服务器返回的响应,根据响应的结果更新界面,例如显示登录成功或注册成功的提示信息。
注意:在实际的应用中,登录和注册功能需要注意安全性,例如使用 HTTPS 协议以保证通信的安全,对密码使用加密算法进行加密存储,以及对表单输入进行防止跨站脚本攻击(XSS)的安全措施。
手写洗牌算法
洗牌算法是用来随机打乱一个序列中元素的顺序的算法。手写洗牌算法的一种方法是使用随机数来交换序列中的元素。
下面是使用 JavaScript 实现的洗牌算法的示例代码:
map和object的区别
Map 和 Object 是 JavaScript 中两种常用的数据结构,主要用来存储键值对数据。但它们有一些区别:
1 存储键的类型不同: Map 可以将任意类型的值作为键,而 Object 只能将字符串或者 Symbol 类型的值作为键。
2 内存开销不同:在存储大量数据的情况下, Map 的内存开销比 Object 更小。这是因为 Object 中的键都会被转化为字符串类型,因此存储同样数量的数据时, Map 中所需的内存更少。
3 遍历顺序不同: Map 中的键值对是按插入顺序遍历的,而 Object 中的键值对是按照字典序遍历的。
4 拓展性不同: Map 可以使用扩展运算符(…)和解构赋值的方式进行扩展和复制,而 Object 不支持这些操作。
Map的键值对个数可以轻易地通过 size 属性获取
总的来说,Map 比 Object 更加灵活,可以用来存储任意类型的键值对数据,而且在存储大量数据的情况下更加节省内存。但 Object 更加常用,在多数情况下也能满足需求。
Object.create()和{}有什么区别
Object.create() 是一个用于创建新对象的函数,而 {} 是用于创建新对象的语法糖。
两者的主要区别在于创建对象的方式不同:
● Object.create() 函数会接受一个对象作为参数,并使用这个对象作为新对象的原型。这样新对象就可以继承自这个原型对象的属性和方法。
● {} 语法糖会创建一个新对象,并可以在大括号内定义对象的属性和方法。这种方式创建的对象没有原型,也不能继承任何属性和方法。
所以,如果你想让新对象继承自另一个对象的属性和方法,就应该使用 Object.create() 函数。如果你只是想创建一个空对象,就可以使用 {} 语法糖。
说说jwt
JSON WEB TOKEN 简称为 JWT ,是一个基于 JSON 的开放标准,用于通信双方之间传递安全信息的简洁的、 URL 安全的表述性声明规范,经常用于身份验证。
JWT 使用了数字签名的方式来保证信息的完整性和可靠性,因此可以用于身份认证和授权场景。例如,在 Web 应用或 API 中,JWT 可以用于在客户端和服务器之间传递身份认证信息,以便服务器端验证用户的身份并进行相应的授权操作。
JWT 由三个部分组成:
1 头部(Header):描述了 JWT 的元数据,例如它使用的签名算法。
2 载荷(Payload):包含了 JWT 的声明,例如身份信息、权限信息等。
3 签名(Signature):使用私钥生成的数字签名,用于验证 JWT 的完整性。
JWT 的格式如下:
xxxxx.yyyyy.zzzzz
其中,头部和载荷部分是 base64 编码的 JSON 格式的字符串,签名部分是数字签名。三个部分之间用一个点号(.)分隔。
在使用 JWT 进行身份认证和授权时,通常会将 JWT 发送给客户端并存储在浏览器的本地存储中,然后在客户端的请求中携带 JWT 与服务器进行通信。服务器端收到请求后,会使用公钥进行验签,以确认 JWT 的完整性。如果验签通过,则可以将 JWT 中的声明信息(例如身份信息、权限信息等)解析出来,并根据声明信息进行相应的授权操作。
总的来说,JWT 是一种轻量级的身份认证和授权解决方案,可以用于在客户端和服务器之间传递身份信息和权限信息。
react常用的api 生命周期
● React.createElement():创建 React 元素。
● ReactDOM.render():将 React 元素渲染到 DOM 中。
● React.Component :创建组件类的基类。
● React.PureComponent :创建纯组件类的基类。
● React.memo() :创建纯函数组件的高阶组件。
● useState() :使用 Hooks 在函数组件中管理状态。
● useEffect() :使用 Hooks 在函数组件中处理副作用。
● useContext() :使用 Hooks 访问上下文。
● useRef():使用 Hooks 访问 DOM 节点。
React 组件有几个不同的生命周期阶段,在每个阶段中,React 组件可以调用特定的生命周期方法。这些方法能够让你在组件的整个生命周期中进行预设的操作。
● Mounting: 这是组件第一次渲染的过程。在这个阶段,React 组件会调用以下生命周期方法:
1 constructor()
2 getDerivedStateFromProps()
3 render()
4 componentDidMount()
● Updating: 当组件的 props 或 state 发生改变时,组件就会进行更新。在这个阶段,React 组件会调用以下生命周期方法:
1 getDerivedStateFromProps()
2 shouldComponentUpdate()
3 render()
4 getSnapshotBeforeUpdate()
5 componentDidUpdate()
● Unmounting: 当组件从 DOM 中被移除时,它就会进入卸载阶段。在这个阶段,React 组件会调用以下生命周期方法:
1 componentWillUnmount()
说说ts
TypeScript 是一种由微软开发的强类型编程语言,是 JavaScript 的超集。它增加了许多新的特性,包括类型系统、接口、命名空间和类,使得 JavaScript 代码更加类型安全,易于维护和扩展。
● 增强代码的可维护性,尤其在大型项目的时候效果显著
● 友好地在编辑器里提示错误,编译阶段就能检查类型发现大部分错误
● 支持最新的JavaScript新特特性
● 周边生态繁荣,vue3已全面支持 typescript
同源策略
协议 域名 端口
只要不满足其中任意一个要求,就不符合同源策略,就会出现“跨域”
TCP 和 UDP
TCP 和 UDP都是传输层协议,他们都属于TCP/IP协议族:
UDP
UDP的全称是 用户数据报协议 ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。
1)面向无连接
首先 UDP 是不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。
具体来说就是:
● 在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了
● 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会任何拼接操作
2)有单播,多播,广播的功能
UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。
3)面向报文
发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。因此,应用程序必须选择合适大小的报文
4)不可靠性
TCP
TCP的全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 是面向连接的、可靠的流协议(流就是指不间断的数据结构)。
它有以下几个特点:
1)面向连接
面向连接,是指发送数据之前必须在两端建立连接。建立连接的方法是“三次握手”,这样能建立可靠的连接。建立连接,是为数据的可靠传输打下了基础。
2)仅支持单播传输
每条TCP传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式。
3)面向字节流
TCP不像UDP一样那样一个个报文独立地传输,而是在不保留报文边界的情况下以字节流方式进行传输。
4)可靠传输
对于可靠传输,判断丢包、误码靠的是TCP的段编号以及确认号。TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的字节发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。
5)提供拥塞控制
当网络出现拥塞的时候,TCP能够减小向网络注入数据的速率和数量,缓解拥塞。
6)提供全双工通信
TCP允许通信双方的应用程序在任何时候都能发送数据,因为TCP连接的两端都设有缓存,用来临时存放双向通信的数据。当然,TCP可以立即发送一个数据段,也可以缓存一段时间以便一次发送更多的数据段(最大的数据段大小取决于MSS)
TCP和UDP的区别
| UDP | TCP | |
|---|---|---|
| 是否连接 | 无连接 | 面向连接 |
| 是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输(数据顺序和正确性),使用流量控制和拥塞控制 |
| 连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
| 传输方式 | 面向报文 | 面向字节流 |
| 首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
| 适用场景 | 适用于实时应用,例如视频会议、直播 | 适用于要求可靠传输的应用,例如文件传输 |
| 箭头函数是 JavaScript 中的一种简写形式,可以让你更方便地书写函数。 | ||
| 箭头函数有几个特征: | ||
| ● 它没有自己的 this,它的 this 是继承自外面的函数的。 | ||
| ● 它没有 arguments 对象,如果要用,可以用 rest 参数代替(…args)。 | ||
| ● 它不能作为构造函数,也就是说,不能使用 new 操作符来调用。 | ||
| ● 隐式返回 (优点)多行要用一个括号包起来 | ||
| 说说状态码 | ||
| ● 200 请求成功 √ | ||
| ● 204 成功处理,但没有返回内容 √ | ||
| ● 301 (Permanently Moved) 永久性重定向,请求的资源已不存在,需用新的 URL 访问 √ | ||
| ● 302 (Temporarily Moved ) 临时重定向,请求的资源还在,暂时需要用另一个 URL 来访问 √ | ||
| ● 304 ——缓存重定向, 不具有跳转含义,可继续使用缓存资源。 服务器只根据请求头中的 If-None-Match 和 响应头中的 ETag 比较判断是否返回 304,一致返回 304 √ | ||
| ● 400 请求的语法错误(一般为参数错误)√ | ||
| ● 401 需要进行http认证,如果之前发送过请求就说明认证失败 | ||
| ● 403 禁止访问资源,可能是客户端权限不对 √ | ||
| ● 404 资源不存在(错误 URL)或找不到请求的网页,也可用于拒绝请求且不想说明理由 √ | ||
| ● 500 Internal Serve Error, 与 400 类型,笼统通用 √ | ||
| ● 502 Bad Gateway 通常是服务器作为网关或代理时返回,服务器自身工作正常,访问后端服务器发生错误 √ | ||
| BFC | ||
| BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。 | ||
| 有几种方法可以为元素生成BFC: | ||
| 1 元素是浮动元素(float 不是 none) | ||
| 2 元素是绝对定位元素( position 为 absolute 或 fixed ) | ||
| 3 元素的 display 属性为 inline-block, table-cell, table-caption, flex, inline-flex 或 grid | ||
| 4 元素是 overflow 属性不为 visible 的块级元素 | ||
| 5 元素是弹性布局中的项目(子元素),且父元素的display属性为 flex 或 inline-flex | ||
| 前端埋点的实现 | ||
| 前端埋点是指在前端页面中加入用于监测用户行为的代码,这些代码可以收集用户的操作数据并发送到服务器端。前端埋点是一种常用的用户行为分析方法,可以帮助我们了解用户在使用网站或应用时的真实行为。 | ||
| 前端埋点的实现方式有很多种,常见的方法包括: | ||
| ● 在页面中加入 JavaScript 代码来监测用户行为,并使用 XMLHttpRequest 或 fetch API 发送数据到服务器。 | ||
| ● 使用第三方的前端埋点工具,例如 Google Analytics、Mixpanel 等。这些工具通常会提供简单的 API,可以让开发人员轻松地在页面中添加埋点代码。 | ||
| ● 使用前端框架(如 React、Vue 等)的插件或扩展来实现埋点功能。这些插件或扩展通常会提供简单的 API,让开发人员能够轻松地在应用中添加埋点功能。 | ||
| 在 React 中实现前端埋点有多种方法,你可以根据自己的需要选择合适的方法。 | ||
| 一种常见的方法是使用 React 的生命周期函数或自定义钩子函数来监测用户行为,并使用 XMLHttpRequest 或 fetch API 发送数据到服务器。例如,你可以在 componentDidMount 生命周期函数中添加代码来监测组件挂载事件,并在组件卸载时使用 componentWillUnmount 生命周期函数来发送数据。 | ||
| 另一种方法是使用第三方的前端埋点工具,例如 Google Analytics、Mixpanel 等。这些工具通常会提供简单的 API,让开发人员能够轻松地在 React 应用中添加埋点功能。 | ||
| 你也可以使用 React 插件或扩展来实现埋点功能。这些插件或扩展通常会提供简单的 API,让开发人员能够轻松地在应用中添加埋点功能。 | ||
| 如果金融系统使用定时器每隔一秒钟用 Promise 发送请求,可能会有以下几个问题: | ||
| 1 负载增加:由于定时器每隔一秒钟发送请求,如果有大量请求同时发出,可能会导致服务器负载过高,影响系统的稳定性和性能。 | ||
| 2 安全风险:可能会有攻击者利用这种方式进行 DDoS 攻击,导致服务器资源耗尽,无法正常提供服务。 | ||
| 3 网络带宽消耗:如果每隔一秒钟发送大量请求,可能会消耗大量的网络带宽,导致网络性能下降。 | ||
| 为了避免这些问题,应该谨慎地考虑使用定时器发送请求的频率,避免发送过多的请求。 | ||
| monorepo和multirepo的区别 | ||
| monorepo 是指将多个项目放在同一个版本库中的组织方式,而 multirepo 则是将每个项目都放在各自的版本库中。 | ||
| 两者最明显的区别在于组织方式上,但是这也会带来一些其他的差异: | ||
| ● 协作方式不同:在 monorepo 中,所有项目都共用同一个版本库,因此协作起来会更方便。在 multirepo 中,每个项目都有自己的版本库,协作起来可能需要更多的协调工作。 | ||
| ● 依赖管理方式不同:在 monorepo 中,所有项目都在同一个版本库中,因此可以直接在项目之间使用内部依赖。在 multirepo 中,每个项目都有自己的版本库,如果要在项目之间使用依赖可能需要使用远程依赖或者打包发布。 | ||
| ● 测试和构建方式不同:在 monorepo 中,可以使用单一的测试和构建工具来管理所有项目,这样可以方便地进行测试和构建。在 multirepo 中,每个项目可能都有自己的测试和构建工具,这样可能需要更多的协调工作来维护测试和构建过程。 | ||
| useMemo作用 | ||
| useMemo 是 React 内置的一个 Hook,它允许你在函数组件中进行记忆化(memoization)。它能帮助你避免在每次渲染时都进行一些计算。在依赖不变时 多次定义的时候 返回的值是相同的 | ||
| 进行大量的计算操作时 避免每次渲染时都重新计算 | ||
| 对子组件传递相同内容的对象时 使用useMemo进行性能优化 | ||
| useMemo 会记录上一次执行 create 的返回值,并把它绑定在函数组件对应的 fiber 对象上,只要组件不销毁,缓存值就一直存在,但是 deps 中如果有一项改变,就会重新执行 create ,返回值作为新的值记录到 fiber 对象上。 | ||
| useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数( initialValue )。返回的 ref 对象在组件的整个生命周期内保持不变。 | ||
| useLayoutEffect 是一个 React Hook,它与 useEffect 类似,但是在浏览器重新渲染之前同步地执行。 | ||
| useCallback | ||
| 会返回一个 memoized 的值 在依赖不变时 多次定义的时候 返回的值是相同的 | ||
| 这可以避免在每次渲染期间都创建一个新的回调函数,从而提升性能。 | ||
| 使用useCallback目的是不希望子组件进行多次渲染 并不是为了函数进行缓存 | ||
| 当你将一个回调函数传递给子组件作为 props 时,可以使用 useCallback 来确保只有在依赖项发生变化时才创建新的函数。这可以避免不必要的子组件重新渲染。 | ||
| useMemo 用于缓存函数的结果,而 useCallback 用于缓存函数本身。 | ||
| 事件循环 | ||
| 事件循环是指浏览器或 JavaScript 运行环境中处理异步任务的机制。它可以让 JavaScript 在单线程的情况下也能处理异步任务,提高了程序的效率。 | ||
| JavaScript 是一种单线程语言,所有的任务都在一个主线程上执行。事件循环可以让长时间运行的任务在主线程之外运行,可以避免浏览器卡顿。当主线程空闲时,事件循环会将任务加入执行栈,让主线程执行任务。这样就可以保证浏览器的流畅性,提升用户体验。 | ||
| Js引擎在执⾏代码时候会产⽣执⾏栈,当调⽤异步 API,例如 setTimeout, setInterval,Promise 等回调触发时,就会进⼊异步任务队列,当同步代码执⾏完成 后,就会去异步队列取出回调函数并执⾏,这样就形成了⼀个事件循环。在 Javascript 中有两种任务类型,分别是 宏任务 和 微任务 。 | ||
| 事件循环的工作流程大致如下: | ||
| 1 主线程执行同步任务,并将异步任务加入任务队列。 | ||
| 2 当主线程空闲时,事件循环会检查任务队列是否有任务需要执行。 | ||
| 3 如果有任务需要执行,事件循环会将任务加入执行栈,并执行任务。 | ||
| 4 任务执行完毕后,事件循环会再次检查任务队列,如果还有任务就继续执行,直到任务队列为空。 | ||
| 事件循环是 JavaScript 异步任务处理的基础,它可以让 JavaScript 在单线程的情况下也能处理异步任务,提高了程序的效率。 | ||
| 宏任务主要包括:setTimeout、setInte rval、 setImmediate:node 的⽅法 | ||
| 微任务主要包括:promise、process.nextTick() | ||
| 执行规则:同步代码直接进入主线程执行,JS引擎判断主线程是否为空,如果为空,则读取 微任务Event Queue 中所有的消息,并依次执行。主线程和微任务 Event Queue 都为空后,读取 宏任务Event Queue 中的第一个消息进入主线程执行,来回微宏。 | ||
| flex属性 | ||
| promise的理解 | ||
| Promise 对象是异步编程的一种解决方案。Promise 是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。一个 Promise 实例有三种状态,分别是pending、 fulfilled 和 rejected。实例的状态只能由 pending 转变 fulfilled 或者 rejected 状态,并且状态一经改变,无法再被改变了。 | ||
| 状态的改变是通过传入的 resolve() 和 reject() 函数来实现的,当我们调用resolve回调函数时,会执行Promise对象的then方法传入的第一个回调函数,当我们调用reject回调函数时,会执行Promise对象的then方法传入的第二个回调函数,或者catch方法传入的回调函数。 | ||
| webpack loader plugin都知道哪些 | ||
| Loader 是用于转换某些类型的模块,例如将 TypeScript 转换为 JavaScript 或将 SASS 转换为 CSS。 | ||
| Plugin 是用于执行自定义任务的扩展,例如打包优化、资源管理和注入环境变量。 | ||
| 一些常用的 Loader 包括: | ||
| ● babel-loader:用于将 JavaScript 代码转换为浏览器可以理解的形式 | ||
| ● css-loader:用于加载并解析 CSS 文件 | ||
| ● style-loader : CSS 将使用 style-loader 生成一个内容为最终解析完的 CSS 代码的 Style 标签,放到 head 标签里 | ||
| ● file-loader:用于加载并输出文件,例如图像或字体 | ||
| ● sass-loader:用于将 SASS/SCSS 转换为 CSS | ||
| ● ts-loader:用于将 TypeScript 转换为 JavaScript | ||
| 一些常用的 Plugin 包括: | ||
| ● clean-webpack-plugin:用于清理输出目录 | ||
| ● html-webpack-plugin:用于生成 HTML 文件 | ||
| ● mini-css-extract-plugin:用于 分离样式文件,把 CSS 提取为独立文件 | ||
| ● optimize-css-assets-webpack-plugin:用于压缩 CSS 文件 | ||
| ● webpack-bundle-analyzer:用于生成包的可视化报告 | ||
| Webpack的loader和plugin的区别 | ||
| Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。 | ||
| Plugin 就是插件,基于事件流框架 Tapable ,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。 | ||
| Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。 | ||
| Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。 | ||
| Webpack构建流程简单说一下 | ||
| es6+的新特性 | ||
| 1 const、let | ||
| 2 模板字符串 | ||
| 3 箭头函数 | ||
| 4 函数参数默认值 | ||
| 5 解构赋值 | ||
| 6 for…of 用于数组,for…in用于对象 | ||
| ES6 引入了迭代器和生成器,这些特性允许你使用 for-of 循环迭代可迭代对象,以及使用 yield 关键字在函数中暂停和恢复执行。for…in 还会遍历原型链上的属性,而 for…of 不会。 | ||
| 7 Promise | ||
| 8 展开运算符(…) | ||
| 9 对象字面量、class(原型链的语法糖) | ||
| 原型与原型链 | ||
| 在 JavaScript 中,原型是一种特殊的对象,用于为其他对象提供属性和方法。每个构造函数都有一个prototype属性,该属性指向的就是显示原型对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法,每个实例对象上有一个 __proto__ 属性,该属性指向的就是隐式原型对象。 | ||
| 原型链是一种特殊的继承关系,用于定义对象之间属性和方法的继承关系。查找一个属性先在自身查找,如果找不到,就沿着 __proto__ 属性在原型对象上进行查找,如果还找不到,就沿着原型对象的 __proto__ 属性进行查找,直到查找到直到找到Object的原型对象,如果还没有找到就会返回undefined,沿着__proto__查找属性(方法)的这条链就是原型链。 | ||
| 对闭包的理解 以及闭包会造成的问题 | ||
| 闭包是一种特殊的对象,它包含了一个函数以及与函数相关的引用环境。当这个函数被调用时,它能够访问并修改它的引用环境中的变量。 | ||
| 在 JavaScript 中,闭包可以通过在一个函数内部定义另一个函数来创建。 | ||
| 闭包常常被用来创建可以访问私有变量的函数。 | ||
| 内部函数 可以访问其外部函数中声明的变量,调用 外部函数返回 内部函数后,即使 外部函数执行结束了,但 内部函数引用外部函数的变量依然保存在内存中。 | ||
| 作用 | ||
| 1 独立作用域,避免变量污染 | ||
| 2 实现缓存计算结果,延长变量生命周期 | ||
| 3 创建私有变量 | ||
| 运用: | ||
| 防抖节流 模拟块级作用域 对象中创建私有变量 hook | ||
| 缺陷 | ||
| 常驻内存当中 增加内存的使用量 使用不当会造成内存泄漏 | ||
| 垂直居中 | ||
| 绝对定位 + margin-top 负一半高度 or transfrom: translate(0, -50%); | ||
| table-cell + vertical-align: middle | ||
| grid flex align-items justify-content 子项margin: auto 0 | ||
| 在 CSS 中,有几种方法可以使元素垂直居中: | ||
| 1 使用 display: flex 和 align-items: center : | ||
| 2 使用 position: absolute 和 top: 50% ,然后使用负的 margin-top 抵消一半的高度: | ||
| 3 使用 position: absolute 和 top: 50% ,然后使用负的 transform: translateY(-50%) 抵消一半的高度: | ||
| 4 使用 display: table-cell 和 vertical-align: middle : | ||
| 这个方法适用于块级元素,并且它的父元素必须有固定的高度。 | ||
| 这个方法适用于所有元素,但是它的父元素必须有定位属性(比如 relative 或 absolute )。 | ||
| 这个方法和第二种方法类似,但是不需要计算元素的高度。 | ||
| 这个方法适用于块级元素,并且它的父元素必须是一个表格单元格( display: table-cell )。 | ||
| 在选择垂直居中方法时,要注意兼容性和布局的要求。 | ||
| Git的操作?如何合并分支?git fetch和git pull有什么区别? | ||
| Git是一个版本控制系统,它允许你跟踪文件的更改,并在需要时回滚更改。这里是一些常见的Git操作: | ||
| 1 克隆现有仓库:在终端中输入 git clone | ||
| 2 添加文件:要将文件添加到Git仓库,请使用 git add | ||
| 3 提交更改:将文件添加到仓库后,使用 git commit -m “message” 提交更改。 | ||
| 4 推送更改:使用 git push 将本地更改推送到远程仓库。 | ||
| 5 检查状态:使用 git status 可以查看仓库中未提交的更改。 | ||
| 6 创建分支:使用 git branch | ||
| 7 切换分支:使用 git checkout | ||
| 8 合并分支:要将两个分支合并到一起,请先切换到要合并到的分支,然后使用 git merge | ||
| 9 删除分支:使用 git branch -d | ||
| 10 拉取更改:使用git pull从远程仓库拉取最新更改。 | ||
| git fetch是用来从远程仓库获取最新版本的命令。它并不会自动合并到你的本地仓库,而是让你决定是否要将远程仓库的更改合并到你的本地仓库。 | ||
| git pull是一个组合命令,它包含了git fetch和git merge的功能。它会从远程仓库获取最新版本,然后将其自动合并到你的本地仓库。 | ||
| 因此,git fetch和git pull的主要区别是,git fetch只会获取远程仓库的更改,而不会自动合并到本地仓库,而git pull会获取远程仓库的更改并自动合并到本地仓库。 | ||
| 通常情况下,建议使用git fetch来获取远程仓库的更改,然后手动合并到本地仓库。这样可以让你有更多的灵活性和控制,并且可以避免一些意外的合并冲突。 | ||
| 如何解决git冲突,merge和rebase区别 | ||
| 在合并分支时,git merge 命令会将两个分支的提交历史完整地合并到一起,并创建一个新的提交记录来记录这次合并。 | ||
| 相比之下,git rebase 命令会把当前分支的提交记录取出来,并放在另一个分支的最新提交之后,再重新打包成新的提交记录。这样,当前分支的提交记录就会变成一条直线,而不是两条分叉的提交历史。 | ||
| 当你使用 git merge 命令合并分支时,如果有冲突,会生成一个新的提交记录来解决冲突。你需要手动解决冲突,然后再提交。 | ||
| 而在使用 git rebase 命令合并分支时,如果有冲突,会停止在冲突的地方,并要求你手动解决冲突。解决冲突后,使用 git add 命令标记为已解决,然后使用 git rebase —continue 命令继续进行合并。如果你想取消合并,可以使用 git rebase —abort 命令取消合并。 | ||
| 总的来说,git merge 命令更常用,因为它更简单,而且在合并后的提交历史中可以很清楚地看到分支的合并关系。但是,如果你想要让提交历史看起来更整洁,可以使用 git rebase 命令。 | ||
| 解决 git 冲突的步骤如下: | ||
| 1 使用 git status 命令查看当前分支的状态,看看哪些文件有冲突。 | ||
| 2 打开冲突的文件,找到冲突的地方,它会用特殊的标记来标出冲突的两个版本。 | ||
| 1 手动解决冲突。删除冲突标记,并选择一个版本作为最终的版本。 | ||
| 1 使用 git add 命令标记文件已解决冲突,并准备提交。 | ||
| 2 使用 git commit 命令提交解决冲突的版本。 | ||
| 如果是使用 git merge 命令合并分支,则可以直接按照上面的步骤进行。 | ||
| 如果是使用 git rebase 命令合并分支,在解决冲突后,使用 git add 命令标记文件已解决冲突,然后使用 git rebase —continue 命令继续进行合并。 | ||
| hooks原理 | ||
| React Hooks 是一种在函数组件中使用状态和其他 React 特性的方式。它们允许你在不编写 class 的情况下使用状态和其他 React 特性。 | ||
| React Hooks 的原理是利用了 JavaScript 闭包的特性。它们在函数组件中提供了访问私有状态的方法,这些状态可以在函数的生命周期内保持不变。 | ||
| JavaScript 中的函数有两个主要的生命周期阶段: | ||
| 1 定义阶段:在这个阶段,函数被定义,但是还没有被调用。在这个阶段,函数会将它的代码块加载到内存中,但是并没有执行代码块内的任何操作。 | ||
| 2 调用阶段:在这个阶段,函数被调用,代码块内的操作会被执行。函数调用完成后,函数会从内存中清除,释放空间。 | ||
| 不可变数据 | ||
| React 中的不可变数据是指一旦创建就不能更改的数据。这意味着你不能对不可变数据进行修改、添加或删除其中的元素。使用不可变数据的好处是它可以被多个组件共享,因为它不会被修改,所以不会导致组件之间的状态混淆。 | ||
| 使用不可变数据的另一个好处是它可以帮助你更好地管理应用程序的状态。因为你不能更改不可变数据,所以可以轻松地跟踪每次状态变化,并且可以使用状态的历史记录进行调试和错误排除。 | ||
| React 提供了一些内置的不可变数据结构,如 Immutable.js 和 Mori.js,可以帮助你创建和管理不可变数据。 | ||
| react生命周期 | ||
| React 组件有几个不同的生命周期阶段,在每个阶段中,React 组件可以调用特定的生命周期方法。这些方法能够让你在组件的整个生命周期中进行预设的操作。 | ||
| ● Mounting: 这是组件第一次渲染的过程。在这个阶段,React 组件会调用以下生命周期方法: | ||
| 1 constructor() | ||
| 2 getDerivedStateFromProps() | ||
| 3 render() | ||
| 4 componentDidMount() | ||
| ● Updating: 当组件的 props 或 state 发生改变时,组件就会进行更新。在这个阶段,React 组件会调用以下生命周期方法: | ||
| 1 getDerivedStateFromProps() | ||
| 2 shouldComponentUpdate() | ||
| 3 render() | ||
| 4 getSnapshotBeforeUpdate() | ||
| 5 componentDidUpdate() | ||
| ● Unmounting: 当组件从 DOM 中被移除时,它就会进入卸载阶段。在这个阶段,React 组件会调用以下生命周期方法: | ||
| 1 componentWillUnmount() | ||
| 说说webpack的tree shaking | ||
| Webpack 的 tree shaking 功能可以帮助你在打包的过程中移除没有使用到的代码。它可以辅助你减小最终的输出文件体积,从而提升应用的加载速度。 | ||
| 在使用 tree shaking 时,你需要使用 ES6 语法,比如 import 和 export。Webpack 会分析你的代码,找出哪些模块是真正被使用到的,然后把没有用到的模块从最终的输出文件中移除掉。 | ||
| 要启用 tree shaking,你需要在 webpack 的配置文件中设置 mode 为 “production” ,并且使用一个支持 tree shaking 的压缩工具,比如 UglifyJS。 | ||
| 需要注意的是,tree shaking 仅能够移除没有被使用到的代码,而不能移除没有被执行的代码。如果你的代码中有未使用到的函数,但是这些函数被作为回调函数或者是 Promise 的 resolve 函数传入了其他的函数,那么这些函数依然不会被移除。因此,在使用 tree shaking 时,你需要注意避免这种情况的出现。 | ||
| js为什么是单线程 | ||
| js是 作为浏览器的脚本语言,主要 是 实现用户与浏览器的交互,以及操作dom;这决定了它只能 是单线程 ,否则会带来很复杂的同步问题。 | ||
| 首先是历史原因,在创建 javascript 这门语言时,多进程多线程的架构并不流行,硬件支持并不好。 | ||
| 其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。 | ||
| 而且,如果同时操作 DOM ,在多线程不加锁的情况下,最终会导致 DOM 渲染的结果不可预期。 | ||
| setState | ||
| 在 react 18 之前 | ||
| 在组件的生命周期或者 react 的合成事件中 st 是异步的 | ||
| 在 setTimeout Promise 或者原生 dom 事件中 st 是同步的 | ||
| 18 之后 默认所有操作都放到批处理中 (异步处理) 如果希望同步拿到 可以使用 flushSync | ||
| 前端安全(xss csrf sql注入) | ||
| 前端安全是指保护网站或应用程序免受客户端攻击的措施。这些攻击包括跨站脚本攻击(XSS)和跨站请求伪造(CSRF)。 | ||
| XSS 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。 | ||
| XSS 的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行 | ||
| 攻击者可以通过这种攻击方式可以进行以下操作: | ||
| ● 获取页面的数据,如DOM、cookie、localStorage; | ||
| ● DOS攻击,发送合理请求,占用服务器资源,从而使用户无法访问服务器; | ||
| ● 破坏页面结构; | ||
| ● 流量劫持(将链接指向某网站); | ||
| XSS 攻击通常注入在以下位置: | ||
| 1 用户输入字段: 攻击者可以尝试在网站的用户输入字段中注入恶意代码,例如表单字段、搜索框、评论框等。如果网站没有适当地验证和过滤用户输入,攻击者可以注入恶意脚本,然后等待其他用户来访问受影响的页面。 | ||
| 2 URL 参数: 攻击者可以尝试在 URL 中的查询字符串或路径参数中注入恶意代码。如果网站没有正确处理和转义这些参数,恶意代码可能会被执行。 | ||
| 3 Cookie: 如果攻击者能够注入恶意脚本到用户的 Cookie 中,那么他们可以劫持用户的会话,以执行各种恶意操作。 | ||
| 4 HTTP 头部: 攻击者可以尝试通过修改 HTTP 请求或响应的头部信息来注入恶意代码。这可能导致其他用户在访问网站时受到攻击。 | ||
| 5 内联脚本: 如果网站允许用户在页面中插入内联脚本,攻击者可以尝试在这些内联脚本中注入恶意代码。这是一种很危险的情况,因为注入的脚本将被直接执行。 | ||
| 6 第三方内容: 如果网站允许第三方内容(例如广告、社交媒体小部件等)嵌入到页面中,攻击者可以尝试在这些第三方内容中注入恶意代码,以影响页面的访问者。 | ||
| 如何防御 | ||
| ● 对用户输入进行过滤和转义,以防止恶意脚本被注入。 | ||
| ● 使用 CSP( 内容安全策略 ) ,CSP 的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。 | ||
| ● 使用 HTTPOnly 标记的 Cookie,以防止 Cookie 被窃取。在生成cookie时使用HttpOnly标志有助于减轻客户端脚本访问受保护cookie的风险 | ||
| CSRF 攻击指的是 跨站请求伪造攻击 ,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。 | ||
| 攻击者通常会在其他网站上设置一个包含恶意请求的链接或表单,当受害者点击此链接或提交表单时,浏览器会自动发送请求到目标网站,由于受害者已经登录了目标网站,所以请求会被认为是合法的。 | ||
| CSRF 攻击的 本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。 | ||
| ● 进行同源检测 使用请求中的Referer字段。Referer字段包含了请求的来源URL。如果发现请求的Referer字段与服务器期望的不同,则可以拒绝请求。 | ||
| ● 在请求地址中添加token并验证 | ||
| ○ 在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。 | ||
| ● 使用SameSite属性的Cookie。在Cookie中设置SameSite属性可以限制Cookie的发送范围。 | ||
| ● 使用 HTTPS 加密通信,以防止信息被窃取。 | ||
| HttpOnly 值为 true 或 false,若设置为 true ,则不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见,但在发送请求时依旧会携带此Cookie。 | ||
| samesite 用来限制第三方 cookie 在Cookie中设置SameSite属性可以限制Cookie的发送范围以此来防止 CSRF 攻击,有三个属性 Strict(任何情况都不发送)、Lax( 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。 )、None(关闭) | ||
| React DOM 在渲染所有输入内容之前,默认会进行 转义 。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本) 攻击。 | ||
| hook为什么不能在分支语句中使用 | ||
| React要求在每次渲染时,Hooks必须按照相同的顺序调用。如果在分支语句中使用Hooks,可能会导致在某些情况下Hooks的调用顺序发生变化,从而引起错误。 | ||
| vue和react区别 | ||
| vue :template 转为 render 函数 div → h 函数 | ||
| react :div → react.createElement() | ||
| react 的渲染函数只有调用了 setState 的时候才会执行 | ||
| vue 内部数据劫持 调用 render 函数 | ||
| vue 实现了数据的双向绑定 监听每个属性的变化进行响应式的更新 基于 Proxy | ||
| react 是函数式的思想 单向数据流 推崇数据不可变性 react 只会在 setState 之后重走渲染流程 | ||
| Vue通过 getter/setter以及一些函数的劫持,能精确知道数据变化。 | ||
| React 是通过 this.setState 去改变数据,然后根据新的数据重新渲染出虚拟DOM,最后通过对比虚拟DOM找到需要更新 | ||
| vue 采用了template, react 采用了 jsx | ||
| 在渲染时 vue会跟踪每一个组件的依赖关系 不需要重新渲染整个组件树 而 react 在应用的状态被改变时,全部子组件都会重新渲染。通过 shouldComponentUpdate 这个生命周期方法可以进行控制,但Vue将此视为默认的优化。 | ||
| 在diff算法上 两者的源码有区别 | ||
| Vue基于snabbdom库,它有较好的速度以及模块机制。Vue Diff使用双向链表,边对比,边更新DOM 。 | ||
| React主要使用 diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。 | ||
| 基于以上的梳理,总结出以下不同: | ||
| 1 框架的本质是不同的:vue的MVVM的数据双绑的模式,react是构建用户界面的js库。 | ||
| 2 数据流及响应式的不同:vue是数据双绑,react数据流向是单向的。监听数据变化的实现原理不同。 | ||
| 3 模板语法不同:vue是指令+模板语法,react是jsx函数式编程。 | ||
| 4 渲染区别:vue是数据变化通知依赖项精确的驱动渲染,react是需要调用setState时重新渲染全部子组件,但是可以通过 shouldComponentUpdate 等一些方法进行优化控制 | ||
| 5 diff:Vue Diff使用双向链表边对比边更新,react的diff将需要更新的部分添加到任务队列进行批量更新 | ||
| 6 事件机制:vue直接是原生事件,react是合成事件:事件冒泡到根节点进行事件委托处理,且做了跨端兼容处理。 | ||
| 深浅拷贝 | ||
| 跨域 | ||
| 跨域是指在浏览器中从一个域(例如www.example.com)向另一个域(例如api.example.com)发送请求时可能会遇到的问题。这是因为浏览器的同源策略(same-origin policy)限制了脚本可以访问的网站范围。 | ||
| 跨域请求通常是由于前端应用程序(例如网站或移动应用程序)需要访问后端API或其他资源所导致的。为了解决跨域问题,可以使用以下方法之一: | ||
| 1 JSONP(JSON with Padding):这种方法通过在HTML页面中包含一个 | ||
| 2 CORS(Cross-Origin Resource Sharing):CORS是浏览器和服务器之间的一种协议,允许跨域资源共享。通过在服务器端添加特殊的HTTP头,可以允许浏览器在跨域情况下访问服务器资源。 | ||
| 3 代理:可以在前端应用程序的服务器中设置代理,将前端应用程序的请求转发到后端API。这样,前端应用程序就可以通过与代理服务器的同源连接来访问后端API。 | ||
| 自定义hook | ||
| http队头阻塞问题 | ||
| HTTP 队头堵塞是指当浏览器发出 HTTP 请求时,该浏览器所能处理的并发连接数有限,如果在这个限制内的请求都处于等待响应的状态,那么后续的请求就会被堵塞,直到有请求完成并释放一个连接,才能处理新的请求。 | ||
| 这个问题通常发生在网站加载速度较慢的情况下,因为浏览器在等待响应时会占用连接,如果响应时间过长,那么其他请求就会被堵塞。 | ||
| 解决 HTTP 队头堵塞的方法有: | ||
| ● 优化网站性能,使网站加载速度更快 | ||
| ● 减少页面中的资源数量,特别是对于不必要的资源 | ||
| ● 使用内容分发网络 (CDN) 加速静态资源的加载 | ||
| ● 使用 HTTP/2 协议,可以支持多路复用,允许一个连接上同时传输多个请求和响应 | ||
| ● 在浏览器端使用懒加载技术,可以减少页面初始加载时的资源请求数量 | ||
| 输入url到页面渲染 | ||
| dns解析 | ||
| DNS(Domain Name System)是用于将域名解析为 IP 地址的协议。它是互联网上一个重要的分布式数据库系统,支持从域名到 IP 地址的映射。 | ||
| 当你在浏览器中输入一个域名(例如 www.example.com)并回车时,浏览器会进行以下操作来解析域名: | ||
| 1 首先,浏览器会在本地缓存中查找该域名的 IP 地址。如果找到了,就直接使用该地址访问网站。 浏览器缓存、操作系统缓存、本地DNS服务器缓存。 | ||
| 2 如果本地缓存中没有找到该域名的 IP 地址,浏览器会向本地 DNS 服务器发送请求,请求解析该域名。 | ||
| 3 在 DNS 解析的过程中,如果本地 DNS 服务器中没有找到所请求域名的记录,它会向根域名服务器发送请求。根域名服务器会把请求转发给对应的顶级域名服务器。顶级域名服务器再把请求转发给对应的权威域名服务器,权威域名服务器最终会返回该域名的完整信息给本地 DNS 服务器。 | ||
| 一般我们向本地 DNS 服务器发送请求的方式就是递归查询,因为我们只需要发出一次请求,然后本地 DNS 服务器返回给我 们最终的请求结果。而本地 DNS 服务器向其他域名服务器请求的过程是迭代查询的过程,因为每一次域名服务器只返回单次 查询的结果,下一级的查询由本地 DNS 服务器自己进行。 | ||
| DOM0和DOM2级事件简单理解 | ||
| 关于DOM0 和DOM1级事件可以简单理解为: 事件分为DOM 0级事件和Dom 2级事件,DOM2级事件也叫做事件监听。DOM 0级事件的缺 点是如果事件相同 后者的事件会覆盖前者的事件,DOM2级事件可以解决这个问题 | ||
| 1.DOM0级事件就是直接通过onclick绑定到html元素的事件 同元素的同一事件只能绑定一个函数,否则后面的事件会覆盖前面的事件 2.DOM2级事件是通过addEventListener绑定的事件 removeEL 可以绑定多个函数,按顺序执行 3.DOM1一般只有设计规范没有具体实现,所以一般跳过。 | ||
| 死锁 | ||
| 死锁的产生通常需要同时满足以下四个条件,这些条件也被称为死锁的必要条件: | ||
| 1 互斥条件(Mutual Exclusion): 至少有一个资源必须是独占性的,即一次只能由一个进程(线程)使用。 | ||
| 2 占用和等待条件(Hold and Wait): 进程(线程)必须持有至少一个资源,并等待获取其他进程(线程)占用的资源。 | ||
| 3 不可抢占条件(No Preemption): 已分配的资源不能被抢占,只能由进程自愿释放。 | ||
| 4 循环等待条件(Circular Wait): 多个进程(线程)之间形成一个资源请求的循环链,每个进程(线程)都在等待下一个进程(线程)所持有的资源。 | ||
| 死锁的解决方法通常包括以下策略: | ||
| 1 预防死锁(Deadlock Prevention): 预防死锁的方法是通过破坏死锁产生的四个必要条件之一来防止死锁的发生。这可以通过资源分配策略、资源请求策略、资源释放策略等来实现。 | ||
| 2 避免死锁(Deadlock Avoidance): 避免死锁的方法是在资源分配之前使用算法来判断是否会导致死锁,并根据算法的建议来进行资源分配。银行家算法(维护资源数量,分配矩阵)就是一个常用的死锁避免算法。 | ||
| 3 检测和恢复(Deadlock Detection and Recovery): 死锁检测方法用于周期性地检查系统是否处于死锁状态。如果检测到死锁,可以采取措施来恢复,例如终止一些进程,释放它们占用的资源。 | ||
| 4 忽略死锁(Deadlock Ignorance): 有些系统选择忽略死锁问题,因为死锁的发生频率较低,而检测和处理死锁会增加系统开销。 | ||
| 进程和线程 | ||
| 进程是资源分配的最小单位,每个进程都有自己独立的内存空间和系统资源,进程之间相互独立,互不影响。 | ||
| 线程是进程的一个执行单元,是CPU调度的最小单位,同一个进程的线程共享进程的资源 | ||
| IPC:管道,信号量, 消息队列,套接字(Socket) | ||
| 手写发布订阅 |
若有收获,就点个赞吧