尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

前端面试webpack vite

前端面试webpack  vite 前端工程化开发环境1准备阶段技术选型和初始化规范分支规范、UI规范2开发阶段开发、打包测试mocha、jest、Cypress 测试用例保证代码质量3发布阶段CI/CD持续集成和部署、docker、nginx开发体验前端基础数据FCP、LCP异常数据 接口异常、脚本异常资源 白屏率、秒开率performance APIutils业务API 业务组件 环境兼容core全局配置 数据处理 埋点上报一、webpack1、前端代码为什么要进行构建和打包1模块化开发2编译高级语法less、scss、ES6、JSX、TSBabel转译将 ES6/TS/JSX 转换为 ES5 代码PolyfillBabel默认只转换新的JS句法不转换新的API如Iterator、Generator、Set、Map、Promise等全局对象以及一些定义在全局对象上的方法如Object.assignCss预处理3性能优化代码压缩压缩 JS、 CSSTree Shaking删除未使用的代码依赖 ESM 静态分析代码分割Code Splitting拆分包按需加载如路由懒加载缓存策略生成 [contenthash] 文件名利用浏览器缓存4开发体验热更新HMR修改代码后局部更新保留应用状态Dev Server提供本地开发环境如 webpack-dev-serverSource Map调试压缩后的代码时定位到源码5其他资源管理与自动化Loader 处理资源、HTML 中自动引入打包后的 JS/CSS工程化与团队协作Lint 工具、TS类型检查、CI/CD 集成框架支持React、vue等统一高效的开发环境统一的构建流程和产出标准集成公司构建规范提测、上线2、基本配置Entry入口指定打包的起点文件如 src/index.jsOutput输出定义打包后的文件存放位置如 dist/main.jsLoaders加载器处理不同类型的文件处理ES6 babel-loader处理样式 style-loader css-loader less-loader postcss-loaderloader执行顺序从后往前处理图片 file-loader url-loaderPlugins插件扩展功能如 HTML 生成、代码压缩Mode模式development开发或 production生产环境管理DefinePlugin 注入变量区分 development 和 production扩展能力自定义Loader和Plugin3、常见的loader和plugin区别常见的loader如下babel-loader/swc-loader转译 ES6/JSX/TypeScript → ES5css-loader解析 CSS 中的 import 和 url()处理模块化 CSSstyle-loader将 CSS 注入到 DOM通过 style 标签,生产环境用MiniCssExtractPlugin.loader替代将项目中散落的 CSS 代码从 JavaScript bundle 中提取出来生成独立的 CSS 文件。开发环境中通常仍使用 style-loader 而非插件因为它支持热模块替换HMR能实现 CSS 的实时更新。sass-loader/less-loader编译 SCSS/SASS/LESS → CSSpostcss-loader处理 CSS 兼容性如自动添加前缀file-loader将文件如图片/字体输出到 dist 目录返回文件的访问路径URLurl-loader小文件转为 Base64 内联大文件交给 file-loaderraw-loader允许将文件处理成一个字符串导入file-loader、url-loader、raw-loader在webpack5被取代type: asset/source功能相当于 raw-loaderasset/inline功能相当于url-loaderasset/resource功能相当于file-loaderasset默认会根据文件大小来选择使用哪种类型当文件小于 8 KB 的时候会使用 asset/inline否则会使用 asset/resource也可手动进行阈值的设定常见的plugin如下HtmlWebpackPlugin打包结束后自动生成一个html文件并把打包生成的js模块引入该html中mini-css-extract-plugin提取css到一个单独的文件中DefinePlugin允许在编译时创建配置的全局对象是一个webpack内置插件不需要安装ReactRefreshWebpackPluginWebpack 配置中用于启用 React 组件热更新HMR的插件配置SplitChunksPlugin用于优化代码分割的核心插件css-minimizer-webpack-plugincss压缩terser-webpack-pluginjs压缩ImageMinimizerWebpackPlugin图片压缩区别loader模块转换器处理单个文件转换、编译。webpack 本质上只能处理 JavaScript 和 JSON 文件loader 的作用是让 webpack 能够处理其他类型的文件并将它们转换为有效模块供 webpack 进一步处理plugin功能扩展器针对整个构建流程如打包优化、资源管理、环境变量注入等。plugin 用于扩展 webpack 的功能解决 loader 无法完成的任务。它可以监听 webpack 生命周期中的关键事件如编译开始、模块解析完成、输出文件前等并在事件触发时执行自定义逻辑。参考常见的loader和plugin4、module、chunk、bundle分别是什么区别module模块各种源码文件Webpack 将所有文件包括 JS、CSS、图片等视为模块并通过依赖关系如 import/require构建依赖图chunk代码块构建过程中的中间产物由一个或多个模块组成bundle打包产物chunk经过编译和打包后的最终输出文件如 .js、.css直接提供给浏览器运行的静态资源。产生 Chunk 的场景入口文件Entry每个入口文件生成一个 Chunk。动态导入Dynamic Importimport() 语法会自动生成新的 Chunk。代码分割SplitChunksPlugin提取公共代码或第三方库到独立 Chunk。5、webpack构建流程1初始化读取合并配置参数、创建 Compiler 实例、加载插件。配置包括优先级webapck.config.js. npm script “start”: “webpack --XX” cli env。从配置文件和 Shell语句中读取配置并合并基于最终配置创建 Compiler 对象它负责统筹整个构建流程暴露生命周期钩子如 run、compile、done 等。根据配置加载所有插件并调用插件的 apply 方法。2编译阶段根据配置中的 entry 找到入口文件。从入口文件开始根据 module.rules 配置使用对应的 loader 对模块进行转换按从右到左的顺序执行 loader 链。将转换后的代码解析为 AST分析 import、require 等依赖语句。递归解析所有依赖模块直到处理完毕。3生成阶段根据入口文件和动态导入的拆分点将模块组合成 chunk优化处理触发各种优化阶段的插件执行。4输出资源emit assets把Chunk转换成最终输出的文件内容如合并模块、注入 runtime 代码根据 output 配置如 path、filename将所有资源写入到指定的输出目录默认 dist。在完成时触发 done 等钩子。流程图示例参数合并 → 创建Compiler → 加载插件 → 解析入口 →递归编译模块loader处理 → 生成依赖图 →优化代码分割、tree-shaking → 生成输出内容 →写入文件 → 完成构建6、webpack性能优化代码分割Code Splitting减少首屏加载时间按需加载代码。包括入口分割、动态导入、SplitChunksPlugin公共代码提取缓存优化[contenthash]Tree shaking删除未使用的代码压缩代码TerserPlugin等懒加载Lazy Loading延迟加载非关键资源预加载Preload/PrefetchPreload 和 Prefetch 都是 HTML 中用于优化资源加载的 标签 rel 属性值目的是通过提前加载关键资源提升页面性能。Preload 提前加载当前页面必需的关键资源如关键 CSS、字体Prefetch 空闲时提前加载未来页面可能需要的资源如跳转页的 JS/CSS多进程并行处理tree Shaking什么时候失效tree shaking 用于消除代码中未使用的部分,依赖于ES Module的静态语法分析。Tree Shaking 的触发条件1静态模块必须使用 ES6 模块语法import/export。CommonJS 的 require/module.exports 是动态的无法被 Tree Shakingimport(‘./module.js’).then(…) 动态导入会导致整个模块无法被优化。2无副作用Tree Shaking 只会移除没有副作用的代码。如果模块执行时会影响外部状态如修改全局变量、DOM 操作则不会被移除。3未被引用未被其他模块引用的代码才会被移除。4工具支持Webpack/Rollup 等工具配置正确。Webpack必须启用 production 模式自动开启 Tree Shaking。使用 optimization.usedExports: true 标记未使用的导出。package.json中使用 sideEffects: false 告诉 Webpack 哪些模块没有副作用。code splitting目的减少首屏加载时间只加载当前页面需要的代码。优化缓存独立模块可单独缓存避免整体重新加载。提高运行时性能减少主线程阻塞时间。实现方式1入口分割。手动配置多个入口适用于多页面应用MPAmodule.exports { entry: { home: ./src/home.js, about: ./src/about.js, }, output: { filename: [name].[contenthash].js, }, };2动态导入使用 import() 语法ES6 动态导入返回 Promisebutton.addEventListener(click, () { import(./module.js).then(({ func }) { func(); }); });3懒加载组件React/Vue 等框架React使用 React.lazy 和 Suspense。Vue使用异步组件定义const LazyComponent React.lazy(() import(./LazyComponent)); function App() { return ( div React.Suspense fallback{divLoading.../div} LazyComponent / /React.Suspense /div ); }4SplitChunks 插件Webpack自动分割共享模块提取公共依赖到单独的 chunk。配置项minSize、minChunks、cacheGroups 等。module.exports { optimization: { splitChunks: { chunks: all, // 分割所有类型的 chunks cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, name: vendors, }, }, }, }, };最佳实践优先使用动态导入灵活且自动化程度高。避免过度分割过小的 chunks 会增加 HTTP 请求数。预加载Preload与预取Prefetch!-- 预加载当前页面急需的资源 -- link relpreload hrefcritical.js asscript !-- 预取未来可能需要的资源 -- link relprefetch hrefoptional.js使用分析工具 webpack-bundle-analyzer分析包大小7、webpack5相比webpack4新增了哪些功能1资源模块Asset Modules在 Webpack5 之前一般都会使用raw-loader、file-loader、url-loader来处理一些常见的静态资源比如 PNG 图片、SVG 图标等等。Webpack5 提供了内置的静态资源构建能力取代了 file-loader/url-loader/raw-loadermodule.exports { ..., module: { rules: [ { test: /\.(png|jpg|svg|gif)$/, type: asset/resource, generator: { // [ext]前面自带. filename: assets/[hash:8].[name][ext], }, }, ], }, }2内置 FileSystem Cache 能力加速二次构建Webpack5 之前我们会使用 cache-loader缓存一些性能开销较大的 loader 或者是使用 hard-source-webpack-plugin为模块提供一些中间缓存。在 Webpack5 之后默认就为我们集成了一种自带的缓存能力。module.exports { ..., cache: { type: filesystem, ..., }, }3更强大的 Tree Shaking 能力Tree Shaking 能力是指能够在打包的过程中移除 JavaScript 上下文中未被引用到的变量借以次来减少打包后的体积。webpack已经默认开启了这个功能无需其他配置但是使用这个会有条件。webpack5支持嵌套的 tree-shaking、支持 CommonJS 的 tree-shaking、支持内部模块的 sideEffects 标记4移除/废弃功能移除了 Node.js polyfills需要手动添加、弃用了 require.include已完全移除、移除了 IgnorePlugin 的某些用法需要显式指定上下文5长期缓存优化Long term cache: 防止模块顺序变化导致ID变化避免无关修改影响所有chunk的hash6Module Federation模块联邦支持微前端架构跨应用共享模块Webpack 5 的这些改进使得构建更快、输出更小、配置更简单参考Webpack5 新特性8、webpack的热更新原理热模块替换Hot Module Replacement, HMR它允许在不刷新整个页面的情况下替换、添加或删除模块同时保留应用的运行状态如组件的 props、state、DOM 状态等。HMR 仅用于开发环境生产环境会移除相关代码避免性能损耗。核心是通过WebSocket 建立实时通信实现 “模块变化检测→差异传输→客户端更新” 的闭环。具体的1构建阶段标记模块生成 HMR 相关代码webpack 构建时会为每个模块注入 HMR 运行时代码如 module.hot API用于后续模块替换。同时生成 “模块依赖图” 和 “更新清单”记录模块间的依赖关系便于后续精准定位需要更新的模块。2监听阶段检测文件变化生成更新包开发服务器如 webpack-dev-server通过 watch 模式监听文件变化。当文件修改时webpack 会增量构建只重新编译变化的模块而非全量构建生成两个关键文件更新清单update manifest通常是 [hash].hot-update.json包含需要更新的模块列表和对应的更新包路径。模块更新包update chunk通常是 [id].[hash].hot-update.js包含变化模块的新代码。3通信阶段通知客户端有更新开发服务器通过 WebSocket 将 “更新清单” 的 URL 发送给浏览器客户端。客户端浏览器接收到通知后通过 HTTP 请求获取更新清单和对应的模块更新包。4替换阶段客户端应用模块更新客户端的 HMR 运行时webpackHotUpdate解析更新清单确定需要更新的模块。调用模块的 module.hot.accept 回调如果模块定义了 HMR 处理逻辑执行自定义更新逻辑如替换组件、更新样式等。移除旧模块的依赖引用添加新模块到依赖图中完成热替换。若模块未定义 HMR 处理逻辑HMR 会降级为刷新页面。参考Webpack HMR 原理解析9、webpack proxy工作原理为什么能解决跨域中间服务器webpack-dev-server将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在一起目的是伪类提高日常开发效率只用在开发阶段。devServer里面proxy是关于代理的配置proxy ⼯作原理实质上是利⽤ http-proxy-middleware 这个 http 代理中间件实现请求转发给其他服务器。工作流程如下1浏览器发送请求受同源策略限制前端代码请求 http://localhost:8080/api/users本地开发服务器。2webpack-dev-server 拦截请求检测到 /api 匹配代理规则于是 不直接返回数据而是 转发请求 到 http://backend-server.com/api/users。3目标服务器返回数据后端服务器如 http://backend-server.com处理请求返回数据给 webpack-dev-server。4webpack-dev-server 返回数据给浏览器浏览器接收到数据认为请求是来自 http://localhost:8080因此 不会触发跨域错误。浏览器的 同源策略Same-Origin Policy 限制的是前端页面如 http://localhost:8080 不能直接通过 XMLHttpRequest 或 fetch 访问 不同源如 http://backend-server.com 的 API。但 代理服务器 不受此限制因为服务器之间没有跨域限制同源策略仅适用于浏览器。webpack-dev-server 作为中间代理代替浏览器发送请求拿到数据后再返回给前端浏览器看到的是同源响应因此不会报跨域错误。Webpack DevServer Proxy本地环境nginx反向代理生产环境10、hash类型[hash] 整个项目构建级别的 hash一次打包全局同一个 hash[chunkhash] chunk 级别的 hash一个 chunk 一个 hash[contenthash] 单个输出文件asset自身内容的 hash文件粒度二、vite为什么打包速度快1、开发环境基于原生 ESM 的 “按需编译”告别全量打包。传统工具如 Webpack在开发时的核心流程是递归解析所有模块依赖构建完整的依赖图将所有模块打包成一个或多个 bundle.js即使很多模块当前用不到启动开发服务器提供打包后的 bundle。项目越大依赖越多启动和更新速度越慢。Vite按需编译开发服务器只编译当前页面需要的模块2、依赖预构建用 esbuild 处理第三方依赖。项目依赖分为两类源码src/开发者编写的代码频繁变动需要即时编译依赖node_modules/第三方库如 React、Vue极少变动但可能包含 CommonJS 模块或复杂依赖关系。传统工具处理第三方依赖时需要通过 babel-loader 等工具逐模块转译且重复处理未变动的依赖耗时较长。Vite 在首次启动时会对 node_modules 中的依赖做一次 “预构建”并缓存结果后续启动直接复用将 CommonJS/UMD 依赖转换为 ESM 格式合并多个小文件减少网络请求。预构建结果会缓存到 node_modules/.vite仅在依赖package.json 或锁文件变化时重新构建3、热模块替换HMR避免全量刷新。Vite 的 HMR 基于原生 ESM 实现当某个模块修改时仅需向浏览器发送 “更新指令”无需重新加载整个应用4、生产环境基于 Rollup 的高效打包兼顾速度与体积vite configresolvedevServerbuildoptimizeDepsVite 的 HMR热更新是如何实现的Vite 的 HMR 基于原生 ESM 实现核心流程服务器与浏览器通过 WebSocket 建立连接。当文件修改时服务器发送更新通知到浏览器。浏览器接收更新后仅替换变更的模块保持应用状态。Vite 如何处理 CSS、TypeScript 和静态资源CSS直接支持 import ‘./style.css’自动处理路径和 HMR无需额外配置内置 CSS Modules.module.css和 PostCSS 支持无需额外插件less/scss预处理器只需要安装对应的预处理器npm install -D less/sasstailwind css需要通过插件配置TypeScript无需 Babel直接通过 esbuild 编译仅类型检查不转换语法。需配合 tsconfig.json但运行时依赖浏览器原生支持。静态资源自动处理资源导入如 import img from ‘./logo.png’小于 4KB 的资源会内联为 Base64可通过 build.assetsInlineLimit 配置svg需要特殊配置简单场景图片 / 原始代码无需配置直接引入组件场景Vue 用 ?componentReact 需配置 svgr多图标场景推荐使用 vite-plugin-svg-icons 实现雪碧图优化性能和使用体验Vite Pluginvitejs/plugin-reactReact 支持vitejs/plugin-vueVue 支持vite-plugin-htmlhtml优化vite-plugin-image-optimizer图片压缩vite-plugin-compression资源压缩vite-plugin-remove-console移除生产环境的consolerollup-plugin-visualizer可视化分析打包结果unplugin-auto-import自动导入常用APIVite打包构建流程一、准备阶段【解析配置文件】解析配置文件读取项目根目录的 vite.config.js配置合并默认配置与用户配置确定最终的构建参数。【依赖预构建】可选【确定入口文件】二、核心构建阶段基于rollup【模块解析与依赖图构建】从入口文件开始递归解析所有导入的模块调用配置的插件处理非 JS 模块【代码转化与优化】【资源处理】三、输出阶段生成打包产物、生成 manifest 文件可选webpack和vite区别1构建原理webpack基于打包bundle将所有模块递归解析构建依赖图最终打包成一个或多个 bundle 文件vite基于原生ESM开发环境按需编译模块生产环境使用rollup打包预构建优化依赖预构建 代码分割2开发体验差异webpack启动时需要递归解析所有依赖构建完整的依赖图并打包大型项目启动速度较慢。热更新时需重新构建受影响的模块及其依赖然后将更新的代码块发送到浏览器大型项目热更新可能有明显延迟vite启动时仅需启动开发服务器无需打包依赖预构建将 CommonJS 转 ESM、合并第三方库后直接响应浏览器请求大型项目启动速度极快。热更新时仅需重新编译修改的模块利用 ESM 特性直接替换模块无需处理无关依赖更新速度与项目规模无关几乎瞬间完成。3生产环境构建webpack自带打包能力优化需手动配置或依赖插件实现优化如 splitChunks 分割代码需通过 babel-loader、postcss 等处理兼容性vite生产环境使用 Rollup 打包自动代码分割、预构建依赖、CSS 提取、Tree-shaking 等内置 vitejs/plugin-legacy 处理旧浏览器兼容自动生成 polyfill。4配置复杂度webpack配置灵活但复杂需要手动配置入口、出口、loader处理不同文件类型、plugin扩展功能等vite零配置开箱即用默认支持 JSX、TypeScript、CSS、静态资源等无需手动配置 loader。复杂需求通过简洁的配置文件实现。三、babelbabel介绍及配置用法解析其他pnpm、npm、yarnnpm生态最成熟锁文件package-lock.jsonyarn在大型项目中表现稳定支持离线模式锁文件yarn.locknpm和yarn都采用扁平化依赖树hoisting会将依赖提升到顶层 node_modules可能导致版本冲突。pnpm使用软连接的方式管理依赖以及缓存机制原生支持workspace磁盘占用少中心化思想解决依赖复用问题锁文件pnpm-lock.yaml。适合追求性能和磁盘空间优化、需要严格依赖隔离的项目或使用 monorepo 架构的项目性能对比安装速度pnpm yarn npm通常情况下。pnpm 因共享依赖存储重复安装时无需重新下载yarn 早期通过并行安装提升速度npm 后期版本也在性能上有较大优化。磁盘空间pnpm 最优因为依赖被全局存储并复用npm 和 yarn 可能在不同项目中重复存储相同依赖。monorepo、multirepomonorepo 单一代码仓库适用于关联紧密的多包 / 项目如组件库 应用、同一产品线的多个模块。包管理工具lerna、yarn workspace、pnpm workspace、nx、turbomultirepo 多代码仓库适用于独立项目 / 业务线、权限隔离严格使用包版本管理工具相比不使用有什么优点1依赖管理自动处理内部包关联 vs 手动维护不使用lerna多个包package之间若存在依赖关系如 packageA 依赖 packageB需要手动通过 npm link 或相对路径关联且每次 packageB 更新后需手动重新安装或更新 packageA 的依赖容易出现版本不一致或关联失效的问题。使用Lerna通过 lerna bootstrap 命令自动处理内部包依赖将相互依赖的包通过软链接symlink关联类似全局 npm link 但局限于当前仓库确保本地开发时依赖始终指向最新代码支持 --hoist 选项将多个包共用的外部依赖提升到根目录的 node_modules避免重复安装减少磁盘占用。2版本控制统一版本或独立版本管理 vs 手动维护版本号不使用lerna每个包的 package.json 版本号需手动修改使用Lerna通过 lerna version 命令自动检测变更基于 Git 提交记录批量更新受影响的包版本号并生成标准化的 Git Tag如 packageA1.2.0减少手动操作。3发布流程自动化发布 vs 手动打包上传不使用lerna发布时需手动对每个变更的包执行 npm publish且需确保发布顺序依赖项先于被依赖项发布否则可能出现依赖版本不存在的错误。使用Lernalerna publish 命令自动完成发布流程避免手动发布的顺序错误。结合 conventional-changelog 可自动生成符合规范的 CHANGELOG.md清晰记录每个包的版本变更内容4工作流效率批量执行命令 vs 逐个操作不使用lerna若需要在所有包中执行相同命令如 npm test、npm run build需手动切换到每个包目录执行或编写复杂的 shell 脚本遍历目录效率低下且不易维护。使用Lerna通过 lerna run 命令 批量执行脚本如 lerna run test 对所有包运行测试支持通过 --scope 过滤指定包如 lerna run build --scope packageA 仅构建 packageA。lerna有什么缺点1性能问题依赖安装和命令执行效率较低lerna bootstrap 速度慢依赖提升hoist的局限性批量命令执行效率低2版本管理和发布的灵活性不足版本策略较僵化发布流程耦合 npm3与现代工具链的兼容性问题对 pnpm/yarn 新特性支持滞后缺乏对模块联邦Module Federation等现代方案的支持4配置和学习成本较高微前端、MF模块联邦微前端钩子是协调主应用与子应用生命周期、实现通信和状态管理的核心机制。子应用生命周期钩子1bootstrap初始化触发时机子应用首次加载时执行仅执行一次。作用完成子应用的初始化工作如配置全局变量、初始化路由、创建 Vue/React 实例等不涉及 DOM 渲染。2mount挂载触发时机子应用被插入主应用 DOM 时执行每次显示子应用都会触发。作用渲染子应用到指定容器绑定事件监听启动应用逻辑。3unmount卸载触发时机子应用从主应用 DOM 中移除时执行每次隐藏子应用都会触发。作用清理子应用的副作用如卸载 DOM、移除事件监听、取消定时器等避免内存泄漏。4update更新触发时机主应用向子应用传递的参数发生变化时触发部分框架支持。作用根据新参数更新子应用状态如刷新数据、重新渲染部分内容。主应用钩子1beforeLoad加载前触发时机子应用资源加载前执行。作用校验子应用权限、预加载依赖、修改子应用配置如动态修改入口 URL。2beforeMount挂载前触发时机子应用 bootstrap 后、mount 前执行。作用准备子应用挂载环境如设置全局样式、初始化通信通道。3afterMount挂载后触发时机子应用 mount 执行完成后触发。作用记录子应用挂载状态、执行全局回调如上报加载成功事件。4beforeUnmount卸载前触发时机子应用 unmount 前执行。作用保存子应用状态、清理主应用中与该子应用相关的临时数据。5afterUnmount卸载后触发时机子应用 unmount 执行完成后触发。作用确认子应用已完全卸载、回收资源如清除缓存。通信相关钩子onGlobalStateChange全局状态变化监听setGlobalState修改全局状态路由相关钩子onAppRouteChange子应用路由变化beforeAppActivate子应用激活前​
返回列表