【前端面试】工程化相关

包管理器

Pnpm是怎么解决幽灵依赖的

hard link + symbolic link

如何确定一个项目用的包管理器版本:enigne字段
1
2
3
4
"engines": {
"node": ">=14.0.0",
"npm": ">=7.0.0" // 项目要求的 npm 最低版本
}
如何避免业务项目被发到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入口
alt text

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实现

在做按需加载时,如何去实现一个asyncComponent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Home = AsyncComponent(() => './test.tsx')

const AsyncComponent = (importComponent) => {
return funciton() {
const [component, setComponent] = useState(null)
useEffect(() => {
importComponent.then((cmp) => {
setComponent(cmp.default)
})
}, [])

return component ? <component/> : null
}
}