chaojidan

导航

webpack的原理和优化

webpack是一个用于现代 JavaScript 应用程序的静态模块打包工具

webpack解决的问题:

  • 对模块化编程的支持
  • 通过loader可以使 webpack 支持多种语言和预处理器语法编写的模块,如Vue, React,等
  • webpack-dev-serve提供的本地服务和无需刷新整个页面即可应用更新的能力(HMR)
  • 开发完成后需要将代码进行压缩、合并以及其他相关的优化

loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader让 webpack 能够去处理其他类型的文件,并将它们转换为有效
模块,以供应用程序使用,以及被添加到依赖图中。

less-loader:webpack 将 Less 编译为 CSS 的 loader。
css-loader:css-loader会对@import和url()进行处理,就像 js 解析 import/require()一样。
style-loader:把 CSS 插入到 DOM 中。

plugin
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

Babel

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或
其他环境中。babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面,共分为两类

  • 语法插件:支持解析特定类型的语法,例如@babel/plugin-proposal-optional-chaining
  • 转换插件:通过对不支持的新语法进行转换以实现在低版本浏览器的兼容,例如@babel/plugin-transform-arrow-functions

presets:
一组插件的集合,因为常用,不必重复定义和安装,现有如下4种
@babel/preset-env
@babel/preset-flow
@babel/preset-react
@babel/preset-typescript
除了语法转换,@babel/preset-env另一个重要的功能是可以依赖core-js在全局和构造函数静态属性、实例属性上添加 api 的方式来解决 api 兼容性
问题,当设置了useBuiltIns选项(不为false)时,就会使用core-js来对api进行处理。

{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                '@babel/preset-env'
                {
                   useBuiltIns: 'usage',
                    corejs: 3
                }
            ]
        }
    }
}

资源模块
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
在 webpack 5 之前,通常使用:

  • [raw-loader]将文件导入为字符串
  • [url-loader]将文件作为 data URI 内联到 bundle 中
  • [file-loader]将文件发送到输出目录

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource发送一个单独的文件并导出 URL。之前通过使用file-loader实现。
  • asset/inline导出一个资源的 data URI。之前通过使用url-loader实现。
  • asset/source导出资源的源代码。之前通过使用raw-loader实现。
  • asset在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader,并且配置资源体积限制实现。

webpack-dev-server
webpack-dev-server为你提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能

HMR

Hot Module Replacement,简称HMR,无需完全刷新整个页面的同时,更新模块。HMR 的好处,节省宝贵的开发时间、提升开发体验。

模块热更新实现原理:

  • 使用webpack-dev-server(后面简称 WDS)托管静态资源,同时以 Runtime 方式注入 HMR 客户端代码
  • 浏览器加载页面后,与 WDS 建立 WebSocket 连接
  • Webpack 监听到文件变化后,增量构建发生变更的模块,并通过 WebSocket 发送hash事件
  • 浏览器接收到hash事件后,请求manifest资源文件,确认增量变更范围
  • 浏览器加载发生变更的增量模块
  • Webpack 运行时触发变更模块的module.hot.accept回调,执行代码变更逻辑

mode
提供mode配置选项,告知 webpack 使用相应模式的内置优化。

区分开发环境和生产环境配置:
development(开发环境)和production(生产环境)这两个环境下的构建目标存在着巨大差异。在开发环境中,我们需要:强大的 source map 和一个有着
live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 localhost server。而生产环境目标则转移至其他方面,关注点在于压
缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的
webpack 配置。

webpack.dev.js

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
    mode: 'development',
    devtool: 'inline-source-map',
    devServer: {
        static: './dist',
    },
});    

webpack.prod.js

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
    mode: 'production',
});

构建流程

  • 从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
  • 用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
  • 根据配置中的 entry 找出所有的入口文件
  • 从入口文件出发,调用所有配置的 Loader 对模块进行编译,再找出该模块被编译后的最终内容以及它们之间的依赖关系
  • 在经过上一步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
  • 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  • 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

优化:

  1. 默认devtools使用eval-cheap-module-sourcemap,可以用exclude掉node_modules
  2. 采用webpack5的文件系统缓存
  3. noParse指定某些第三方包不解析
  4. 单独配置splitchunk避免lodash重复引用
  5. 对体积比较大的第三方包,比如:echarts,做按需引入
  6. 使用contextreplacementplugin排除掉moment语言包的引入

posted on 2025-04-30 10:26  chaojidan  阅读(24)  评论(0)    收藏  举报