【前端面试】工程化相关
包管理器
Pnpm是怎么解决幽灵依赖的
hard link + symbolic link
如何确定一个项目用的包管理器版本:enigne字段
1 | "engines": { |
如何避免业务项目被发到npm上
在 package.json 中添加 private 字段
在项目的 package.json 文件中添加 private: true 字段,这是最直接的方式。当 private 为 true 时,npm 会拒绝发布该项目。
使用 .npmignore 或 .gitignore 文件
将项目中的所有文件添加到忽略列表,确保没有文件被发布。.npmignore 会覆盖 .gitignore 的规则。
设置预发布钩子
在 package.json 中添加 prepublishOnly 脚本,使其在发布前失败:
使用 .npmrc 文件锁定发布源
在项目根目录创建 .npmrc 文件,设置为私有源或无效源:
package.json
package.json 中 main/module/browser/exports 字段有何区别
以antd为例,main一般放Commonjs入口,module放ESModule入口,unpkg放UMD入口
peerdependencies相关
peerDependencies用于声明当前包依赖的宿主环境必须安装的依赖,主要作用是约束宿主项目的依赖版本,确保插件与宿主环境兼容
Webpack
webpack 的 loader 的机制是什么,有哪些常用的loader?
本质上是一个函数,做文件转换,处理的操作,输入输出都是文件,常用loader有ts-loader,less-loader,babel-loader
webpack默认打包成什么产物?UMD、CJS、ESM
类似于UMD的那种iife
webpack 的插件系统是什么,有哪些常用的plugin?
用于在webpack打包构建的生命周期里去做一些逻辑,常用plugin有:
- HtmlWebpackPlugin:生成 HTML 文件并自动注入打包后的 JS/CSS
- TerserPlugin:压缩和混淆 JavaScript 代码
- SplitChunksPlugin:自动分割公共代码和第三方库(Webpack 内置)
- BundleAnalyzerPlugin:产物体积分析
webpack有哪两种代码分割模式
打包分离(bundle spliting)
为了更好的缓存,把代码分割成一个个小文件,见下文splitchunk
代码分离(code spliting)
动态,按需的加载代码,通过 import()实现
import() 函数是由 TS39 提出的一种动态加载模块的规范实现,其返回是一个 promise。
webpack 检测到这种import() 函数语法会自动代码分割,把这段代码打包成单独的chunk。使用这种动态导入语法代替以前的静态引入,可以让组件在渲染的时候,再去加载组件对应的资源,
webpack 通过创建 script 标签来实现动态加载,找出依赖对应的 chunk 信息,然后生成 script 标签来动态加载 chunk,每个 chunk 都有对应的状态:未加载、加载中、已加载。
webpack splitchunk有哪些配置
- chunks: ‘async’, // 分割哪些类型的模块(async、initial、all)
- minSize: 20000, // 生成 chunk 的最小体积(字节)
- minRemainingSize: 0, // 确保拆分后剩余的最小 chunk 体积
- minChunks: 1, // 被多少模块共享时才分割
- maxAsyncRequests: 30, // 按需加载时的最大并行请求数
- maxInitialRequests: 30, // 入口点的最大并行请求数
- enforceSizeThreshold: 50000, // 强制拆分阈值
- cacheGroups: { // 缓存组,自定义拆分策略
defaultVendors: {
test: /[\/]node_modules[\/]/, // 匹配 node_modules 中的模块
priority: -10, // 优先级
reuseExistingChunk: true, // 复用已存在的 chunk
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
Webpack运行时runtime都做了什么
webpack 的 runtime,也就是 webpack 最后生成的代码,实现了一个自定义的模块加载系统,替代浏览器原生的模块机制
webpack_modules
维护一个所有模块的数组。将入口模块解析为 AST,根据 AST 深度优先搜索所有的模块,并构建出这个模块数组。每个模块都由一个包裹函数 (module, module.exports, webpack_require) 对模块进行包裹构成。
webpack_require(moduleId)
手动实现加载一个模块。对已加载过的模块进行缓存,对未加载过的模块,执行 id 定位到
webpack_modules
webpack_modules中的包裹函数,执行并返回 module.exports,并缓存
webpack_require(0)
webpack_require_(0)运行第一个模块,即运行入口模块,另外,当涉及到多个 chunk 的打包方式中,比如 code spliting,webpack 中会有 jsonp 加载 chunk 的运行时代码。
性能优化
有哪些常见的性能优化指标,如何计算出这些指标
如何计算:通过Performance API获取performance.getEntriesByType('paint')
- FP(首次绘制)
表示渲染出第一个像素点。FP一般在HTML解析完成或者解析一部分时候触发。标志着页面开始有视觉反馈 - FCP(首次内容绘制)
表示渲染出第一个内容,这里的“内容”可以是文本、图片、canvas。用户首次看到实际内容的时间,更贴近真实体验。 - FMP(First Meaningful Paint,首次有效绘制)
FMP 已被弃用,更推荐用 LCP(最大内容绘制) 来衡量“主要内容多快出来”。 - DCL(DOMContentLoaded)
HTML 解析完成且 DOM 树已构建完毕时触发,但此时 CSS、图片等外部资源可能仍在加载中。标志着可以安全操作 DOM,是前端交互初始化的重要时机。 - FID(首次输入延迟)
用户首次与页面交互(点击按钮、输入框等)到浏览器实际能够处理该交互的时间差。 - TTI(可交互时间)
页面不仅完成渲染(如 LCP),且能可靠响应用户输入的时间点。
Core Web Vitals(谷歌核心指标)
LCP(最大内容渲染)
最大内容绘制,可视区域内最大的内容元素完成渲染的时间点,用于衡量主内容可见的时间,反映首屏加载速度INP(Interaction to Next Paint)
用户交互后到下一次有反馈的绘制的延迟(综合多次交互)CLS(Cumulative Layout Shift)
页面加载过程中意外布局偏移的累计分数(会不会抖、会不会点错)
关注哪些性能指标,怎么实现的
首字节时间
HTML解析完成,开始渲染第一个字节的时间。
应该也是借助Performance API实现
DomReady时间
和DCL一致,HTML文档被完全加载和解析后触发,此时外部资源可能仍然在下载
首屏时间
页面完全渲染完毕的结束时间。
借助MutationObserver和PerformanceObserver实现
- MutationObserver:MDN: MutationObserver
- PerformanceObserver:MDN: PerformanceObserver
- 实现方案:

在做按需加载时,如何去实现一个asyncComponent
1 | const Home = AsyncComponent(() => './test.tsx') |