前端打包工具之Webpack5

2023-12-13 16:54:35

什么是打包工具

将浏览器无法运行的高级语法代码(如:es6、scss)编译成浏览器能识别的JS、CSS等语法。

打包工具的作用

  • 语法编辑
  • 压缩代码
  • 兼容性处理
  • 代码性能优化

常见的打包工具

  • Vite
  • Webpack
  • parcel

一、Webpack

1、什么是webpack

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。它会以一个或多个文件作为打包的入口,将整个项目所有文件编译组合成一个或者多个文件(bundles)输出。

webpack 开箱即用,可以无需使用任何配置文件。然而,webpack 会假定项目的入口起点为 src/index.js,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化

2、webpack本身的功能是有限的

webpack本身的功能是有限的

  • 开发模式: 仅能编译JS中的ES Model语法
  • 生产模式:编译JS中的ES Model语法,还能压缩JS代码

即是:webpack本身只能处理JS编译,其他编译和处理需要进一步的配置

3、webpack基本使用:基于本身功能只解析JS资源

  • 初始化package.json
$ npm init -y
  • 下载依赖 cli
$ npm i webpack webpack-cli -D

webpack从4.0版本开始,在安装时,就需要安装webpack和webpack-cli这两个东西。(1、webpack是打包代码时的核心内容;2、webpack-cli是一个用来在命令行中运行webpack的工具)

xxx-cli的主要功能就是:处理命令行参数(如果你想使用 npx 来运行 webpack,请确保你已经安装了 webpack-cli);npm run build -> 执行node_modules/webpack文件 -> 执行webpack-cli文件 -> 执行cli.run() -> 处理命令行参数。

  • 执行webpack打包
// 开发环境
$ npx webpack ./app.js --mode=development
// 生产环境
$ npx webpack ./app.js --mode=production

npx webpack: 运行本地安装的webpack包(npx: 用于快速执行本地或远程的npm包)
./app.js: 打包入口文件
–mode=XXX:打包模式

以上步骤默认打包的是JS代码,CSS等其他代码的处理,需要另行配置

4、webpack配置

npx webpack init: 根据项目需求快速生产webpack配置

4.1、entry(入口):指示webpack从哪个文件开始打包

默认src/index.js

4.2、output(出口):指示webpack打包完的文件输出到哪里、如何命名
4.3、loader加载器):webpack本身只能处理js、json资源,其他资源需要借助loader

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

loader 的两个属性:

  • test 属性,识别出哪些文件会被转换。
  • use属性,定义出在进行转换时,应该使用哪个 loader。
const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    // 嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用) raw-loader 转换一下
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};
4.4、plugins (插件): 扩展webpack功能

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
4.5、mode (模式) :开发、生产模式
4.6、browser compatibility (浏览器兼容性)

Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import() 和 require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill

4.7、environment (环境): Webpack 5 运行于 Node.js v10.13.0+ 的版本

5、webpack提升打包构建速度

5.1、热更新:只针对开发模式
// 生产模式下不需要devServer
devServer: {
   host: '0.0.0.0',
   port: 3000,
   compress: true,
   // 默认为true: 提升开发模式的打包构建速度
   // 默认只支持css热更新  js的需要加载相应loader
   hot: true, // 开启HRM 热更新
},
5.2、oneOf配置项
// 加载器: 帮助webpack识别  本身不能识别的资源
module: {
    // loader的配置
    rules: {
        // 避免找到了loader 还会继续遍历后面的loader
        oneOf: [
            // 处理css资源
            {},
            // 处理图片资源
            {},
            // 处理其他资源
            {},
            // 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
            {
                test: /\.js$/,
                // 排除哪些不需要处理的文件
                exclude: /(node_modules|bower_components)/,
                // 只加载一个loader 可以直接使用loader替换use
                loader: 'babel-loader',
                // 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
                // options: {
                //     presets: ['@babel/preset-env']
                // }
            }
        ]
    }
},
5.3、exclude配置项或者include配置项

exclude、include不能同时使用

{
    test: /\.js$/,
    // 排除哪些不需要处理的文件
    exclude: /(node_modules|bower_components)/,
    // include: path.resolve(__dirname, "../src"),
    // 只加载一个loader 可以直接使用loader替换use
    loader: 'babel-loader',
    // 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
    // options: {
    //     presets: ['@babel/preset-env']
    // }
}
5.4、ESLint、babel添加缓存
  • babel添加缓存
// 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
{
    test: /\.js$/,
    // 排除哪些不需要处理的文件
    exclude: /(node_modules|bower_components)/,
    // 只加载一个loader 可以直接使用loader替换use
    loader: 'babel-loader',
    options: {
        // 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
        // presets: ['@babel/preset-env']
        cacheDirectory: true, // 开启babel缓存
        cacheCompression: false // 关闭缓存文件压缩
    }
}
  • ESLint 添加缓存
