• 【Webpack5】Webpack学习笔记(一)


    Webpack

    前言

    Webpack是一款项目打包工具,将多个模块打包成一个模块,将ESM规范转换为旧的JS语法。

    1.Webpack使用

    • 使用步骤
      1. 初始化项目npm init -y
      2. 安装依赖webpackwebpack-cli
        npm i -D webpack webpack-cli
      3. 在项目中创建src目录,然后编写代码
      4. 执行npx webpack进行打包

    2.配置项

    2.1 entry(入口)

    也就是文件打包的起点。

    用法:entry: string | [string]

    webpack.config.js

    2.1.1 单入口写法
    module.exports = {
       entry: {
       	app: '.src/app.js',
      },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是单入口写法,可扩展性不大。使用对象语法虽然繁琐一些,但是可扩展性强。

    2.1.2 对象语法

    用法:entry: { string | [string] } | {}

    module.exports = {
      entry: {
        app: './src/app.js',
        adminApp: './src/adminApp.js',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.1.2.1 描述入口的对象
    属性名描述
    dependOn当前入口所依赖的入口。它们必须在该入口被加载前被加载。
    filename指定要输出的文件名称。
    import启动时需加载的模块。
    library指定 library 选项,为当前 entry 构建一个 library。
    runtime运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
    publicPath当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。
    1. runtimedependOn 不应在同一个入口上同时使用。

      module.exports = {
        entry: {
          a2: './a',
          b2: {
            runtime: 'x2',
            dependOn: 'a2',
            import: './b',
          },
        },
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    2. 确保 runtime 不能指向已存在的入口名称。

      module.exports = {
        entry: {
          a1: './a',
          b1: {
            runtime: 'a1',
            import: './b',
          },
        },
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    3. 另外 dependOn 不能是循环引用的。

      module.exports = {
        entry: {
          a3: {
            import: './a',
            dependOn: 'b3',
          },
          b3: {
            import: './b',
            dependOn: 'a3',
          },
        },
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2.1.3 常见场景配置
    2.1.3.1 分离 app(应用程序) 和 vendor(第三方库) 入口

    webpack.config.js

    配置 2 个单独的入口点,一个是主应用程序,一个是第三方库。这样就可以在 vendor.js 中存入未做修改的必要 library 或文件(例如 Bootstrap, jQuery, 图片等),然后将它们打包在一起成为单独的 chunk。内容哈希保持不变,这使浏览器可以独立地缓存它们,从而减少了加载时间。

    module.exports = {
      entry: {
        main: './src/app.js',
        vendor: './src/vendor.js',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    webpack.prod.js

    配置生产环境下的打包文件名

    module.exports = {
      output: {
        filename: '[name].[contenthash].bundle.js',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    webpack.dev.js

    配置开发环境下的打包文件名

    module.exports = {
      output: {
        filename: '[name].bundle.js',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2.1.3.2 多页面应用程序

    webpack.config.js

    module.exports = {
      entry: {
        pageOne: './src/pageOne/index.js',
        pageTwo: './src/pageTwo/index.js',
        pageThree: './src/pageThree/index.js',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.3 output(输出)

    通过配置output选项,告知webpack如何向硬盘写入编译文件。注意,即使可以存在多个entry起点,也只能配置一项output.

    用法:

    webpack.config.js

    module.exports = {
      output: {
        filename: 'bundle.js',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此配置将一个单独的 bundle.js 文件输出到 dist 目录中。

    2.3.1 有多个入口

    使用占位符来确保每个文件具有唯一的名称。

    module.exports = {
      entry: {
        app: './src/app.js',
        search: './src/search.js',
      },
      output: {
        filename: '[name].js',
        path: __dirname + '/dist',
      },
    };
    
    // 写入文件到硬盘:./dist/app.js, ./dist/search.js
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.3.2 高级进阶

    如果资源使用CDN和hash

    module.exports = {
      //...
      output: {
        path: '/home/proj/cdn/assets/[fullhash]',
        publicPath: 'https://cdn.example.com/assets/[fullhash]/',
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果在编译时,不知道最终输出文件的 publicPath 是什么地址,则可以将其留空,并且在运行时通过入口起点文件中的 __webpack_public_path__ 动态设置。

    __webpack_public_path__ = myRuntimePublicPath;
    
    // 应用程序入口的其余部分
    
    • 1
    • 2
    • 3

    2.4 loader(加载器)

    用于对模块的源代码进行转换, loader 可以使你在 import 或 “load(加载)” 模块时预处理文件。比如可以将TypeScript转换为JavaScript,将内联图像转换为data URL

    2.4.1 使用loader
    • 配置方式(推荐):在 webpack.config.js 文件中指定 loader。
    • 内联方式:在每个 import 语句中显式指定 loader。
    2.4.2 配置方式

    module.rules允许在webpack中配置多个loader。loder从右到左或者从下到上进行取值和执行。如下示例,从sass-loader开始执行,然后继续执行css-loader,最后执行style-loader

    module.exports = {
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [
                        { loader: 'style-loader' },
                        { 
                            loader: 'css-loader',
                            options: {
                                modules: true,
                            },
                        },
                        { loader: 'sass-loader' },
                    ],
                },
            ],
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.4.3 内联方式

    可以在 import 语句或任何 与 “import” 方法同等的引用方式 中指定 loader。使用 ! 将资源中的 loader 分开。每个部分都会相对于当前目录解析。

    import Styles from 'style-loader!css-loader?modules!./styles.css';
    
    • 1

    通过为内联 import 语句添加前缀,可以覆盖配置中的所有 loader, preLoaderpostLoader

    • 使用 ! 前缀,将禁用所有已配置的 normal loader(普通 loader)

      import Styles from '!style-loader!css-loader?modules!./styles.css';
      
      • 1
    • 使用 !! 前缀,将禁用所有已配置的 loader(preLoader, loader, postLoader

      import Styles from '!!style-loader!css-loader?modules!./styles.css';
      
      • 1
    • 使用 -! 前缀,将禁用所有已配置的 preLoaderloader,但是不禁用 postLoaders

      import Styles from '-!style-loader!css-loader?modules!./styles.css';
      
      • 1
    2.4.4 loader特性
    1. loader 支持链式调用。
    2. loader 可以是同步的,也可以是异步的。
    3. loader 运行在 Node.js 中,并且能够执行任何操作。
    4. loader 可以通过 options 对象配置。
    5. 除了常见的通过 package.jsonmain 来将一个 npm 模块导出为 loader,还可以在 module.rules 中使用 loader 字段直接引用一个模块。
    6. 插件(plugin)可以为 loader 带来更多特性。
    7. loader 能够产生额外的任意文件。
    2.4.5 解析loader

    loader 遵循标准模块解析规则。多数情况下,loader 将从模块路径加载(通常是从 npm install, node_modules 进行加载)。

    2.5 plugin(插件)

    插件用于解决loader无法实现的其他事情。
    webpack 插件是一个具有 apply方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。

    ConsoleLogOnBuildWebpackPlugin.js

    const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
    
    class ConsoleLogOnBuildWebpackPlugin {
      apply(compiler) {
        compiler.hooks.run.tap(pluginName, (compilation) => {
          console.log('webpack 构建正在启动!');
        });
      }
    }
    
    module.exports = ConsoleLogOnBuildWebpackPlugin;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    compiler hook 的tap方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中重复使用。

    2.5.1 用法

    由于插件可以携带参数/选项,在进行webpack配置时,需要向plugins属性传入一个new实例。

    2.5.2 配置方式

    webpack.config.js

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const webpack = require('webpack');
    const path = require('path');
    
    module.exports = {
        entry: './src/app.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist'),
        },
        module: {
            rules: [
                {
                    test: /\.(js | jsx)$/,
                    use: 'babel-loader',
                },
            ],
        },
        plugins: [
            new webpack.ProgressPlugin(),
            new HtmlWebpackPlugin({ template: './src/index.html' }),
        ],
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    2.5.3 Node API方式

    在使用 Node API 时,还可以通过配置中的 plugins 属性传入插件。
    some-node-script.js

    const webpack = require('webpack'); // 访问 webpack 运行时(runtime)
    const configuration = require('./webpack.config.js');
    
    let compiler = webpack(configuration);
    
    new webpack.ProgressPlugin().apply(compiler);
    
    compiler.run(function (err, stats) {
      // ...
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.6 Congiguration(配置)

    由于webpack遵循CommonJS模块规范,因此可以在配置中使用:

    • 通过require(...)引入其他文件及使用npm下载的工具函数
    • 使用JS控制流表达式,例如? :操作符
    • value使用常量或变量赋值
    • 编写并执行函数,生成部分配置

    避免如下操作:

    • 当使用 webpack CLI 工具时,访问 CLI 参数(应编写自己的 CLI 工具替代,或者使用 --env
    • 导出不确定的结果(两次调用 webpack 应产生相同的输出文件)
    • 编写超长的配置(应将配置文件拆分成多个)
    2.6.1 基本配置

    webpack.config.js

    const path = require('path');
    
    module.exports = {
        mode: 'development',
        entry: './src/app.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: '[name].js',
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2.6.2 多个target

    除了可以将单个配置导出为 object,function或Promise以外,还可以将其导出为多个配置。

    对于多targets(如AMDCommonJS)构建library

    module.exports = [
        {
            output: {
                filename: './dist-amd.js',
                libraryTarget: 'amd',
            },
            name: 'amd',
            entry: './app.js',
            mode: 'production',
    	},
        {
            output: {
                filename: './dist-commonjs.js',
                libraryTarget: 'commonjs',
            },
            name: 'commonjs',
            entry: './app.js',
            mode: 'production', 
        },
    ];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    只传一个--config-name名字标识,webpack将只会构建指定的配置项,比如--config-amd

    如果某个配置依赖另一个配置的输出,可以使用一个dependencies列表指定一个依赖列表。

    webpack.config.js

    module.exports = [
        {
            name: 'client',
            target: 'web',
            // ...
        },
        {
            name: 'server',
            target: 'node',
            dependencies: ['client'],
        },
    ];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果导出了多个配置,可以在配置中使用parallelism选项来指定编译的最大并发数。类型:number,支持版本:5.22.0+

    module.exports = [
      {
        //config-1
      },
      {
        //config-2
      },
    ];
    module.exports.parallelism = 1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.6.3 配置其他语言

    TypeScript

    先安装必要依赖,比如TypeScript以及其相应的类型声明:

    npm i --save-dev typescript ts-node @types/node @types/webpack
    # 如果使用版本低于 v4.7.0 的 webpack-dev-server,还需要安装以下依赖
    npm i --save-dev @types/webpack-dev-server
    
    • 1
    • 2
    • 3

    示例配置文件如下:

    webpack.config.ts

    import * as path from 'path';
    import * as webpack from 'webpack';
    import 'webpack-dev-server';
    
    const config: webpack.Configuration = {
        mode: 'production',
        entry: './src/app.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: [name].bundle.js,
        },
    };
    
    export default config;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    该示例需要 typescript 版本在 2.7 及以上,并在 tsconfig.json 文件的 compilerOptions中添加 esModuleInteropallowSyntheticDefaultImports 两个配置项。

    需要确保 tsconfig.jsoncompilerOptionsmodule 选项的值为 commonjs,否则 webpack 的运行会失败报错,因为 ts-node 不支持 commonjs 以外的其他模块规范。

    可以通过三个途径来完成 module 的设置:

    • 直接修改 tsconfig.json 文件

    • 修改 tsconfig.json 并且添加 ts-node 的设置。

    • 使用 tsconfig-paths

    第一种方法就是打开 tsconfig.json 文件,找到 compilerOptions 的配置,然后设置 targetmodule 的选项分别为 "ES5""CommonJs" (在 target 设置为 es5 时你也可以不显示编写 module 配置)。

    其他两种方法查看:https://www.webpackjs.com/configuration/configuration-languages/#typescript

    Babel and JSX

    安装依赖项:

    npm i --save-dev babel-register jsxobj babel-preset-es2015
    
    • 1

    .babelrc

    {
      "presets": ["es2015"]
    }
    
    • 1
    • 2
    • 3

    webpack.config.babel.js

    import jsxobj from 'jsxobj';
    
    // 插件引入示例
    const CustomPlugin = (config) => ({
        ...config,
        name: 'custom-plugin',
    });
    
    export default (
    	<webpack target='web' watch mode='production'>
        	<entry path='src/index.js' />
        	<resolve>
        		<alias
        			{...{
                        react: 'preact-compat',
                        'react-dom': 'preact-compat',
        			}}
        		/>
            </resolve>
            <plugins>
                <CustomPlugin foo="bar" />
            </plugins>
        </webpack>
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    ARM传输数据以及移位操作
    Verilog 过程赋值 区别 详解
    鲁大师7月新机性能/流畅榜:性能跑分突破123万!
    Origin常用功能
    BEVFormer -通过时空transformers学习多视角图像的BEV表示
    并行计算总结
    mongodb 实现两个集合的关联并分页查询
    2022年京东双十一手机数码全品类数据回顾
    SpringCloud 微服务注册中心 Eureka - Client
    【大数据 | 综合实践】大数据技术基础综合项目 - 基于GitHub API的数据采集与分析平台
  • 原文地址:https://blog.csdn.net/qq_41488033/article/details/133882774