1. 组件库按需加载怎么做的,具体打包配了什么
- 按需加载实现:借助打包工具(如 Webpack 的 require.context 或 ES 模块动态导入),在使用组件时才引入对应的代码。例如在 Vue 项目中,若用 babel - plugin - import 插件,配置插件指定组件库路径和按需加载规则,就可在编译时只引入使用的组件代码。
- 打包配置:配置打包工具(如 Webpack)的 externals 避免将组件库重复打包进项目代码;使用 Tree - shaking 优化,去除未使用代码;配置 splitChunks 进行代码分割,将组件库代码单独打包成 chunk,实现按需加载时单独请求。
2. tree - shaking 怎么做的
在 Webpack 中,需满足 ES 模块规范,代码中没有副作用(如修改全局变量等)。配置 mode 为 production 时,Webpack 会自动开启 Tree - shaking 优化;还可在 optimization 中配置 usedExports: true ,标记出被使用的模块导出,打包时移除未标记的代码 。
3. 性能优化(深挖)怎么计算 LCP,mutationObserver 触发频繁怎么优化
- 计算 LCP(最大内容绘制):在浏览器中,可通过 PerformanceObserver API 监听 largest - contentful - paint 事件获取 LCP 时间,示例代码:
javascript
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP:', entry.startTime + entry.duration);
}
}).observe({ type: 'largest - contentful - paint', buffered: true });
- mutationObserver 优化:减少不必要的 DOM 监听,精准定位需监听的 DOM 节点;设置合理的 MutationObserverInit 选项,如减少对不必要属性变化的监听;使用节流或防抖函数包装回调函数,控制触发频率 。
1. 图片可视区怎么做,interserobserver
利用 IntersectionObserver API 实现。创建 IntersectionObserver 实例,传入回调函数和配置参数(如 rootMargin 等),回调函数在目标元素进入或离开视口时触发。示例代码:
javascript
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 图片进入可视区操作,如加载图片
}
});
});
const img = document.getElementById('target - img');
observer.observe(img);
1. 图片的优化方式
压缩图片大小,使用工具(如 TinyPNG )降低图片分辨率、去除元数据;使用合适的图片格式,如 WebP 格式在同等画质下体积更小;图片懒加载,利用 IntersectionObserver 或原生的 loading="lazy" 属性,在图片进入可视区时加载;设置图片的 srcset 和 sizes 属性,让浏览器根据设备屏幕选择合适尺寸图片加载 。
2. 浏览器渲染页面过程(深挖),js 是否会阻塞 html 的解析吗,js 为什么会阻塞页面渲染
- 渲染过程:浏览器接收 HTML 文档,解析成 DOM 树;解析 CSS 生成 CSSOM 树;将 DOM 树和 CSSOM 树合并成渲染树;计算渲染树中元素的布局信息(位置、大小等);对渲染树进行绘制,将像素信息输出到屏幕。
- js 阻塞解析:会。因为 JavaScript 可能会修改 DOM 和 CSSOM ,浏览器为保证执行结果正确,遇到 <script> 标签时会暂停 HTML 解析,优先下载和执行 JavaScript 代码。
- 阻塞渲染原因:JavaScript 可操作 DOM 和 CSSOM ,若在渲染过程中执行,可能改变页面结构和样式,为避免渲染错误,浏览器等 JavaScript 执行完再继续渲染 。
3. 大文件上传,返回的状态码是多少
正常上传成功一般返回 200 或 201 Created 。上传过程中若有错误,如文件大小超出限制可能返回 413 Payload Too Large ;服务器内部错误返回 500 Internal Server Error 等 。
4. Webworker 和 shareWorker 的区别,最多几个 worker,cpu 核心数怎么计算
- 区别:Webworker 是为在后台线程执行脚本而设计,与主线程通过消息机制通信,有自己独立的全局上下文;shareWorker 是多个页面共享的 worker ,通过 port 与页面通信,需在不同页面连接到同一个 shareWorker 实例 。
- worker 数量:理论上无严格固定上限,但受系统资源(如内存)限制,实际大量创建会影响性能。
- 计算 cpu 核心数:在 JavaScript 中,可通过
navigator.hardwareConcurrency 获取浏览器可用的逻辑 CPU 核心数 。
5. serviceworker 做缓存应用场景,有大小限制吗
- 应用场景:离线应用,如 PWA 应用,让用户在离线时也能访问页面;拦截网络请求,根据缓存策略判断从缓存取数据还是发起网络请求;后台数据同步,在网络恢复时自动同步数据。
- 大小限制:不同浏览器有差异,一般来说,Service Worker 缓存大小限制在 5MB - 50MB 左右,可通过
navigator.storageQuota.queryInfo() 方法查询当前站点可用的存储配额 。
6. 大模型传输数据 websocket 的数据格式,ws 和 sse 的区别
- WebSocket 数据格式:可以是文本(如 JSON 格式字符串)或二进制数据。发送和接收数据时,可根据需求进行序列化和反序列化操作,例如将 JavaScript 对象序列化为 JSON 字符串发送,接收到后再解析为对象。
- 区别:WebSocket 是全双工通信协议,客户端和服务器可随时双向发送消息;SSE(Server - Sent Events)是单向的,由服务器向客户端推送消息,常用于服务器向客户端实时推送数据场景,如新闻更新、实时日志等 。
7. websocket 心跳具体怎么做,传递用户信息用什么
- 心跳机制:客户端定时(如每隔一段时间,如 30 秒)向服务器发送一个特殊的心跳包(如简单的文本消息 ping ),服务器收到后回复 pong 。通过判断是否按时收到回复来检测连接状态,若超时未收到回复,可尝试重新连接。
- 传递用户信息:一般将用户信息(如用户 ID、用户名等)以 JSON 格式序列化后通过 WebSocket 发送,接收方解析 JSON 数据获取用户信息 。
8. watcheffect 和 watch 原理的区别
- watch:是 Vue 中用于监听数据变化的 API ,可监听一个或多个响应式数据源,当依赖的数据源变化时执行回调函数。它是惰性的,需显式指定监听的数据源。
- watcheffect:也是 Vue 响应式监听 API ,会自动追踪回调函数中使用的响应式数据,只要这些数据变化就会重新执行回调。它更侧重于自动响应数据变化执行副作用操作,无需手动指定依赖 。
9. vue 双向绑定有几类 watcher,为什么 computed 类中 watcher 会反过来收集 dep 类
- watcher 类型:主要有渲染 watcher ,负责在数据变化时重新渲染组件;用户自定义 watcher ,通过 watch 选项创建,用于监听特定数据变化执行自定义逻辑。
- computed 中 watcher 收集 dep 原因:computed 本质是一个惰性求值的 watcher ,当计算属性依赖的数据变化时,会触发其依赖的 dep 通知 computed watcher 重新计算,为了能准确追踪依赖,computed watcher 会反过来收集依赖的 dep ,以便在依赖数据变化时重新计算结果 。
10. diff 算法比对过程,几个指针
在 Vue 或 React 等框架的 diff 算法中,一般有两个指针,一个指向旧节点列表头部,一个指向新节点列表头部。通过不断比较两个指针指向的节点,进行节点的插入、删除、移动等操作。比对过程是先进行同级比较,若节点类型不同则直接替换,类型相同再比较属性等,逐步遍历完新旧节点列表 。
11. vue 路由的原理
- hash 模式:利用 URL 中的 # 及后面的内容作为路由标识,改变 # 后面的内容不会重新加载页面。通过监听 hashchange 事件,获取 # 后的路径,根据路径匹配组件进行渲染 。
- history 模式:借助 HTML5 的 history.pushState() 和 history.replaceState() 方法改变 URL ,不会触发页面重载。通过监听 popstate 事件以及在代码中调用上述方法,实现路由切换和页面组件渲染更新 。
12. 单点登录,拦截是在请求拦截还是路由拦截
都可以。请求拦截可在每次发送 HTTP 请求时,检查请求头(如是否携带有效的 token )判断用户登录状态,未登录则进行处理(如跳转到登录页);路由拦截是在路由导航守卫(如 Vue Router 的 beforeEach 守卫)中检查用户登录状态,未登录阻止进入某些路由页面 。
13. axios 的设计思想,aop 面向切面?
- 设计思想:axios 是基于 Promise 的 HTTP 客户端,设计上支持浏览器和 Node.js 环境。它提供简洁易用的 API ,可配置请求和响应拦截器,方便统一处理请求和响应(如添加请求头、处理错误等),具有良好的扩展性和灵活性 。
- 与 AOP 关系:axios 的拦截器机制类似 AOP(面向切面编程)思想。拦截器可在不修改核心业务逻辑(请求发送和响应处理)的情况下,在请求发送前和响应接收后执行额外操作(如日志记录、权限验证等),将横切关注点从核心业务逻辑中分离出来 。
14. 打包工具 webpack、vite、rollup 的区别,tsup?
- webpack:功能全面强大,支持多种模块类型和加载器,可处理复杂项目,通过插件机制扩展功能,但配置相对复杂,打包速度在大型项目中较慢 。
- vite:基于浏览器原生 ES 模块支持,开发模式下启动快、热更新快,适合现代前端项目。生产模式下也有不错的性能,配置相对简单,对 Vue 等框架有很好的支持 。
- rollup:专注于 ES 模块打包,擅长构建库和应用程序,输出代码更扁平、体积更小,对 Tree - shaking 支持好,但在处理复杂项目依赖和多页面应用时功能相对有限 。
- tsup:是基于 esbuild 构建的零配置打包工具,支持 TypeScript ,打包速度快,配置简单,可快速构建项目,不过功能丰富度上可能不如 webpack 等 。
15. esbuild 的三个流程
解析(Parsing):将代码字符串解析成抽象语法树(AST);转换(Transforming):对 AST 进行转换操作,如类型检查(对于 TypeScript 代码)、语法转换等;生成(Generating):将转换后的 AST 再转换回 JavaScript 代码字符串输出 。
16. webpack 怎么做模块化打包,打包 js 输出什么,怎么开始多进程打包
- 模块化打包:webpack 通过 loader 处理不同类型模块(如 babel - loader 处理 JavaScript 模块, css - loader 处理 CSS 模块),将其转换为可处理的形式,然后根据模块依赖关系构建依赖图,最后将依赖图中的模块打包成一个或多个 bundle 文件 。
- js 输出:通常输出一个或多个包含所有依赖 JavaScript 代码的 bundle 文件,可根据配置进行代码分割等操作,输出多个 chunk 文件,每个 chunk 包含特定功能模块代码 。
- 多进程打包:使用 thread - loader 插件,在 module.rules 中配置 thread - loader ,将耗时的 loader 操作(如 Babel 编译)分配到多个进程中执行,提高打包速度 。
17. New 一个构造函数生成实例的的过程,箭头函数为什么不能 new
- 构造函数实例化过程:创建一个新对象;将新对象的 __proto__ 指向构造函数的 prototype ;将构造函数中的 this 指向新对象,执行构造函数代码;返回新对象 。
- 箭头函数不能 new 原因:箭头函数没有自己的 this 绑定(它的 this 继承自外层作用域),也没有 prototype 属性,而 new 操作符需要构造函数有 prototype 用于设置实例的原型,并且需要正确处理 this 绑定,所以箭头函数不能使用 new 操作符 。
18. 原型链、继承的几种方式,extends
- 原型链:每个 JavaScript 对象都有一个 __proto__ 属性指向其原型对象,原型对象又有自己的原型,这样形成一条链式结构。对象查找属性或方法时,若自身没有,就沿着原型链向上查找 。
- 继承方式:原型链继承,通过将子类的原型指向父类实例实现;构造函数继承,在子类构造函数中调用父类构造函数,通过 call 或 apply 改变 this 指向;组合继承,结合前两种方式;寄生组合继承,优化组合继承,减少不必要的属性复制;ES6 类继承,使用 extends 关键字,语法更简洁清晰 。
- extends:在 ES6 中用于类继承,子类通过 extends 关键字继承父类的属性和方法,例如 class Child extends Parent {} ,子类会自动继承父类的实例方法和属性,还可重写或扩展父类方法 。
19. await async 执行顺序,原理,generator 改变代码顺序的原理(yield 关键字 next 方法?)
- await async 执行顺序和原理: async 函数返回一个 Promise ,函数内部遇到 await 表达式时,会暂停执行,等待 await 后的 Promise 解决(resolved 或 rejected ),然后继续执行 async 函数后续代码。原理是基于 Promise 状态管理,将异步操作以同步写法呈现 。
- generator 原理: generator 函数是一种特殊函数,通过 function* 定义,内部使用 yield 关键字暂停和恢复执行。调用 generator 函数返回一个迭代器对象,通过调用迭代器对象的 next 方法,可让函数从上次 yield 暂停处继续执行,实现代码执行顺序的控制和异步操作的分步执行 。
20. 分别说下浏览器事件循环和 node 事件循环
- 浏览器事件循环:主线程执行同步代码,遇到宏任务(如 setTimeout 回调、DOM 渲染等)放入宏任务队列,遇到微任务(如 Promise.then 回调)放入微任务队列。当前调用栈清空后,先执行微任务队列中所有任务,再从宏任务队列中取出一个任务执行,不断重复 。
- node 事件循环:分为多个阶段(如 timers 阶段处理 setTimeout 、 setInterval 回调; I/O callbacks 阶段处理一些系统底层 I/O 回调; idle, prepare 阶段; poll 阶段检查新的 I/O 事件; check 阶段处理 setImmediate 回调; close callbacks 阶段处理关闭相关回调)。事件循环在这些阶段间切换,执行对应阶段队列中的任务 。
21. node 中进程和线程的通信方式
- 进程通信:通过 child_process 模块,如 spawn 、 exec 、 fork 等方法创建子进程,父子进程间通过 process.send() 和 process.on('message') 进行消息传递 。
- 线程通信:在 Node.js 中,通过 worker_threads 模块创建线程,线程间可通过 postMessage 方法发送消息,接收方通过 on('message') 监听消息 。
22. 怎么做安全防御,vue 底层做了什么,v - html 怎么防御
- 安全防御通用做法:输入验证,对用户输入进行合法性检查,防止恶意输入;防止 XSS(跨站脚本攻击),对输出到页面的内容进行转义,避免执行恶意脚本;防止 CSRF(跨站请求伪造),使用 CSRF 令牌,在表单提交或 AJAX 请求时验证令牌 。
- Vue 底层安全措施:对数据绑定和渲染做了处理,防止 XSS 攻击,例如在插值表达式中自动对数据进行 HTML 转义;在组件化开发中,通过作用域隔离避免样式和脚本冲突 。
- v - html 防御:使用 v - html 时要确保绑定的数据来源可信,若数据不可信,需提前对数据进行 sanitize 处理(如使用 DOMPurify 库),过滤掉恶意的 HTML 标签和脚本 。
23. http1.0,http1.1,http2
- HTTP/1.0:早期版本,每次请求 - 响应需建立新 TCP 连接,性能较低,无持久连接机制,头部信息未压缩 。
- HTTP/1.1:引入持久连接( Connection: keep - alive ),多个请求可复用一个 TCP 连接;支持