// 插件
plugins: [
    // ESLint检验
    new ESLintPlugin({
        // eslint 只检测src下面的文件
        context: path.resolve(__dirname, '../src'),
        exclude: 'node_modules', // 默认值: node_modules
        cache: true, // 开启缓存
        cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslintcache")
    }),
],
5.5、启动多线程打包
  • 获取cpu核数
const os = require('os')
// 获取cpu核数
const threads = os.cpus().length;
  • babel操作开启多线程
# 安装thread-loader
$ npm install --save-dev thread-loader
// 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
{
    test: /\.js$/,
    // 排除哪些不需要处理的文件
    exclude: /(node_modules|bower_components)/,
    use: [
        {
            loader: "thread-loader",
            options: {
                workers: threads,  // babel: 进程数量
            }
        },
        {
            loader: 'babel-loader',
            options: {
                // 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
                // presets: ['@babel/preset-env']
                cacheDirectory: true, // 开启babel缓存
                cacheCompression: false // 关闭缓存文件压缩
            }
        }
    ]
}
  • ESLint开启多进程
 plugins: [
   // ESLint检验
    new ESLintPlugin({
        // eslint 只检测src下面的文件
        context: path.resolve(__dirname, '../src'),
        exclude: 'node_modules', // 默认值: node_modules
        cache: true, // 开启缓存
        cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslintcache"),
        threads  // 开启多进程
    }),
],
  • 压缩js开启多进程
const TerserWebpackPlugin = require('terser-webpack-plugin')
optimization: {
  // 压缩的操作
  minimizer: [
      // 压缩js
      new TerserWebpackPlugin({
          // 开启多进程
          parallel: threads,
      }),
  ],
},
5.6、减少代码体积

Tree Shaking: 移除JS中没有使用上的代码。 生产环境下已经默认开启了

5.7、减少文件中的babel辅助代码引入
{
 test: /\.js$/,
 // 排除哪些不需要处理的文件
 exclude: /(node_modules|bower_components)/,
 use: [
     {
         loader: "thread-loader",
         options: {
             workers: threads,  // babel: 进程数量
         }
     },
     {
         loader: 'babel-loader',
         options: {
             // 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
             // presets: ['@babel/preset-env']
             cacheDirectory: true, // 开启babel缓存
             cacheCompression: false, // 关闭缓存文件压缩
             plugins: ["@babel/plugin-transform-runtime"] // 减少代码体积:避免不必要的babel的辅助代码注入
         }
     }
 ]
}
5.8、压缩静态资源图片

图片压缩

6、webpack配置代码

const path = require('path')
const os = require('os')
const ESLintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
const nodeExternals = require('webpack-node-externals')
const TerserWebpackPlugin = require('terser-webpack-plugin')
// 获取cpu核数
const threads = os.cpus().length;
// 生产模式下:进行项目优化: 优化打包速度; 优化运行性能


