一、说一下浏览器事件模型
1、Dom0级别
简单直接,html属性上添加事件函数
<button onclick="handleClick()">Click me</button>
2、Dom1级别
Dom1只是一个规范标准,但并没有明确地引入新的事件模型规范
3、Dom2级别
-
特点:
- 引入了更强大、更规范的事件处理机制。
- 支持事件的捕获和冒泡阶段,可以通过
addEventListener
方法来添加事件监听器,并可以选择在捕获阶段或冒泡阶段处理事件。 - 可以为同一个事件添加多个事件处理函数,这些处理函数会按照添加的顺序依次执行,IE会以相反的顺序执行。
- 无法移除匿名函数。
-
代码实例
const button = document.getElementById('myButton');
if (button.addEventListener) {
// 第三个参数默认是false,表示添加到冒泡阶段,改为true代表事件捕获
button.addEventListener('click', handleClick1, false);
button.addEventListener('click', handleClick2, false);
} else {
// IE 兼容性处理
button.attachEvent('onclick', handleClick1);
button.attachEvent('onclick', handleClick2);
}
二、XMLHttpRequest
1、onReadyStateChange方法
监听异步对象请求状态码readyState的改变,每当readyState改变就触发onReadyStateChange事件
2、readyState:请求状态码
- 0:请求未初始化,还没调用open()
- 1:服务器连接已建立,还没调用send()
- 2: 请求已接收,正在处理中,可以从响应中获取内容
- 3:请求处理中,通常响应中已有部分数据可用了,没有全部完成
- 4:请求已完成,可以通过异步对象的属性获取数据
3、status:http状态码
- 1开头,100:continue,101:http->websocket切换更高协议
- 2开头,200:ok,204:只有响应头无body,206:断点续传分块下载
- 3开头,301:永久重定向,302:暂时重定向,304:缓存
- 4开头,400:bad request,403:禁止访问,404:资源不存在,405:方法不可用
- 5开头,500:服务器错误,501:不支持客户端请求,502:代理服务器异常,503:资源过载
三、fetch与XMLHttpRequest差异
- fetch使用Promise,写起来更简洁
- fetch采用模块化设计,API分散在对象上,Request对象、Response对象、Headers对象,更加合理简洁
- fetch通过数据流(Stream对象)获取数据,可以分块读取,减少内存占用,对于大文件和网络慢情况有用。XMLHttpRequest必须放在缓存里,不支持分块读取,必须全部拿到后一次性吐出来
四、说说闭包的作用和坑
闭包是指在函数内可访问外层函数变量的函数。
1、特点
- 变量持久性,不会因为外部函数执行结束而垃圾回收
- 数据隐藏,外部无法直接访问闭包内部变量,只能通过闭包提供的接口
2、作用
- 控制访问:模块化编程,封装私有属性
- 函数柯里化:将多参函数转为一系列单参函数
- 单例模式
- react hooks
3、问题坑和解决方案
问题:内存泄露
解决方法:
- 1.及时清理不必要的引用,不需要时将外部变量设置为nul
- 2.避免循环引用
- 3.及时清理定时器和事件监听
- 4.通过Performance中的memory截取快照比对,看有没有内存泄漏
五、this指向问题
this绑定的5种方式
- 默认绑定(非严格模式下
this
指向全局对象, 严格模式下this
会绑定到undefined
) - 隐式绑定(当函数引用有上下文对象时, 如
obj.foo()
的调用方式,foo
内的this指向obj)
- 显示绑定(通过
call()
或者apply()
方法直接指定this
的绑定对象, 如foo.call(obj))
new
绑定(使用new来调用一个函数,会构造一个新对象并把这个新对象绑定到调用函数中的this)- 箭头函数绑定(
this
的指向由外层作用域决定的,this
绑定的是最近一层非箭头函数的this
,否则,this
为undefined
)
六、什么是事件委托及作用
- 事件委托是把一个或一组事件委托到父级或更外层元素上,真正绑定事件的是外层元素。
- 事件流经过三个阶段:捕获阶段–目标阶段–冒泡阶段,事件委托发生在冒泡阶段
- 当事件响应在目标元素上时,会通过冒泡机制从而触发外层元素的绑定事件上,然后在外层元素上执行函数
七、事件循环
1、执行步骤
- 首先执行同步任务。
- 当所有同步任务执行完毕后,检查并执行微任务队列中的所有微任务。
- 微任务队列清空后,从宏任务队列中取出一个宏任务并执行。
- 一个宏任务执行完毕后,再次检查并执行微任务队列,然后继续执行下一个宏任务,如此循环。
2、宏任务与微任务
微任务:Promise \ MutationObserver
宏任务:通常包括setTimeout
、setInterval
、XMLHttpRequest
回调函数、事件监听器回调函数等
八、构造器创建对象与class有什么区别?
- 构造器可以不通过new,但class一定需要
- 对于属性和方法的共享,构造器必须通过原型上才能实现
- class声明的内部方法都是不可枚举的
- class内可以使用get和set关键字,拦截该属性的存取函数
- class声明类不存在变量提升
九、浏览器从输入url到渲染全过程
- 应用层:解析url,对域名进行DNS查询获取IP地址,构建Http报文请求
- 传输层:通过3次握手构建TCP连接
- 网络层:将TCP段中的Http请求封装成IP数据包传输
- 数据链路层:将IP数据包封装成帧
- 服务端数据链路层:查看mac地址是否匹配,将帧解析成IP数据包
- 服务端网络层:查看IP地址是否匹配,将IP数据包解析成TCP段返回给客户端
- 客户端:响应并显示页面,对html解析生成DOM树,css资源对DOM树渲染,JS资源对DOM树动态修改
十、DNS解析过程
本地–根服务器–顶级域名服务器–权威域名服务器–IP地址
- 输入URL后,缓好是否有,有返,没有下一步
- 查操作系统缓存有没有
- 向本地DNS服务器发查询清求,本地查缓存有没有,没有的话向根域名服务器发起查询
- 根服务器返回顶级域名服务器地址
- 本地DNS服务器向顶级域名服务器发起查询,顶级域名服务器返回权威服务器地址
- 本地向权威服务器查询,权威返回IP地址
- 本地将IP地址返给浏览器
十一、强制缓存、协议缓存和cdn缓存的区别
1、强制缓存
通过设置cache-control相对时间比如20s
- no-cache:用协议缓存
- no-store:不用缓存
- public:无条件缓存,用的最多
- private:只针对单个用户浏览器缓存
2、协议缓存
也叫对比缓存,当强制缓存失效后使用
- Last-Modified / If-Modified-Since:服务器设置“Last-Modified”告知资源最后修改时间,浏览器通过“If-Modified-Since”带上上次最后修改时间,如果一样使用304缓存
- ETag / If-None-Match:Etag是服务器生成的唯一标识,浏览器通过“If-None-Match”带上ETag值,比对是否一致,如果一直使用304缓存
3、CDN缓存
CDN 会根据用户的 IP 地址将请求转发到距离用户最近的边缘节点服务器上,特点是就近访问、负载均衡、独立缓存
十二、如何监控js异常
1、代码内方式监控
- try…catch
- window.onerror,全局的错误处理函数,可以捕获未被
try...catch
捕获的运行时错误,包括异步错误(在浏览器环境中)。 Promise
的错误可以通过.catch()
方法来处理,未被处理的Promise
错误会被全局的unhandledrejection
事件捕获。
2、埋点方式
通过PerformanceObserver获取主要数据指标:
- FP:First Paint 首次绘制
- LCP(Largest Contentful Paint):最大内容绘制
- CLS Cumulative Layout Shift:累积布局偏移
3、通过Chrome devTool监控
- 断点调试:在 “Sources”(源代码)选项卡中,找到你的 JavaScript 文件,在代码行号处点击可以设置断点
- Performance面板:可以点击Record录制,查看Main线程,分析页面加载和执行过程中的各个任务,包括脚本执行、布局、绘制等。可以查看每个任务的耗时和调用栈,以便找出性能瓶颈
- “Memory”(内存)选项卡可以用于分析页面的内存使用情况。
4、React框架内
使用ErrorBoundary包裹组件,对报错信息记录,当js出现错误时不显示组件
十三、react与vue的区别
1、React
react通过Scheduler和Fiber架构一级高效率的diff调度算法实现了异步可中断的更新
1. Scheduler:
- 通过重写
requestIdleCallback
(在不支持的浏览器中使用setTimeout
模拟)等机制,React 可以在每一个时间片(大约 5ms)内执行一部分任务。如果在一个时间片内没有完成当前任务,它会保存当前的状态,让出主线程的控制权,等待下一个时间片继续执行,从而实现可中断与恢复. - 不同的更新任务可以有不同的优先级。高优先级的任务(如用户交互产生的更新)可以中断低优先级的任务,优先得到处理。
2. Fiber
- React Fiber 使用了双缓冲的 Fiber 树机制。在更新过程中,会构建一棵新的 Fiber 树(称为“workInProgress 树”),同时保留旧的 Fiber 树(“current 树”)。当新树构建完成后,通过指针切换,瞬间将新树变为当前树进行渲染,避免了一次性大规模的 DOM 操作,提高了更新的效率和性能。
- 作为静态的数据结构来说
每个Fiber节点对应一个React element
,保存了该组件的类型(函数组件/类组件/原生组件…)、对应的DOM节点等信息; - 作为动态的工作单元来说
每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新…)
3.Diff算法
- 算法改进,通过比较类型和关键属性如key,判断哪些节点需要被更新,避免整体节点树的遍历更新。
- 只对同级元素进行diff
- 不同类型的元素直接删除重建
- 开发者可以通过key判断哪些元素在不同的渲染下保持稳定。
2、Vue2响应式原理及问题
1.数据劫持
- Vue 会遍历传入的对象的所有属性,使用
Object.defineProperty()
将这些属性转换为 getter 和 setter。 - 当属性被读取时,会触发 getter 方法;当属性被修改时,会触发 setter 方法。
2.依赖收集与触发更新
- 当一个 Vue 实例被创建时,会进行初始化过程,这个过程中会对模板中用到的数据进行 “依赖收集”。
- 会将当前的依赖(通常是一个 Watcher 实例,表示一个正在观察数据变化的组件实例或计算属性等)添加到该数据的依赖列表中。
- 当数据被修改时,会触发 setter 方法,在 setter 方法中,会通知所有依赖该数据的 Watcher 进行更新操作,Watcher 会触发相应的组件重新渲染。
3.问题
- 无法检测到对于对象属性的添加删除,需用用Vue.set()更新
- 无法检测数组索引的变化和长度变化,需要用
push
、pop
、shift
、unshift
、splice
、sort
、reverse
等方法触发更新 - 对于大型数据对象,遍历所有属性并使用
Object.defineProperty()
进行数据劫持会有一定的性能开销。
3、Vue3响应式原理
1.数据代理
Vue3使用Proxy
对象来代理原始数据对象。Proxy
可以拦截对目标对象的各种操作,如属性读取、设置、删除等。当对代理对象进行操作时,Proxy
可以触发相应的拦截器函数,从而实现对数据的响应式处理。
性能更好:
因为Proxy
是在对象级别进行拦截,而不是对每个属性进行单独处理。
十三、关于项目优化
1、webpack
- 分包拆包,将公共包common和业务包business分开,common包通过提前加载和缓存应用,实现可复用
- tree-shaking,对第三库的引用,只保留引用的部分,未使用的部分shake掉,不打进包内
- 组件按需应用,使用Babel-plugin-import,配合tree-shaking使用
- uglifyjs对js文件进行压缩
- CDN加速
2、架构优化
- 容器优化:容器单实例实现
原生端创建一个react instance manage,同时留一个备份,通过这个容器加载common包,打开页面时把业务的bundle包load进去
每次打开页面都使用已有的实例,不需要等待实例加载
- 预加载功能:前置加载数据
加载引擎的同时,根据业务包的描述文件开一个线程,提前获取数据
- RN降级:有开关,在跳转时进行判断(阿波罗配置)
3、组件优化
- 异步加载:
react-lazyload
的核心原理是监听元素是否进入可视区域,当元素进入可视区域时才触发实际的加载操作。对于长列表加载场景,这一特性非常有用。 - 使用React.memo({data}=>return<></>)防止组件过多渲染
- 使用useMemo记忆计算结果,避免在每次渲染时都进行昂贵的计算
- 副作用操作(如网络请求、订阅事件等)应该在
useEffect
中进行,而不是在组件的渲染函数中。这样可以确保渲染函数的纯粹性,减少不必要的重新渲染触发。
4、页面加载
- 首屏加载优化,首次只加载首屏内的组件,之外的组件通过异步加载方式加载
- 首屏接口切分,防止首屏接口过大影响数据获取速度
- 非敏感信息进行缓存,用户切换页面时可第一时间看到新的页面信息
十四、HTTP 和 HTTPS 的区别
1、安全性方面
1. 加密方式
- HTTP:不进行加密,数据以明文形式在网络中传输,存在极大安全风险,任何人截取数据包后可轻松读取敏感信息。
- HTTPS:使用 SSL 或 TLS 协议进行加密,在客户端和服务器之间建立加密通信通道,数据被加密为密文,难以被破解。
2. 证书认证
- HTTP:无证书要求,通信不需要进行身份验证,容易遭受中间人攻击。
- HTTPS:服务器需向权威证书颁发机构申请数字证书,客户端验证证书合法性,确保连接真实可信,证书无效或有问题时客户端会发出警告阻止连接建立。
2、连接方式方面
1. 端口号
- HTTP:默认使用 80 端口进行通信。
- HTTPS:默认使用 443 端口进行通信。
2. 连接建立过程
- HTTP:连接建立相对简单,客户端发送请求,服务器响应请求后开始数据传输。
- HTTPS:连接建立过程复杂。客户端发送请求,服务器返回数字证书,客户端验证证书合法性,若有效则生成随机密钥并用服务器公钥加密后发送给服务器,服务器用私钥解密得到随机密钥,双方用此密钥进行加密解密以安全传输数据。
3、性能方面
1. 传输速度
- HTTP:不进行加密和证书验证等操作,传输速度相对较快。
- HTTPS:因需进行加密和证书验证等操作,会消耗一定计算资源和时间,传输速度相对较慢,但随着硬件性能提升和加密算法优化,性能差异在逐渐减小。
2. 资源消耗
- HTTP:对服务器和客户端资源消耗较小。
- HTTPS:服务器进行证书管理和加密解密等操作会消耗更多 CPU 和内存资源,客户端在验证证书和进行加密解密时也会消耗一定资源。
4、适用场景方面
1. 一般应用场景
- HTTP:适用于对安全性要求不高的场景,如公开信息网站、博客等。
- HTTPS:适用于对安全性要求较高的场景,如电子商务网站、网上银行、企业内部管理系统等,涉及用户隐私信息和重要业务数据传输。
2. 搜索引擎优化(SEO)
- 一些搜索引擎会给予 HTTPS 网站更高权重,认为其更安全可靠,对于希望在搜索引擎中获得更好排名的网站,采用 HTTPS 可能有一定优势。
十五、前端安全防御方式:
- 定期更新登录组件参数、收银台组件,保持和银行内最新规范同步。
- 针对跨站脚本攻击XSS,输入输出验证,过滤转义符号。同源策略:在 HTML 页面的 <head> 标签中,你可以通过添加 Content Security Policy 来限制页面加载的资源来源
- 跨站请求伪造(CSRF),关键信息请求携带token。使用同源策略限制外部网站对用户数据的访问
- 使用 X-Frame-Options 头部,可以禁止网页被嵌入到 iframe 中,从而预防点击劫持
- 使用 HTTPS 加密数据传输,可以有效防止在数据传输过程中被窃听或篡改