前端青训营-工具篇-Webpack与Vite | 豆包MarsCode AI 刷题

43 阅读16分钟

Webpack

概述

前端项目由什么构成?

前端项目是由各种资源构成的

img

img

这些资源可以依赖手工,但

  • 依赖手工,比如有50个JS文件...操作,过程繁琐
  • 当代码文件之间有依赖的时候,就得严格按依赖顺序书写
  • 开发与生产环境一致,难以接入TS或JS新特性
  • 比较难接入Less、Sass 等工具
  • JS、图片、CSS资源管理模型不一致

发展历程

这些都是旧时代非常突出的问题,对开发效率影响非常大,直到...

出现了很多工程化工具,某种程度上正是这些工具的出现,才有了”前端工程”这一概念

img

什么是Webpack

Webpack本质上是一种前端资源编译、打包工具

img

  • 多份资源文件打包成一个 Bundle
  • 支持Babel、Eslint、TS、CoffeScript、Less、 Sass
  • 支持模块化处理css、图片等资源文件支持HMR+开发服务器
  • 支持持续监听、持续构建支持代码分离
  • 支持Tree-shaking
  • 支持SourceMap

核心流程

示例

  1. 安装:npm i -D webpack webpack-cli

  2. 编辑配置文件

img

  1. 执行编译命令:npx webpack

会把项目里管理的JS文件打包成一个文件,并且处理好互相引入的过程。

核心流程

  1. 入口处理:从entry文件开始,启动编译流程
  2. 依赖解析:从entry文件开始,根据require or import等语句找到依赖资源。
  3. 资源解析:根据module配置,调用资源转移器,将png、css等非标准JS资源转义为JS内容。
  4. 合并打包:将转译后的资源内容合并打包为可直接在浏览器运行的JS文件

注:递归调用2、3,直到所有资源处理完毕

小结

Webpack本质上做的就是模块化+一致性的事情

  • 多个文件资源合并成一个,减少 http 请求数
  • 支持模块化开发
  • 支持高级JS特性
  • 支持 Typescript、CoffeeScript 方言
  • 统一图片、CSS、字体等其它资源的处理模型
  • Etc...

如何使用

配置项

img

关于Webpack的使用方法,基本都围绕“配置”展开,而这些配置大致可划分为两类:

  • 流程类:作用于流程中某个or若干个环节,直接影响打包效果的配置项
    1. 入口处理:entrycontext
    2. 依赖解析:resolveexternals
    3. 资源解析:module
    4. 合并打包:optimizationmodetarget
  • 工具类:主流程之外,提供更多工程化能力的配置项

img

最常用的一些配置项:

  • entry/output
  • module/plugins
  • mode
  • watch/devServer/devtool

使用流程-起步

img

  1. 声明入口entry
  2. 声明出口output
  3. 运行npx webpack

img

使用流程-处理CSS

img

img

  1. 安装Loader:npm add -D css-loader style-loader
  2. 添加module处理css文件

img

思考题:

  • Loader 有什么作用?为什么这里需要用到css-loader, style-loader

    用来在Webpack里将不同类型的文件(例如CSS、图片、字体等)转化为可以被浏览器理解的JavaScript模块。

    由于Webpack本身只理解JavaScript文件,loader的作用是将其他文件类型处理后嵌入到JavaScript中,以便Webpack可以管理和打包它们。

  • 与旧时代——在HTML文件中维护css相比,这种方式会有什么优劣处?

    • 优点:易于维护、性能优化、模块化、作用域控制(样式隔离)
    • 缺点:配置复杂、学习成本高
  • 有没有接触过Less、Sass、Stylus 这一类CSS 预编译框架?如何在 Webpack接入这些工具?

    • CSS预编译框架提供了变量、嵌套规则、混入、继承等功能,可以极大地提升CSS的组织性和功能性。

    • 使用方式(和处理CSS相似)

      1. 安装Loader:npm install sass sass-loader --save-dev
      2. 添加module处理sass文件等。。。

img

使用流程-接入Babel

babel是一种转义JS代码的工具,可以解决js代码对低版本兼容性的问题

