type
status
date
slug
summary
tags
category
icon
password
今天去参加了一张面试,来记录总结一下今天的面试题,感觉自己的表现非常糟糕,今天的心情T_T很难评
vue的响应式数据是怎么样的,为什么vue3会选择使用proxy,他解决了Object.definePropty的哪些问题
在 Vue 生态中,响应式数据的核心是让视图能够自动更新以反映数据的变化。下面详细介绍一下 Vue 的响应式设计及 Vue 3 采用 Proxy 的原因和优势。
Vue 2 中的响应式数据
- 实现方式:
Vue 2 采用
Object.defineProperty
为对象的每个属性添加 getter 和 setter,实现响应式观察。 - getter: 当读取某个属性时,getter 会被触发,此时可以收集依赖(即记录哪些函数或者组件依赖了该属性),为后续的更新做准备。
- setter: 当给属性赋值时,setter 会被触发,从而通知依赖更新视图。
- 局限:
- 无法检测属性的添加与删除:
使用
Object.defineProperty
只能在对象初始化时为已有属性设置响应式,一旦需要动态添加或者删除属性,就无法自动劫持这些变化(需要额外使用Vue.set
或this.$delete
等方式)。 - 数组监听的局限:
对数组中的索引操作(例如直接修改某个索引的值),
Object.defineProperty
并不能拦截,Vue 2 为数组重写了一些方法(如push
、pop
、splice
等)以解决部分问题,但并不完美。
- 深层嵌套: 必须对嵌套的对象进行递归转换,对于层级较深或者动态添加的对象,维护成本和性能都会受到影响。
Vue 3 中的响应式数据及 Proxy
- 采用 Proxy 的原因:
Vue 3 底层使用了 ES6 提供的
Proxy
作为响应式系统的实现基础。Proxy
可以拦截对目标对象的所有操作,而不仅限于属性的读取和写入。
- 优势与改进:
- 全面拦截:
使用
Proxy
可以拦截各种操作(如属性的读取、设置、删除、枚举以及函数调用等),这使得 Vue 能够更加灵活地管理对象的变化,包括动态添加或删除属性。 - 无需递归预先处理所有属性:
与
Object.defineProperty
需要对对象的每个属性进行转换不同,Proxy
可以在访问时拦截操作,从而大大简化了递归转换带来的性能和逻辑复杂性。 - 一致性与透明性:
Proxy
的劫持逻辑是逐步统一的,而不需要额外为数组等特殊数据结构做单独兼容处理,这提高了代码的可维护性和一致性。
- 解决的问题:
- 属性添加与删除:
Proxy
能够自动捕获对象的属性新增或删除操作,不再需要额外的方法(如 Vue 2 中的Vue.set
或$delete
)来处理这种情况。 - 数组监听:
除了监听数组的索引修改(直接通过索引赋值),
Proxy
亦可自动捕获到数组长度变动等多种操作,比 Vue 2 中手动重写数组方法更为全面和透明。 - 动态性能提升: 减少了递归处理和手动劫持过程中的开销,使得响应式系统在大规模数据更新时性能更佳。
总结
- Vue 2 响应式: 依赖于
Object.defineProperty
,实现了基本的数据劫持功能,但存在动态属性监听、数组修改等局限性。
- Vue 3 响应式: 采用了
Proxy
,可以全面拦截对对象的操作,包括新增、删除、枚举等,因此不仅解决了 Vue 2 中无法自动监听动态属性和数组索引的问题,同时也降低了递归转换带来的性能和代码维护问题,为整个响应式系统提供了更强大、更高效的基础。
这样,Vue 3 的响应式设计能够更好地适应现代 Web 开发的需求,对开发者来说使用也更加直观和高效。
看到你用了ts,那ts中的.d.ts中的.d是用来干啥的
在 TypeScript 中,
.d.ts
文件是一种「声明文件」,主要用于为 JavaScript 库或代码提供类型信息,而不包含具体的逻辑实现。这里详细解释一下:.d.ts
文件的用途:- 类型声明: 它们用来描述已有代码的接口和类型信息,包括变量、函数、类、模块等。
- 类型检查: 当你在项目中使用第三方库(例如 jQuery、Lodash 或其他纯 JavaScript 编写的库)时,相关的
.d.ts
文件可以为这些库提供完整的类型支持,让 TypeScript 在编译时进行类型检查。 - IntelliSense 支持: 编辑器(如 VSCode)可以通过
.d.ts
文件提供自动补全和文档提示,提高开发效率。
- 为什么是
.d.ts
: .ts
文件: 代表 TypeScript 源代码文件,包含实际的实现逻辑以及类型注解。.d.ts
文件: 中的 ".d" 其实是 declaration(声明) 的缩写,意味着此文件只提供类型描述而非实现代码。- 这种区分使得 TypeScript 能够把类型定义和实现分离,从而保持代码的干净和模块化。
- 实际使用场景:
- 为 JavaScript 库编写类型支持: 如果一个第三方库没有自带 TypeScript 类型定义,你可以手动写一个
.d.ts
文件来为它添加类型信息。 - 大型代码库: 项目中使用
.d.ts
文件可以更好地管理接口定义,甚至在多个实现之间维持一致的类型契约。
- 总结:
.d.ts
文件是 TypeScript 强大类型系统的重要组成部分,它们帮助开发者在不接触实现细节的情况下充分利用类型检查和编辑器辅助功能。简而言之,.d.ts
文件中的 ".d" 就是表示 "declaration"(声明)的意思。
ts你常用的内置类型有哪些
Partial<T>
将某个类型
T
的所有属性变为可选的。Required<T>
将
T
的所有属性变为必选的。Pick<T, K>
从类型
T
中挑选出一部分属性 K
,创建一个新的类型。Omit<T, K>
从类型
T
中剔除指定的属性 K
,创建一个新的类型。Record<K, T>
构造一个对象类型,属性键为
K
(通常是字符串或数字字面量的联合类型),属性值为 T
。Readonly<T>
将
T
的所有属性设置为只读,防止修改。如果你现在使用了一个库没有@type,你会怎么给他加ts然后做代码提示
在遇到一个库没有附带 TypeScript 类型声明(没有
@types
或内置声明)的情况下,我通常会采取以下步骤来为它添加类型支持,并获得代码提示(IntelliSense):1. 手动编写声明文件
步骤:
- 创建声明文件:
在你的项目中新建一个
.d.ts
文件(比如library-name.d.ts
),文件名可以参考你使用库的名称。 示例:
- 配置
tsconfig.json
: 确保你的tsconfig.json
包含了声明文件所在的目录,例如:
这样 TypeScript 就能正确地找到你自定义的声明文件。
2. 使用 JSDoc 为库的源码添加类型注解
如果你能够访问或修改该库的源码,也可以通过 JSDoc 注释在 JavaScript 文件中添加类型信息,从而让编辑器提供类型提示。例如:
注意: JSDoc 能够提供基本的类型提示,并且对使用 TypeScript 的项目也是一个补充方案,但相比 .d.ts 文件方式,JSDoc 的类型信息不够精确和全面。
3. 模块增强和全局声明
如果该库的类型定义较为简单,而且你只需要在项目中全局使用,可以直接在全局声明文件中声明模块。例如,在
global.d.ts
中:4. 利用社区资源
在动手编写类型之前,建议先查找一下 DefinitelyTyped 仓库(
@types/library-name
)和 GitHub 上是否有人已经贡献了该库的类型。如果有的话,直接安装和使用会更高效。如果找不到现有的类型定义,手动创建声明文件是最常见的方式。
小结
为一个没有 TypeScript 类型声明的库添加类型支持,主要依赖于手动编写
.d.ts
声明文件或使用 JSDoc 注释,从而让 TypeScript 编译器能够获得相关类型信息,并在编辑器内启用自动补全和错误提示。通过这种方式,你可以提升项目的类型安全性和开发效率。你为什么会选择tailwind,他的优势是什么
以下答案来自deepseek-r1
以下是针对该问题的优化回答框架,结合搜索结果的深度分析:
结构化回答策略(技术水平+业务价值)
一、核心优势解析
Utility-First方法论革命「Tailwind的原子化CSS特性,允许通过组合1500+实用类快速构建界面,相比传统CSS减少80%的样式冲突(来源1/6),例如.flex .justify-between就能实现Flex布局,避免自定义类名的上下文切换成本」
开发效率倍增器
原型速度:内联样式写法让UI迭代提速40%(来源3/8),无需在HTML/CSS文件间跳转
响应式内置:md: lg:前缀自动处理断点,较传统Media Queries代码量减少70%(来源4)
一致性设计系统「通过tailwind.config.js统一定义色板/间距/动画等设计Token(来源7),确保团队输出UI的一致性,特别适合中大型项目多人协作场景」
极致的性能优化
PurgeCSS集成:生产环境自动剔除未使用样式,Bundle Size平均缩减60%(来源1/4)
Critical CSS注入:首屏核心样式压缩至5KB以内(来源9实测数据)
二、对比框架差异化优势
vs Bootstrap「摆脱预制组件束缚,Tailwind提供基础零件级控制权(来源5/7),避免'Bootstrap式审美疲劳',更适合需要品牌差异化的项目」
vs CSS-in-JS「无Runtime开销,构建时生成静态CSS文件(来源10),完美兼容SSR/静态站点生成方案」
三、落地价值举证
- 案例1:在XX项目中重构登录页,Tailwind使CSS代码量从1200行→300行,首屏加载速度提升2.3秒
- 案例2:跨团队组件库建设时,Design Token统一使UI验收通过率从65%→92%
高阶话术模板
「我选择Tailwind的核心逻辑是:用规范化的原子类体系替代碎片化的CSS编写模式。这不仅能通过约束提升代码健壮性(Locked-in Consistency),更通过可组合性释放设计灵活性(Composable Freedom)。正如源码中的variantGroup函数所示,它本质上是在构建一套DSL,让样式开发更贴近现代前端工程化实践」
注:若面试官追问缺点,可补充:「初期学习曲线陡峭,但2周适应期后生产力会指数级上升。建议搭配@tailwindcss/forms等官方插件增强体验」
通过展示原理级认知+数据化论证,可将此题转化为技术领导力展示点。
Vite 与 Webpack 的性能差异与打包方式对比分析
核心差异点
开发环境模式
- Vite
原生 ES Module (ESM):浏览器直接加载 ES 模块,无需打包整个应用(来源1/2/6)。
按需编译:仅编译当前请求的模块(如首页仅编译入口文件,其他模块按需动态加载),极大减少冷启动时间(来源1/4/10)。
预构建优化:使用 esbuild(Go 语言编写)预构建依赖项,速度比传统工具快 10-100 倍(来源1/6)。
- Webpack
全量打包:启动时需打包整个应用(包括未使用的代码),复杂度高时耗时显著增加(来源4/7)。
基于 JavaScript:Loader 和 Plugin 链式处理文件,效率受限于单线程 JS 执行(来源5/9)。
生产环境构建
- Vite
使用 Rollup 进行生产打包,支持 Tree-shaking 和 Code-splitting,但依赖预构建缓存(来源1/8)。
- Webpack
自研打包机制,通过复杂配置优化输出,但默认配置下资源处理冗余(来源7/9)。
关键性能优势
- 启动速度
Vite 通过 按需编译 + 预构建,冷启动时间可缩短至 Webpack 的 1/10(来源2/6)。例如,一个中型网站项目,Webpack 启动需 30 秒,Vite 仅需 1-3 秒。
Webpack 的打包器设计导致 启动时必须处理全部依赖关系(来源4/7)。
- 热更新(HMR)
Vite:基于 ESM 的精准更新,仅重载修改的模块,毫秒级响应(来源6/8)。
Webpack:需重新构建依赖链,文件越多更新越慢(来源4/9)。
- 生态工具效率
Vite:核心工具链(如 esbuild、Rollup)均采用高性能语言(Go/Rust),并行处理能力强(来源1/6)。
Webpack:依赖 Babel/TS Loader 等 JS 工具链,单线程处理易成瓶颈(来源5/7)。
打包方式对比

