Skip to content

webpack高级使用

仲灏2022-06-10约 1 分钟

<div style="display: none;" hidden="true" aria-hidden="true" data-nosnippet>Are you an LLM? You can read better optimized documentation at /pages/820652.md for this page in Markdown format</div>

抽离公共代码和第三方代码

js
    optimization: {
      	...
        // 分割代码块
        splitChunks: {
            chunks: 'all',
            /**
             * initial 入口chunk,对于异步导入的文件不处理
                async 异步chunk,只对异步导入的文件处理
                all 全部chunk
             */

            // 缓存分组
            cacheGroups: {
                // 第三方模块
                vendor: {
                    name: 'vendor', // chunk 名称
                    priority: 1, // 权限更高,优先抽离,重要!!!
                    test: /node_modules/,
                    minSize: 0,  // 大小限制
                    minChunks: 1  // 最少复用过几次
                },

                // 公共的模块
                common: {
                    name: 'common', // chunk 名称
                    priority: 0, // 优先级
                    minSize: 0,  // 公共模块的大小限制
                    minChunks: 2  // 公共模块最少复用过几次
                }
            }
        }
      ...
    }

动态数据-懒加载

js
import('**').then(res => {
	res.default.** // 注意default
})

module chunk bundle 的区别

image-20220610165948317

  • module- 各个源码文件,webpack 中一切皆模块
  • chunk - 多模块合并成的,如 entry import splitChunk
  • bundle- 最终的输出文件

webpack 性能优化 - 构建速度

优化 babel-loader

js
{
    test:八.js$/
    use: ["babel-loader?cacheDirectory'],//开启缓
    include: path. reso lve(dirname,'src"),//明确范
    //// 排除范围,include 和 exclude 两者选一个即可
    // exclude: path. resolve(_dirname,'node_ modules")
}

IgnorePlugin 避免引入无用模块

  • import moment from 'moment'
  • 默认会引入所有语言 JS代码,代码过大
  • 如何只引入中文?
js
// webpack.prod.js

//忽略 moment 下的 / Locale 目录
new webpack.Ignoreplugin(/\.\/locale/, /moment/

image-20220610171412874

noParse 避免重复打包

image-20220610171542388

happyPack 多进程打包

  • JS单线程,开启多进程打包
  • 提高构建速度(特别是多核 CPU)
js
// module.rules
  // js
  {
      test: /\.js$/,
      // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
      use: ['happypack/loader?id=babel'],
      include: srcPath,
      // exclude: /node_modules/
  },
js
// plugins
// happyPack 开启多进程打包
new HappyPack({
    // 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
    id: 'babel',
    // 如何处理 .js 文件,用法和 Loader 配置中一样
    loaders: ['babel-loader?cacheDirectory']
}),

ParallelUglifyPlugin 多进程压缩JS

  • webpack 内置 Uglify 工具压缩 JS
  • JS单线程,开启多进程压缩更快
  • 和 happyPack 同理
js
// plugins

// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
new ParallelUglifyPlugin({
    // 传递给 UglifyJS 的参数
    // (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
    uglifyJS: {
        output: {
            beautify: false, // 最紧凑的输出
            comments: false, // 删除所有的注释
        },
        compress: {
            // 删除所有的 `console` 语句,可以兼容ie浏览器
            drop_console: true,
            // 内嵌定义了但是只用到一次的变量
            collapse_vars: true,
            // 提取出出现多次但是没有定义成变量去引用的静态值
            reduce_vars: true,
        }
    }
})

开发环境下

自动刷新 热更新

热更新需要开启监听范围

js
// 增加,开启热更新之后的代码逻辑
if (module.hot) {
    module.hot.accept(['./math'], () => {
        const sumRes = sum(10, 30)
        console.log('sumRes in hot', sumRes)
    })
}

DllPlugin 动态链接库插件

  • 前端框架如 vue React,体积大,构建慢

  • 较稳定,不常升级版本

  • 同一个版本只构建一次即可,不用每次都重新构建

  • webpack 已内置 DllPlugin 支持

  • DlPlugin -打包出dll文件

    react.dll.js

    manifest.json // 索引文件

  • DlIlReferencePlugin - 使用 dlll文件

​ index.html 中引入react.dll.js文件

​ 然后配置索引地址

js
plugins

new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      // 例如 react.manifest.json 中就有 "name": "_dll_react"
      name: '_dll_[name]',
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(distPath, '[name].manifest.json'),
    }),

使用: 创建webpack.dll.js

js
const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const { srcPath, distPath } = require('./paths')

module.exports = {
  mode: 'development',
  // JS 执行入口文件
  entry: {
    // 把 React 相关模块的放到一个单独的动态链接库
    react: ['react', 'react-dom']
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    // 也就是 entry 中配置的 react 和 polyfill
    filename: '[name].dll.js',
    // 输出的文件都放到 dist 目录下
    path: distPath,
    // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
    // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
    library: '_dll_[name]',
  },
  plugins: [
    // 接入 DllPlugin
    new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      // 例如 react.manifest.json 中就有 "name": "_dll_react"
      name: '_dll_[name]',
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(distPath, '[name].manifest.json'),
    }),
  ],
}

webpack 性能优化 - 产出代码

  • 体积更小

  • 合理分包,不重复加载

  • 速度更快、内存使用更少

  • 小图片 base64 编码

  • bundle 加 hash

  • 懒加载

  • 提取公共代码

  • IngorePlugin

  • 使用cdn 加速

    • publicPath
  • 使用 production

    • 更改mode配置 , production会自动压缩代码
    • Vue React 等会自动删掉调试代码(如开发环境的 warning)
    • 启动 Tree-Shaking
  • Scope Hosting

    • 代码体积更小
    • 创建函数作用域更少
    • 代码可读性更好
    • image-20220610193408856

ES6 Module 和 Commonjs 区别

两个都是模块化解决方案

  • ES6 Module 静态引入,编译时引入
  • Commonjs 动态引入,执行时引入
  • 只有 ES6 Module 才能静态分析,实现 Tree-Shaking