img

img

  1. 安装依赖:npm i -D @babel/core @babel/preset-env babel-loader
  2. 声明产物出口output
  3. 执行npx webpack

img

思考题:

  • Babel具体有什么功能? - 语法转换、插件拓展、转义JSX、类型注释
  • Babel与Webpack分别解决了什么问题?为何两者能协作到一起了?
    • Babel:将现代 JavaScript 转换为向后兼容的版本,使代码可以在旧的环境中运行。
    • Webpack:将项目的多个模块和资源打包成少量的 bundle 文件,优化性能和加载速度。
    • 协作:通过 babel-loader 将 Babel 的编译能力集成到 Webpack 的打包流程中, 从而实现既兼容旧环境,又优化资源加载的构建方案。

参考资料:

Babel

Babel Loader

@babel/preset-env · Babel

@babel/preset-react · Babel

@babel/preset-typescript · Babel

使用流程-生成HTML

img

  1. 安装依赖:npm i -D html-webpack-plugin
  2. 声明产物出口output
  3. 执行npx webpack

img

思考题:

  • 相比于手工维护HTML内容,这种自动生成的方式有什么优缺点?
    • 优点:自动更新资源、避免重复维护的错误、统一管理模板和内容、可集成更多自动化流程
    • 缺点:学习配置成本高、生成页面的灵活性受限、调试和排错过程复杂、增加代码的构建时间

参考资料:

HtmlWebpackPlugin

使用工具-HMR

Hot Module Replacement - 模块热替换(页面实时刷新)

HMR 原理全解析

  1. 开启HMR

    {
        devServer: {
            hot: true
        }
    }
    
  2. 启动Webpack:npx webpack serve

使用工具-Tree-Shaking

Tree-Shaking-树摇,用于删除 Dead Code(没有用到的代码)

Dead Code:

  • 代码没有被用到,不可到达
  • 代码的执行结果不会被用到
  • 代码只读不写

开启tree-shaking:

{
    mode: "production"
    optimization: {
        usedExports: true
    }
}

PS:对工具类库如 Lodash 有奇效

其他工具

Webpack还有缓存、Sourcemap、性能监控、日志、代码压缩、分包等工具

Loader

引入

在默认情况下,Webpack只能处理js的资源。为了处理非标准JS资源,就设计出了资源翻译模块——Loader,用来将非JS资源翻译为标准JS(包括CSS也会被转换成JS)。

img

使用

  1. 安装依赖

  2. 配置module配置

    module: {
        rules: [
            {
                test: 正则匹配,如/\.less$/i,
                use: [loader列表]
            }
        ]
    }
    

链式调用

img

  • less-loader:实现 less => css 的转换
  • css-loader:将CSS 包装成类似module.exports = "${css}"的内容,包装后的内容符合 JavaScript 语法
  • style-loader:将 css 模块包进 require 语句,并在运行时调用 injectStyle 等 函数将内容注入到页面的 style 标签

其它特性

img

  • 链式执行
  • 支持异步执行
  • 分normal、pitch两种模式

如何编写

如何编写loader

img

常见Loader

站在使用角度,建议掌握这些常见Loader的功能、配置方法

img

思考题

  • Loader 输入是什么?要求的输出是什么?

    • 输入:Loader 的输入通常是一个模块的源代码,可能是文本、样式、图片或其他静态文件。
    • 输出:Loader 的输出是 JavaScript 模块代码,通常以字符串形式返回,并会被 Webpack 打包成最终输出的 JavaScript 文件。这可以包括经过编译、转换或其他形式处理后的代码。
  • Loader 的链式调用是什么意思?如何串联多个 Loader?

    • 链式调用的概念:Webpack 中允许多个 Loader 串联工作,称为“链式调用”。每个 Loader 会依次处理输入文件的数据,并将处理结果传递给下一个 Loader
    • 串联方式:Webpack 会从==最后一个== Loader 开始调用链式中的各个 Loader。比如 use: ['style-loader', 'css-loader', 'sass-loader'] 的配置中,sass-loader 会首先将 Sass 文件转换为 CSS,然后 css-loader 处理 CSS 文件的模块化,最后 style-loader 将 CSS 注入到 JavaScript 中。
  • Loader 中如何处理异步场景?

    在异步场景中,可以使用 this.async() 方法来处理异步逻辑。 调用 this.async() 后,它会返回一个 callback 函数, Loader 可以在处理完异步操作后调用该 callback,并将处理结果作为参数传递。