适用场景建议
选择 Vite:✅ 需要快速启动的现代前端项目(如 Vue/React 单页应用)✅ 重视开发体验的轻量级应用或微前端子模块✅ 需要深度整合 ESM 和浏览器原生特性的场景
选择 Webpack:✅ 传统大型项目(需兼容旧版浏览器)✅ 需要复杂自定义构建流程(如多入口高级优化)✅ 依赖 Webpack 特有插件(如 Module Federation)
实测对比:某电商项目迁移至 Vite 后,开发环境构建时间从 45 秒降至 3 秒,热更新速度从 5 秒缩短至 50 毫秒(来源3/6)。这种效率提升在高频迭代的业务场景中优势尤为显著。
为什么vite编译的时候会首页白屏
- 依赖预构建比较耗时间
- 首次启动的时候会对
node_modules
进行预构建,在依赖较多的情况下会导致时间变长,首页会白屏 - 所以需要对预构建进行缓存,然后排除掉一些无需构建的依赖
- 模块动态加载延迟
- 开发模式机制:vite不打包代码,直接通过esmodule来加载模块,所以加载时间和文件量大小有关系
- 优化方案:可以使用动态导入拆分代码
你提到vite编译的时候会使用esmoudle,那什么是esmodule
ESModule
是指 ECMAScript 模块,也称为 ES 模块。它是 ES6(ECMAScript 2015)标准中引入的一种模块系统,用于在 JavaScript 中导入和导出模块。相较于之前常用的 CommonJS 模块系统(如 Node.js 中的 require
和 module.exports
),ES 模块有以下几个主要特点和优势:- 静态结构 ES 模块的依赖关系是静态的,这意味着:
- 模块的导入和导出在编译或打包阶段就能被分析到,便于进行代码优化,比如 Tree Shaking(剔除未使用的代码)。
- 开发工具和打包工具(如 Vite)可以更早地确定模块之间的依赖关系,实现更高效的构建和热更新。
- 简洁的语法
ES 模块使用
import
和export
关键字来组织代码。例如:
- 异步加载和浏览器支持
现代浏览器已经原生支持 ES 模块,可以通过
<script type="module" src="..."></script>
标签直接加载模块。 同时,ES 模块具有异步加载的特性,提高了页面加载和执行的性能。
- 模块作用域 每个 ES 模块都有自己的作用域,不会污染全局命名空间。这有助于管理大型应用、提高代码的安全性和可维护性。
Vite 与 ESModule
Vite 充分利用了 ES 模块的特性,这也是它备受欢迎的原因之一:
- 极速启动与热更新: 利用 ES 模块的静态分析特性,Vite 可以更快地解析依赖关系,实现高效的热模块替换(HMR),大幅提升开发体验。
- 基于原生 ESM 构建: Vite 在开发环境下直接使用浏览器支持的 ES 模块特性,这意味着无需传统打包工具复杂的编译步骤,启动速度更快,反馈更及时。
总结来说,ESModule(即 ECMAScript 模块)是 JavaScript 的原生模块系统,提供了一种静态、简洁、高效的模块组织方式。Vite 使用 ESModule 来优化构建流程和开发体验,实现快速响应的热更新和高效打包,这正是现代前端构建工具追求的目标之一。
你平时在react里面是怎么做性能优化的
useMeomo
memo
useCallBack
Suspense/lazy
更新于2月18日
如何优化冷启动速度和HMR
- 可以通过vite的
optimizeDeps
来指定或者排除依赖,减少预构建的负担
- 热更新是通过react-HMR这个插件来实现,同时我们要确保自己组件的模块适中(保持组件的合理拆分,优化更新速度)
- 合理的设置路径别名,减少路径解析带来的开销,同时优化代码结构,让他更清晰
- 使用代码拆分和动态引入,通过
React.layzu + Suspense
来进行懒加载,减少首次进入屏幕时候的加载体积
如何减少打包的体积
- 代码层面:利用vite的tree-shaking,代码拆分和动态导入等功能,在打包的时候去掉不必要的代码
- 工具方面:合理配置压缩、混淆和外部化依赖,确保生产环境构建的高效
- 依赖管理:减少不必要的第三方依赖,并使用轻量版的替代比较重的库,然后可以按需引入我们所需要的资源
- 资源管理:我们可以生成打包分析报告,查看一下打包中比较重的组件,对代码进行优化和修改,使用如延迟加载一类的功能
- 静态资源的优化:减少不必要的静态资源、图片或者css文件,可以降低打包体积,也可以说是代码拆分
缓存策略有哪些
浏览器缓存分为两种:强缓存(Strong Cache)/协商缓存(Conditional/Negotiation Cache)
介绍
强缓存
当资源在客户端背缓存以后,在缓存的有效期内,浏览器会直接使用缓存的的内容,而不会向浏览器发送请求,从而节省了请求和响应的时间和流量
特点
- 在资源有效期内,浏览器不会向服务器发送请求
- 需要等待缓存过期才会发送新的请求,才能检查资源是否有更新
- 大大减少了请求次数,但是用户可能会获取到过时的数据。
协商缓存
协商缓存工作时,浏览器在发起请求的时候,会同时携带缓存的相关的资源相关信息(上次返回的时间戳或标识符),服务器根据这些信息资源判断是否有更新 - 若资源未更新,返回304 Not Modified
,浏览器继续使用本地缓存,响应体为空 - 若资源已经更新,则服务器返回新的资源数据,浏览器更新缓存
特点
- 每次请求都会与服务器通信,服务器返回304时,只有响应头会被传输,响应体为空,依然比完全重新下载少了数据传输量
- 能够及时的反应资源是否更新,适用于频繁更新的资源
它们之间的主要区别
- 请求次数
- 强缓存:在缓存有效期内,浏览器不会发送http请求,直接使用本地缓存
- 协商缓存:每次都会向服务器发送请求,虽然只发送请求头,浏览器判断后返回304状态码,但仍会产生一次http请求
- 实时性
- 强缓存:如果资源更新而客户端缓存仍未超时,则用户可能会看到过时的数据。
- 协商缓存:服务器端可以判断资源是否更新,保证了较高的数据实时性。
- 性能和流量
- 强缓存:整体上可以减少请求和响应的 HTTP 请求总数,从而降低网络延迟和流量消耗。
- 协商缓存: 虽然每次请求都需要与服务器沟通,但在资源未更新时只传输很少的数据(304 响应),在数据准确性上有一定保障。
小结
- 强缓存适用于不会频繁更新的静态资源,如图片、字体、第三方库等,能极大提高响应速度和降低服务器压力。
- 协商缓存适用于可能更新的资源,当更新频率较高时,通过 304 状态码既保证了数据的实时性,也减少了不必要的数据传输。
如何提高首评加载速度
- 代码拆分以及懒加载
- 通过
react
提供的Suspense和lazy,将不同部分的代码按需导入,如果还想优化,甚至可以把每个组件做到按需导入,减少打包的体积 - 利用vite/webpack的代码拆分功能
- 资源压缩与优化
- 使用压缩工具对图片进行优化,采用更高效的格式
- 使用gzip对静态资源进行压缩,减少传输体积
- 使用ssr(服务端渲染)或者ssg(静态生成)
- 减少第三方的依赖,利用tree-shaking去除无效代码,并做好按需引入的功能模块
- 监控分析:
- 通过性能监控和分析工具定期检查首屏加载情况,找到相关瓶颈
- 根据报告进行针对性的优化,不断改进加载策略
以上内容是我真的能想起来的一些内容,下面还有一些其他的
- 预加载与资源优先级管理
- 预加载/预取: 使用
<link rel="preload">
将关键资源(CSS、JS)提前加载,或使用<link rel="prefetch">
为后续页面加载做准备。 - 文件顺序: 在 HTML 中调整资源引用顺序,确保关键资源先加载,非关键资源异步加载。
浏览器请求一个url所经历完整的网络流程以及涉及到的网络协议
- 作者:阿杜
- 链接:http://try2relax.top/JavaScript/InterviewQuestion0217
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。