module.exports = {
    target: 'node',   // ignore built-in modules like path, fs, etc.
    externals: [nodeExternals()], // 排除Node.js核心模块
    // 项目打包入口: 相对路径
    entry: './src/app.js',
    // 模式
    mode: "production",
    // 包含行和列的映射  打包速度慢
    // 开发模式:  'cheap-module-source-map'
    devtool: 'source-map', // 将编译后的代码映射回原始源代码
    // 打包出口: 绝对路径
    output: {
        // __dirname: node变量: 当前文件的文件夹目录
        path: path.resolve(__dirname, '../dist'),  // 打包后所有文件的输出路径
        filename: 'static/js/[name].js',   // 入口文件打包后的输出路径
        // 给打包输出的其他文件命名 比如import动态导入的文件
        // 结合webpack 魔法命名 import(/* webpackChunkName: "app" */ "./src/app.js").then(()=>{})
        chunkFilename: 'static/js/[name].chunk.js',
        // 图片、字体等通过type: asset 处理资源命名方式  替换掉generator
        assetModuleFilename: 'static/media/[hash:10][ext][query]',
        clean: true, //  自动清空path目录内容,再进行打包
        publicPath: '/assets/',
    },
    // 加载器: 帮助webpack识别  本身不能识别的资源
    module: {
        // loader的配置
        rules: {
            // 避免找到了loader 还会继续遍历后面的loader
            oneOf: [
                // 处理css资源
                {
                    test: /\.s[ac]ss$/,
                    // use的加载顺序: 从右向左
                    use: [
                        // "style-loader",  // 将js中的css通过创建style标签添加到html文件中
                        MiniCssExtractPlugin.loader, // 将css资源抽离成css文件, 通过link引入(避免了加载js的阻塞)
                        "css-loader",   // 将css资源编译成commonjs模块到js中
                        // sass兼容处理
                        // 需要在package 添加需要兼容到哪些浏览器
                        // "browserslist": [
                        //     "last 2 version",  // 所有的浏览器取最新的两个版本
                        //     "> 1%",     // 覆盖99% 的浏览器
                        //     "not dead"
                        //   ]
                        {
                            loader: 'postcss-loader',
                            options: {
                              postcssOptions: {
                                plugins: [
                                  [
                                    'postcss-preset-env',
                                    {
                                      // 其他选项
                                    },
                                  ],
                                ],
                              },
                            },
                        },
                        "sass-loader"   // 将sass编译成css
                    ],
                },
                // 处理图片资源
                {
                    // webpack5内置了处理图片的loader
                    test: /\.(png|jpe?g|gif|webp|svg)$/,
                    type: 'asset',
                    parser: {
                        dataUrlCondition: {
                            // 将小于10kb的图片打包成base64
                            // 优点: 较少请求数量
                            // 缺点: 图片体积变大(所以一般都转的小体积的图片)
                            maxSize: 10 * 1024 // 10kb
                        }
                    },
    
                    // 图片打包后的目录
                    // generator: {
                    //     // hash:10   取hash前10个字符
                    //     filename: 'static/images/[hash:10][ext][query]'
                    // }
                },
                // 处理其他资源
                {
                    test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
                    type: 'asset/resource',  // 原样打包到dist中
                    // generator: {
                    //     // hash:10   取hash前10个字符
                    //     filename: 'static/media/[hash:10][ext][query]'
                    // }
                },
                // 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
                // babel-loader: 不能处理async、promise等ES+的语法   需要借助core-js
                // core-js 是专门做ES6以上API的polyfill(补丁)
                // 在babel.config.js
                // module.exports = {
                //     // 智能预设: 能够编译ES6语法
                //     presets: [
                //         '@babel/preset-env',
                //         {
                //             useBuiltIns: 'usage',  // 按需自动加载core-js对ES6+的兼容性处理
                //             corejs: 3  // core-js的版本号
                //         }
                //     ]
                // }
                {
                    test: /\.js$/,
                    // 排除哪些不需要处理的文件
                    exclude: /(node_modules|bower_components)/,
                    // include: path.resolve(__dirname, "../src"),
                    // 只加载一个loader 可以直接使用loader替换use
                    use: [
                        {
                            loader: "thread-loader",
                            options: {
                                workers: threads,  // babel: 进程数量
                            }
                        },
                        {
                            loader: 'babel-loader',
                            options: {
                                // 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
                                // presets: ['@babel/preset-env']
                                cacheDirectory: true, // 开启babel缓存
                                cacheCompression: false, // 关闭缓存文件压缩
                                plugins: ["@babel/plugin-transform-runtime"]   // 减少代码体积:避免不必要的babel的辅助代码注入
                            }
                        }
                    ]
                }
            ]
        }
    },
    // 插件
    plugins: [
        // ESLint检验
        new ESLintPlugin({
            // eslint 只检测src下面的文件
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            threads
        }),
        // html文件自动引入依赖资源
        new HtmlWebpackPlugin({
            // 模板: 以public/index.html 文件作为模板,创建打包后新的html文件
            // 新的html文件特点:1、结构和打包之前的一致; 2、自动引入打包输出的资源
            template: path.resolve(__dirname, '../public/index.html')
        }),
        // 单独打包出css文件
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].[contenthash:10].css',
            chunkFilename: 'static/css/[name].chunk.[contenthash:10].css'
        }),
    ],
    optimization: {
        // 压缩的操作
        minimizer: [
            // css压缩
            // 开启生产模式 html、js会自动压缩的
            new CssMinimizerPlugin(),
            // 压缩js
            new TerserWebpackPlugin({
                // 开启多进程
                parallel: threads,
            }),
        ],
        // 代码分割配置
        splitChunks: {
            chunks: 'all'
        },
        // 保证引用文件不变
        // 只变runtime和map里面的文件
        runtimeChunk: {
            name: (entrypoint) => `runtime-${entrypoint.name}.js`
        }
    },
    // 开发服务器: 不会输出资源到dist中的, 是在内存中进行编译打包的
    // src下的代码更改后,自动打包/自动编译代码
    // 生产模式下不需要devServer
    devServer: {
        host: '0.0.0.0',
        port: 3000,
        compress: true,
        // 默认为true: 提升开发模式的打包构建速度

        // 默认只支持css热更新  js的需要加载相应loader
        hot: true, // 开启HRM 热更新
    },
}

文章来源:https://blog.csdn.net/weixin_44224921/article/details/134941395
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。