Plugin

概念

很多知名工具,如:

  • VS Code、Web Storm、Chrome、Firefox
  • Babel、Webpack、Rollup、Eslint
  • Vue、Redux、Quill、Axios

等等,都设计了所谓“插件”架构,为什么? - 提升工具的拓展性

img

如图,是webpack的编译流程,这是一个特别复杂的过程,那么:

  • 新人需要了解整个流程细节,上手成本高
  • 功能迭代成本高,牵一发动全身
  • 功能僵化,作为开源项目而言缺乏成长性(更少的人会愿意参与)

也就是说 心智成本高 => 可维护性低 => 生命力弱

插件架构的精髓:对扩展开放,对修改封闭

甚至,Webpack 本身的很多功能也是基于插件实现的

使用插件

  1. 下载依赖(一般加-D)
  2. 导入插件并加入配置中

img

Dashboard可以帮我们美化编译结果

img

编写插件

插件围绕钩子展开

img

钩子的核心信息:

  1. 时机:编译过程的特定节点,Webpack会以钩子形式通知插件此刻正在发生什么事情;
  2. 上下文:通过tapable 提供的回调机制,以参数方式传递上下文信息;
  3. 交互:在上下文参数对象中附带了很多存在side effect 的交互接口,插件可以通过这些接口改变

img

如图,

  • 时机:compier.hooks.compilation
  • 参数:compilation
  • 交互:dependencyFactories.set

img

思考题

  • Loader 与插件有什么区同点?

    • Loader
      • 作用:专门用于转换某一类型的模块内容,例如将 TypeScript 文件编译成 JavaScript,将 Sass 编译为 CSS。
      • 工作方式:文件级别的转换,Loader 将文件内容作为输入,对其进行处理后返回 JavaScript 模块输出。
      • 用法:通常在 module.rules 中配置,用于特定文件类型的转换(如 JS、CSS 等)。
    • 插件(Plugin):
      • 作用:增强 Webpack 的构建能力,进行更广泛的构建过程控制,如打包优化、代码注入、文件生成等。
      • 工作方式:直接作用于 Webpack 的生命周期,不局限于单个文件,而是可以访问和改变整个构建流程。
      • 用法:在 plugins 数组中配置,通过 Webpack 提供的 API 对打包过程中的生命周期事件进行操作。
    • 相似点:
      • 两者都可以改变构建输出、提高项目构建的灵活性,且可以在一定程度上配合使用。
      • 都可自定义,提供灵活扩展方式,用户可以编写自定义的 LoaderPlugin 满足特定需求。
  • “钩子”有什么作用?如何监听钩子函数?

    • 作用:钩子(Hook)是 Webpack 生命周期中的事件。插件使用这些钩子在构建的不同阶段执行特定的代码,从而控制和自定义 Webpack 的行为。例如,钩子可以用于监听构建开始、完成、模块处理等不同阶段,适时执行自定义逻辑。

    • 如何监听钩子:插件可以通过 compiler.hooks.<hookName>.tap 方法监听钩子,并在钩子触发时执行函数。Webpack 提供了多个生命周期钩子,以下是示例:

      class MyPlugin {
        apply(compiler) {
          // 在编译开始时触发
          compiler.hooks.compile.tap('MyPlugin', (params) => {
            console.log('构建开始...');
          });
      
          // 在编译结束时触发
          compiler.hooks.done.tap('MyPlugin', (stats) => {
            console.log('构建完成');
          });
        }
      }
      
    • 常用的钩子类型:

      • 同步钩子(Sync Hook):按顺序执行所有注册的回调,构建流程会等待钩子执行完成。
      • 异步钩子(Async Hook):支持异步操作,比如 AsyncSeriesHook、AsyncParallelHook 等, 可以通过 tapAsync 或 tapPromise 进行异步回调。
    • 意义:钩子为插件提供了扩展能力,让开发者可以根据项目需求灵活地加入自定义逻辑,改变和控制构建过程。

