tree shakeing
摇树是怎么实现的
- Tree Shaking 实现关键是:*ES6 (es module)模块是 “静态” 的
- 打包工具从「入口文件」开始遍历所有代码,标记死代码并删掉
组件库按需加载
- 打包插件实现将默认的import方式,转成组件的 JS、CSS 分别import的写法
ESM 转 CJS
- 本质是替换
import/export为require/module.exports - Babel/Rollup为转换常用工具
webpack
loader 和 plugin 的区别
- Loader 管「文件转换」:微观处理单个文件,让 Webpack 能识别非 JS/JSON 文件;如
css-loader:解析.css文件里的@import、url(),把 CSS 转成 Webpack 能识别的模块;style-loader:把css-loader处理后的 CSS 注入到 HTML 的<style>标签里;babel-loader:把 ES6+ 语法的 JS 转成 ES5,兼容老浏览器;ts-loader:把 TypeScript 代码转成普通 JS;file-loader:处理图片 / 字体等静态资源,输出到指定目录并返回文件路径。
- Plugin 管「流程扩展」:宏观控制打包全流程,实现各种自动化、优化功能。比如压缩代码、生成 HTML
plugin 的实现原理(node 的 eventbus 模块,发布订阅)
定义一个具有 apply 方法的 prototype 对象,里面会有一个hook对象可以对webpack事件进行监听控制,进而控制打包流程
图片懒加载原理
本质:页面滚动时,只有图片快进入屏幕可视区域了,才加载这张图片
一开始加载缩略图,真实图片url放在data-src属性里,监听滚动事件,待页面滚动到图片进入屏幕可视区域,替换真实属性加载图片
优化:
- 滚动事件频繁触发需要加防抖,
- 不用监听滚动事件,使用IntersectionObserver现代api,自动判断元素是否进入视野
虚拟列表原理
本质:只渲染可视区域的列表项,用占位容器撑出总高度,滚动时只替换可视区的内容和位置
简单说一下vite和webpack区别
| 维度 | Webpack | Vite |
|---|---|---|
| 启动速度 | 慢(项目越大越慢) 原因:启动时要遍历、打包所有代码,生成 bundle 文件 | 极快(毫秒级) 原因:不打包,直接用 ESM,只处理入口文件,按需编译 |
| 热更新速度 | 慢(改代码后要重新打包相关模块) | 快(只编译修改的文件,不用动其他代码) |
| 构建思路 | 「打包优先」 不管代码是否用到,先打包成兼容所有环境的 bundle | 「按需编译」 开发环境不打包,生产环境用 Rollup 轻量打包(体积更优) |
| 底层工具 | 自身处理模块编译(比如 babel 处理 ES6+) | 开发环境用 esbuild(Go 语言写的,编译速度是 babel 的几十倍),生产环境用 Rollup |
| 兼容场景 | 全能(支持老项目、CommonJS/ESM、各种复杂配置) | 主打现代前端(只支持 ESM,老项目 / CommonJS 需适配,配置更简洁) |
| 使用成本 | 配置复杂(loader/plugin 多,需手动配) | 配置极简(内置大部分常用功能,比如 Vue/React/TS 开箱即用) |
| 总结 | ||
| Webpack 是「先打包再运行」,适配全场景但慢;Vite 是「不打包直接运行,按需编译」,只适配现代环境但极快。 |
计算机网络
类比 “快递运输流程”,每层只干自己的活,层层配合:
- 物理层:最底层(网线、光纤、网卡),负责 “传电信号 / 光信号”(比如快递的 “运输路线,如公路、铁路”);
- 数据链路层:负责 “相邻设备通信”(比如路由器和电脑之间的连接,像 “快递站点之间的短途运输”);
- 网络层:负责 “跨网段找路”(比如从北京到上海的快递路由,核心是 IP 地址,像 “快递的全国路由规划”);
- 传输层:负责 “端到端通信控制”(TCP/UDP 在这里,像 “快递的运输方式:普通快递 / TNT 加急”);
- 会话层:负责 “建立 / 维持 / 断开通信会话”(比如登录网站时的连接会话,像 “快递员和收件人确认收货”);
- 表示层:负责 “数据格式转换”(比如加密、压缩、编码,像 “快递包裹的包装 / 拆包”);
- 应用层:最顶层(用户直接用的),负责 “提供应用服务”(比如 HTTP、FTP、微信,像 “用户下单 / 收件的操作”)。
记忆口诀:物数网传会表应(从下到上,记首字)。
2. TCP 和 UDP 的区别(通俗类比:打电话 vs 发短信)
| 对比维度 | TCP(打电话) | UDP(发短信) |
|---|---|---|
| 连接要求 | 必须先建立连接(三次握手),像打电话要等对方接 | 无连接,直接发,像发短信不用等回复 |
| 可靠性 | 可靠(丢包重传、按序到达),像打电话能确认对方听到 | 不可靠(丢包不重传、无序),像发短信可能丢件 / 乱序 |
| 速度 | 慢(要确认、重传) | 快(无额外开销) |
| 适用场景 | 需可靠传输的(网页、文件下载、登录) | 需高速实时的(视频通话、直播、游戏) |
记忆要点:TCP 重可靠(连、稳、慢),UDP 重速度(无连、快、糙)。
3. TCP 和 UDP 属于哪一层?
属于 传输层(七层模型的第 4 层)。核心作用:在网络层(IP 找路)的基础上,给应用层提供 “端到端的通信服务”(比如区分同一设备上的不同应用,靠端口号,像快递到小区后,区分不同住户)。
面试总结(直接背)
- 七层模型:物数网传会表应,从下到上,每层负责不同通信环节;
- TCP vs UDP:TCP 连、稳、慢(网页 / 文件),UDP 无连、快、糙(直播 / 游戏);
- 所属层级:两者都在传输层,负责端到端通信控制。
闭包是什么
核心定义(一句话):
闭包就是内层函数能访问外层函数的变量,且外层函数执行完后,这些变量不会被销毁。
面试记忆要点:
- 本质:函数嵌套 + 变量跨作用域访问;
- 关键:外层函数执行后,变量不销毁(常驻内存);
- 用途:保存变量(如防抖 / 节流)、私有化变量(避免全局污染);
- 注意:滥用会内存泄漏(变量一直占内存,需手动释放)。
极简例子(辅助理解):
function outer() {
let num = 1; // 外层变量
return function inner() { // 内层函数(闭包)
console.log(num); // 能访问num,且outer执行完num不销毁
};
}
const fn = outer();
fn(); // 打印1(闭包生效)
在react 里函数闭包下如果更新状态,可能会遇到什么问题?怎么解决
React 函数组件中,闭包会捕获当前渲染时的状态值,如果在异步操作(如 setTimeout、事件监听、网络请求)里访问状态,拿到的是「旧状态」,而非最新的状态 —— 因为闭包保存的是当时渲染的状态,后续状态更新触发新渲染,但旧闭包仍指向旧值
解决:
- 函数式更新(最推荐,适配状态更新场景)
- useRef 存最新值(适配「读取最新状态」场景)
- 清理副作用(避免旧闭包生效)