参考资料

Webpack 插件架构深度讲解

一文吃透 Webpack 核心原理

Webpack5 知识体系

img

如何学习

  1. 入门应用
    • 理解打包流程
    • 熟练掌握常用配置项、Loader、插件的使用方法,能够灵活搭建集成Vue、React、Babel、Eslint、Less、 Sass、图片处理等工具的Webpack 环境
    • 掌握常见脚手架工具的用法,例如:Vue—cli、create—react-app. @angular/cli
  2. 进阶
    • 理解Loader、Plugin 机制,能够自行开发 Webpack 组件
    • 理解常见性能优化手段,并能用于解决实际问题理解前端工程化概念与生态现状
  3. 大师级
    • 阅读源码,理解Webpack 编译、打包原理,甚至能够参与共建

img

参考资料:

awesome-webpack-4plus

survivejs-webpack book

Vite

构建工具

前端工程的痛点

img

前端构建工具的意义

img

概要介绍

Vite 概览

img

业界案例

img

当下问题

img

两大行业趋势

  • 全球浏览器对原生ESM的普遍支持(目前占比92%以上)
  • 基于原生语言(Go、Rust)编写前端编译工具链,如 Go 语言编写的Esbuild、Rust 编写的 SWC

浏览器原生ESM支持

img

基于原生ESM的开发服务优势

  • 无需打包项目源代码
  • 天然的按需加载
  • 可以利用文件级的浏览器缓存

img

基于 Esbuild 的编译性能优化

img

Esbuild——基于 Golang 开发的前端工具,具备如下能力:

  • 打包器:Bundler(对标Webpack)
  • 编译器:Transformer(对标Babel)
  • 压缩器:Minifier(对标JS压缩工具)

其性能极高,在Vite中被深度使用

内置的 Web 构建能力

Vite 开箱即用的功能等价于

webpack、webpack-dev-server、css-loader、style-loader、less-loader、sass-loader、postcss-loader、file-loader

MiniCssExtractPlugin、HTMLWebpackPlugin

因为Vite对常见的Web开发的需求进行了封装,并且内置了一些默认的最佳实践。

举例:webpack.config.ts

img

等价于 vite.config.ts

img

上手实战

vite的特点:响应迅速、开箱即用

项目初始化

img

在创建时选择需要的初始化参数即可初始化成功,注意安装依赖和启动项目需要进入目录后操作。

这里如果初始化时选择了ts,我们会看到build命令是tsc && vite build,是因为vite底层使用的vite build没有使用ts的类型支持,所以需要借助ts官方工具进行ts的类型检查。

使用Sass/Scss & CSS Modules

img

运行pnpm i sass -D安装sass

在页面中导入sass文件,并在Header组件中应用这个样式。

其中.module是用上了css module,模块化的CSS代码更不容易出现类名的相互影响。

img

img

img

使用静态资源

这里以svg图片为例,页面上会渲染出src相对于项目根路径的一个url,vite作为dev server会把图片内容返回浏览器。

img

除了常见的图片格式,Vite也内置了对于JSON、Worker、WASM资源的加载支持。参考文档

使用HMR(默认开启)

HMR(Hot Module Replacement,热模块替换)是一种在开发时快速更新页面的技术,不需要刷新整个页面,只更新发生变化的模块。

值得一提的是,vite的HMR支持了对状态的保存,也就是说热模块替换后,组件里的值不会被重置。

生产环境 Tree Shaking(默认开启)

概念:Tree Shaking 是一种用于移除 JavaScript 项目中未使用代码的优化技术,主要用于打包过程中,通过检测代码的依赖关系,将没有用到的代码“摇掉”(删除),从而减小最终输出文件的大小,提升加载性能。

核心机制:Tree Shaking 的核心机制基于 ES6 模块系统的静态结构(import 和 export),它可以在打包时分析模块依赖关系,确定哪些代码是未引用的,从而安全地移除这些代码。

使用场景:主要适用于模块化的前端项目,尤其是那些使用第三方库的项目,可以自动去除没有用到的库函数或模块,减小打包后的体积。例如,如果从 lodash 中引入了一个 map 函数,Tree Shaking 会剔除 lodash 中其他未引用的函数。

优化原理:

  • 基于 ESM 的 import/export 语句依赖关系,与运行时状态无关
  • 在构建阶段将未使用到的代码进行删除

说明:因为默认也开启了代码压缩,如果要更直观的看到压缩效果,请配置build: {minify: false} 以关闭代码压缩。

注意:CommonJS格式由于require的部分可能依赖运行时计算的结果,所以不能做到Tree Shaking

整体架构

img

关键技术:依赖预打包

  • 为什么要进行预打包?
    • 避免 node_modules 过多的文件请求
    • 将CommonJS 格式转换为ESM格式
  • 实现原理:
    • 服务启动前扫描代码中用到的依赖
    • 用Esbuild对依赖代码进行预打包
    • 改写import语句,指定依赖为预构建产物路径

关键技术:单文件编译

  • 实现:用 Esbuild 编译 TS/JSX
  • 优势:编译速度提升10-100×
  • 局限性:
    • 不支持类型检查
    • 不支持语法降级到ES5(最低ES6)

关键技术:代码压缩

Esbuild 作为默认压缩工具,替换传统的Terser、Uglify.js 等压缩工具

img

关键技术:插件机制

  • 开发阶段—>模拟Rollup插件机制
  • 生产环境—>直接使用Rollup
  • 插件兼容性具体可查阅:Vite Rollup Plugins

进阶路线

深入双引擎

  • esbuild:官方文档
  • Roolup:官方文档
  • 学习顺序:
    • 先了解基本使用,动手尝试各项常用配置;
    • 然后学习其插件开发。

Vite 插件开发

为什么需要插件机制?

  • 抽离核心逻辑
  • 易于拓展

通过使用不同的Hook,我们可以在不同的构建阶段插入自定义的逻辑

img

开发Vite插件→配置文件引入

img

img

参考资料:

插件 API | Vite 官方中文文档

复杂度较低的插件:JSON插件

复杂度中等的插件:esbuild插件

复杂度较高的插件:react插件

学习方法:

  • 先看文档,过一遍插件钩子的功能
  • 然后多学习其它插件的实现,掌握套路

代码分割(拆包)

拆包前:打成一个产物文件,无法进行并发请求、缓存复用率低

img

拆包后:

img

参考资料:

构建选项 | Vite 官方中文文档

Configuration Options | Rollup

JS编译工具(Babel)

img

出现的原因:

  • JavaScript 语法标准繁,浏览器支持程度不一
  • 开发者需要用到高级语法

参考资料:

babel官方站点

babel插件手册

语法安全降级

问题:以Promise语法为例,IE11没有支持,要进行语法降级

img

参考文档:

@babel/preset-env文档

Vite官方降级插件文档

服务端渲染(SSR)

是一种常见的渲染模式,用于提升首屏性能和SEO优化。

构建阶段:

img

代码执行阶段:

img

参考文档:

Vite-SSR文档

Vite官方ssr-demo项目

使用 Vite 搭建 SSR 工程

深入了解底层标准

重点特性:CJS规范、ESM规范、HTTP2.0特性

img

参考资料:

ES modules: A cartoon deep-dive

Ship ESM & CJS in one Package

Pure ESM package

Vite 社区生态

Github 40k+ star(可参考 webpack 61.3 K, rollup 21.8 k),并且目前还在持续维护

官方提供插件:

  • @vitejs/plugin-vue,提供Vue 3支持
  • @vitejs/plugin-vue-jsx,提供 Vue 3 JSX 支持
  • @vitejs/plugin-react,提供 React 支持
  • @vitejs/plugin-legacy,提供低版本浏览器降级支持

海量社区插件:awesome-vite: ⚡️ A curated list of awesome things related to Vite.js

众多框架内置:Nuxt、SvelteKit、Astro、Vitepress