webpack基础学习,各个loader和plugin的具体配置

一、邂逅Webpack

Webpack是什么

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

  • 打包bundler:webpack可以将帮助我们进行打包,所以它是一个打包工具

  • 静态的static:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器);

  • 模块化module:webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;

  • 现代的modern:我们前端说过,正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展;

二、webpack配置和css处理

webpack配置文件

1、出口、入口的配置

我们可以在根目录下创建一个webpack.config.js文件,来作为webpack的配置文件:

module.exports = {
    entry: " 指定入口路径",
    output: {
        filename:"bundle.js", // 出口名字
        path: '出口路径'
    }
}

2、css-loader的使用

loader是什么

  • loader 可以用于对模块的源代码进行转换;

  • 我们可以将css文件也看成是一个模块,我们是通过import来加载这个模块的;

  • 在加载这个模块时,webpack其实并不知道如何对其进行加载,我们必须制定对应的loader来完成这个功能;

module.rules的配置如下:

  • test属性:用于对resource(资源)进行匹配的,通常会设置成正则表达式;

  • use属性:对应的值时一个数组:[UseEntry]

    • UseEntry是一个对象,可以通过对象的属性来设置一些其他属性

      • loader:必须有一个loader属性,对应的值是一个字符串;

      • options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;

      • query:目前已经使用options来替代;

    • 传递字符串(如:use: [ 'style-loader' ])是loader 属性的简写方式(如:use: [ { loader: 'style-loader'} ])

  • loader属性:Rule.use: [ { loader } ] 的简写。

module.exports = {
    entry: " 指定入口路径",
    output: {
        filename:"bundle.js", // 出口名字
        path: '出口路径'
    },
    module: {
        rules: [
            {
                test:/\.css$/,
                // loader:"css-loader" // 写法一
                // use:["css-loader"]   //写法二
                // 写法三
                use:[
                    {loader:"css-loader"}
                ]
            }
        ]
    }
}

3、style-loader

当我们通过css-loader来加载css文件时,代码没有生效。这是因为css-loader只是将.css文件进行解析,并不会将解析之后的css插入到页面中,而style-loader将完成插入style的操作

注意:因为loader的执行顺序是从右向左(或者说从下到上,或者说从后到前的),所以我们需要将styleloader写到css-loader的前面

        use:[
                    {loader:"style-loader"},
                    {loader:"css-loader"}
                ]

4、less-loader

        use:[
                    {loader:"style-loader"},
                    {loader:"css-loader"}
                    {loader:"less-loader"}
            ]

5、浏览器的兼容性

认识browserslist工具

  • Browserslist编写规则一:

    • defaults:Browserslist的默认浏览器(> 0.5%, last 2 versions, Firefox ESR, not dead)。

    • 5%:通过全局使用情况统计信息选择的浏览器版本。>=,<和<=工作过。

    • dead:24个月内没有官方支持或更新的浏览器。现在是IE 10,IE_Mob11,BlackBerry 10,BlackBerry 7,Samsung 4和OperaMobile12.1。

    • last 2 versions:每个浏览器的最后2个版本。

配置browserslist

  • 方案一:在package.json中配置;

    "browserslist": [
         "last 2 version",
         "not dead",
         "> 0.2%"
    ]

  • 方案二:单独的一个配置文件.browserslistrc文件;

     last 2 version
     not dead
     > 0.2%

6、认识postCss工具

PostCSS是一个通过JavaScript来转换样式的工具,这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置;

如何使用

安装工具:postcss、postcss-cli

npm install postcss postcss-cli -D

插件autoprefixer

添加浏览器前缀需要安装autoprefixer

npm install autoprefixer -D

直接使用使用postcss工具,并且制定使用autoprefixer

npx postcss --use autoprefixer -o end.css ./src/css/style.css

postcss-loader

  • 借助构建工具进行css处理

npm install postcss-loader -D
 {
  loader:"postcss-loader",
  options: {
   postcssOptions: {
    plugins: [
     require('autoprefixer')
    ]
   }
  }
 },
  • 单独的postcss配置

在跟目录下创建postcss.config.js

module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}
  • postcss-preset-env

在项目中配置postcss-loader时,我们一般不使用autoprefixer。而是使用另一插件postcss-preset-env

  • postcss-preset-env也是一个postcss的插件;

  • 它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境添加所需的polyfill;

  • 也包括会自动帮助我们添加autoprefixer(所以相当于已经内置了autoprefixer);

安装

npm install postcss-preset-env -D

使用

module.exports = {
    plugins: [
        require('postcss-preset-env')
    ]
}

三、加载和处理其他资源

1、file-loader

用来处理jpg、png等格式的图片

  • file-loader的作用就是帮助我们处理import/require()方式引入的一个文件资源,并且会将它放到我们输出的文件夹中;

安装

npm install file-loader -D

配置:

        {
              test:/\.(png|jpe?g|svg|gif)$/i,
              use: {
                  loader: "file-loader"
              }  
            }

1、文件名称规则

对处理后的文件名称按照一定的规则进行显示,一般使用PlaceHolders来完成,webpack给我们提供了大量的PlaceHolders来显示不同的内容:

介绍几个最常用的placeholder:

  • [ext]:处理文件的扩展名;

  • [name]:处理文件的名称;

  • [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);

  • [contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样,后面会讲到);

  • [hash:<length>]:截图hash的长度,默认32个字符太长了;

  • [path]:文件相对于webpack配置文件的路径;

2、设置文件名称和存放路径

        {
              test:/\.(png|jpe?g|svg|gif)$/i,
              use: {
                  loader: "file-loader"
                  options: {
                    name: "img/[name].[hash:8].[ext]",
                    outputPath: "img"
                  }
              }  
            }

2、url-loader

将较小的文件转换为base64的URI

安装:

npm install url-loader -D

配置:

    {
          test:/\.(png|jpe?g|svg|gif)$/i,
          use: {
              loader: "url-loader"
              options: {
                name: "img/[name].[hash:8].[ext]",
                outputPath: "img"
              }
          }  
        }

打包之后的显示结果跟file-loader一样,但是在打包好的dist文件夹中,看不到图片文件,而是转换为base64格式存储。

limit属性

限制转换base64格式的图片大小(比如小于100kb)

    {
          test:/\.(png|jpe?g|svg|gif)$/i,
          use: {
              loader: "url-loader"
              options: {
                limit:100 * 1024,
                name: "img/[name].[hash:8].[ext]",
                outputPath: "img"
              }
          }  
        }

3、asset module type

1、介绍

  • webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loader 、url-loader、file-loader

  • webpack5之后,我们可以直接使用资源模块类型(asset module type),来替代上面的这些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,并且配置资源体积限制实现;

2、使用

      {
        test: /\.(png|jpe?g|svg|gif)$/i,
        type: "asset/resource",
      },
  • 如何自定义文件的输出路径和文件名

方式一:修改output,添加assetModuleFilename属性;

  output: {
    filename: "bundle.js", // 出口名字
    path: "出口路径",
    assetModuleFilename:"img/[name].[hash:6][ext]"
  },
方式二:在Rule中,添加一个generator属性,并且设置filename;    
            {
        test: /\.(png|jpe?g|svg|gif)$/i,
        type: "asset/resource",
        generator: {
          filename: "img/[name].[hash:6][ext]",
        },
      },
  • url-loader中的limit限制图片大小效果

           {
        test: /\.(png|jpe?g|svg|gif)$/i,
        type: "asset",   // 注意
        generator: {
          filename: "img/[name].[hash:6][ext]",
        },
        parser: {
            dataUrlCondition: {
                maxSize: 100 *1024
            }
        }
      },

4、加载字体文件

处理特殊字体或者字体图标的使用

我们可以选择使用file-loader来处理,也可以选择直接使用webpack5的资源模块类型来处理;

           {
        test: /\.(woff2?|eot|ttf)$/,
        type: "asset/resource",
        generator: {
          filename: "img/[name].[hash:6][ext]",
        },
      },

四、认识plugin

  • Loader是用于特定的模块类型进行转换;

  • Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等;

1、CleanWebpackPlugin

每次修改了一些配置,重新打包时,都需要手动删除dist文件夹,CleanWebpackPlugin可以帮助我们完成这个功能。

安装:

npm install clean-webpack-plugin -D

配置:

const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
    plugins : [
        new CleanWebpackPlugin()
    ]
  };

2、HtmlWebpackPlugin

HtmlWebpackPlugin用来对HTML进行打包处理

安装:

npm install html-webpack-plugin -D

配置:

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      titile: "webpack案例",
    }),
  ],
};

3、自定义HTML模板

如果我们想在自己的模块中加入一些比较特别的内容:

  • 添加一个noscript标签,在用户的JavaScript被关闭时,给予响应的提示;

  • 比如在开发vue或者react项目时,我们需要一个可以挂载后续组件的根标签<div id="app"></div>;

自定义模板数据填充

上面的代码中,会有一些类似这样的语法<%变量%>,这个是EJS模块填充数据的方式。

在配置HtmlWebpackPlugin时,我们可以添加如下配置:

  • template:指定我们要使用的模块所在的路径;

  • title:在进行htmlWebpackPlugin.options.title读取时,就会读到该信息;

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      titile: "webpack案例",
      template: "./public/index.html"
    }),
  ],
};

4、DefinePlugin的介绍

当在我们的模块中还使用到一个BASE_URL的常量,我们需要设置这个常量,这个时候我们可以使用DefinePlugin插件;

使用:

DefinePlugin允许在编译时创建配置的全局常量,是一个webpack内置的插件(不需要单独安装):

const { DefinePlugin } = require("webpack");
module.exports = {
  plugins: [
    new DefinePlugin({
        BASE_URL:'"./"'  // 注意需要多一层包裹
    })
  ],
};

5、CopyWebpackPlugin

vue的打包过程中,如果我们将一些文件放到public的目录下,那么这个目录会被复制到dist文件夹中。这个复制的功能,我们可以使用CopyWebpackPlugin来完成;

安装:

npm install copy-webpack-plugin -D

配置:

  • from:设置从哪一个源中开始复制;

  • to:复制到的位置,可以省略,会默认复制到打包的目录下;

  • globOptions:设置一些额外的选项,其中可以编写需要忽略的文件:

    • .DS_Store:mac目录下回自动生成的一个文件;

    • index.html:也不需要复制,因为我们已经通过HtmlWebpackPlugin完成了index.html的生成;

    new CopyWebpackPlugin({
        patterns: [
            {
                from:"public",
                globOptions: {
                    ignore: [
                        '**/.DS_Store',
                        '**/index.html'
                    ]
                }
            }
        ]
    })

五、source-map

source-map是从已转换的代码,映射到原始的源文件。使浏览器可以重构原始源并在调试器中显示重建的原始源

使用:

第一步:根据源文件,生成source-map文件,webpack在打包时,可以通过配置生成source-map;

第二步:在转换后的代码,最后添加一个注释,它指向sourcemap;

//# sourceMappingURL=common.bundle.js.map

浏览器会根据我们的注释,查找响应的source-map,并且根据source-map还原我们的代码,方便进行调试。

分析source-map

  • version:当前使用的版本,也就是最新的第三版;

  • sources:从哪些文件转换过来的source-map和打包的代码(最初始的文件);

  • names:转换前的变量和属性名称(因为我目前使用的是development模式,所以不需要保留转换前的名称);

  • mappings:source-map用来和源文件映射的信息(比如位置信息等),一串base64VLQ(veriablelengthquantity可变长度值)编码;

  • file:打包后的文件(浏览器加载的文件);

  • sourceContent:转换前的具体代码信息(和sources是对应的关系);

  • sourceRoot:所有的sources相对的根目录;

生成source-map

webpack为我们提供了非常多的选项,目前为止是26个,来处理source-map,选择不同的值,打包形成的代码会有性能的差异,可以根据不同情况进行选择

  • 不会生成source-map的配置项

    • false:不使用source-map,也就是没有任何和source-map相关的内容。

    • nnone:production模式下的默认值,不生成source-map。

    • eval:development模式下的默认值,不生成source-map

      • 但是它会在eval执行的代码中,添加//#sourceURL=;

      • 它会被浏览器在执行时解析,并且在调试面板中生成对应的一些文件目录,方便我们调试代码;

source-map值

生成一个独立的source-map文件,并且在bundle文件中有一个注释,指向source-map文件;

bundle文件中有如下的注释:

//# sourceMappingURL=bundle.js.map

eval-source-map值

eval-source-map:会生成sourcemap,但是source-map是以DataUrl添加到eval函数的后面

inline-source-map值

inline-source-map:会生成sourcemap,但是source-map是以DataUrl添加到bundle文件的后面

cheap-source-map

cheap-source-map:

  • 会生成sourcemap,但是会更加高效一些(cheap低开销),因为它没有生成列映射(Column Mapping)

cheap-module-source-map值

会生成sourcemap,类似于cheap-source-map,但是对源自loader的sourcemap处理会更好。

hidden-source-map值

  • 会生成sourcemap,但是不会对source-map文件进行引用;

  • 相当于删除了打包文件中对sourcemap的引用注释;

//  被删除掉的
//# sourceMappingURL=bundle.js.map

nosources-source-map值

会生成sourcemap,但是生成的sourcemap只有错误信息的提示,不会生成源代码文件;

多个值的组合(重要)

事实上,webpack提供给我们的26个值,是可以进行多组合的。

组合的规则如下:

  • inline-|hidden-|eval:三个值时三选一;

  • nosources:可选值;

  • cheap可选值,并且可以跟随module的值;

    [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

在开发中,最佳的实践是什么呢?

  • 开发阶段:推荐使用source-map或者cheap-module-source-map

  • 测试阶段:推荐使用source-map或者cheap-module-source-map

  • 发布阶段:false、缺省值(不写)

六、Babel深入理解

1、babel是什么东西,用来做什么的

  • Babel是一个工具链,主要用于旧浏览器或者缓解中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;

  • 语法转换、源代码转换、Polyfill实现目标缓解缺少的功能等;

Babel命令行使用

  • @babel/core:babel的核心代码,必须安装;

  • @babel/cli:可以让我们在命令行使用babel;

npm install @babel/cli @babel/core

使用babel来处理我们的源代码:

  • src:是源文件的目录;

  • --out-dir:指定要输出的文件夹dist;

npx babel src--out-dirdist

插件的使用

比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:

npm install @babel/plugin-transform-arrow-functions -D
npx babel src--out-dirdist--plugins=@babel/plugin-transform-arrow-functions

查看转换后的结果,我们会发现const 并没有转成var,这是因为plugin-transform-arrow-functions,并没有提供这样的功能,我们需要使用plugin-transform-block-scoping 来完成这样的功能。

npm install @babel/plugin-transform-block-scoping -D
npx babel src--out-dirdist--plugins=@babel/plugin-transform-block-scoping @babel/plugin-transform-arrow-functions

Babel的预设preset

如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset)

安装 :

npm install @babel/preset-env -D

执行:

npx babel src--out-dirdist--presets=@babel/preset-env

2、Babel的底层原理

工作流程:

  • 解析阶段(Parsing)

  • 转换阶段(Transformation)

  • 生成阶段(CodeGeneration)

流程图:

3、babel-loader

安装:

npm install babel-loader @babel/core      

使用:

module.exports = {
    module: {
        rules: [
            {
                test:/\.js$/,
                use: {
                    loader: "babel-loader"
                }
            }
        ]
    }
}

指定使用的插件

我们必须指定使用的插件才会生效

module: {
        rules: [
            {
                test:/\.js$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        plugins: [
                            "@babel/plugin-transform-block-scoping",
                            "@babel/plugin-transform-arrow-functions"
                        ]
                    }
                }
            }
        ]
    }

babel-preset

如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个preset,webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel。

常见的预设:

  • env

  • react

  • TypeScript

安装preset-env:

npm install @babel/preset-env
  {
  test:/\.js$/,
      use: {
        loader: "babel-loader",
         options: {
            plugins: [
              "@babel/plugin-transform-block-scoping",
                "@babel/plugin-transform-arrow-functions"
           ]
         }
     }
  
}

Babel的Stage-X设置

在babel7之前(比如babel6中),我们会经常看到这种设置方式:

  • 它表达的含义是使用对应的babel-preset-stage-x预设;

  • 从babel7开始,已经不建议使用了,建议使用preset-env来设置;

4、Babel的配置文件

我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:

  • babel.config.json(或者.js,.cjs,.mjs)文件;

  • .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;

区别:

  • .babelrc.json:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的;

  • babel.config.json(babel7):可以直接作用于Monorepos项目的子包,更加推荐;

5、认识polyfill

更像是应该填充物(垫片),一个补丁,可以帮助我们更好的使用JavaScript;

使用场景:

比如我们使用了一些语法特性(例如:Promise,Generator,Symbol等以及实例方法例如Array.prototype.includes等),但是某些浏览器压根不认识这些特性,必然会报错,我们可以使用polyfill来填充或者说打一个补丁,那么就会包含该特性了;

可以通过单独引入core-js和regenerator-runtime来完成polyfill的使用:

npm install core-js regenerator-runtime --save
{
	test:/\.m?js$/,
	exclude:/node_modules/,
	use:"babel-loader"
}

配置babel.config.js

我们需要在babel.config.js文件中进行配置,给preset-env配置一些属性:

  • useBuiltIns:设置以什么样的方式来使用polyfill;

  • corejs:设置corejs的版本

    • 另外corejs可以设置是否对提议阶段的特性进行支持;

    • 设置proposals属性为true即可;

useBuiltIns属性设置

  • useBuiltIns属性有三个常见的值

    第一个值:false

    • 打包后的文件不使用polyfill来进行适配;

    • 并且这个时候是不需要设置corejs属性的;

    第二个值:usage

    • 会根据源代码中出现的语言特性,自动检测所需要的polyfill;

    • 这样可以确保最终包里的polyfill数量的最小化,打包的包相对会小一些;

    • 可以设置corejs属性来确定使用的corejs的版本;

    第三个值:entry

    • 如果我们依赖的某一个库本身使用了某些polyfill的特性,但是因为我们使用的是usage,所以之后用户浏览器可能会报错,果你担心出现这种情况,可以使用entry;

    • 需要在入口文件中添加`import 'core-js/stable'; import 'regenerator-runtime/runtime';

    • 这样做会根据browserslist目标导入所有的polyfill,但是对应的包也会变大;

6、认识Plugin-transform-runtime(了解)

在前面我们使用的polyfill,默认情况是添加的所有特性都是全局的,如果我们正在编写一个工具库,这个工具库需要使用polyfill,别人在使用我们工具时,工具库通过polyfill添加的特性,可能会污染它们的代码,所以,当编写工具时,babel更推荐我们使用一个插件:@babel/plugin-transform-runtime来完成polyfill的功能;

7、React的jsx支持

安装:

npm install @babel/preset-react -D

使用:

presets: [
	["@babel/preset-env", {
		useBuiltIns:"usage",
		corejs: 3.8
	}],
	["@babel/preset-react"]
]

8、TypeScript的编译

TypeScript通过compiler来转换成JavaScript

安装:

npm install typescript -D

TypeScript的编译配置信息我们通常会编写一个tsconfig.json文件:

tsc --init

之后我们可以运行npxtsc来编译自己的ts代码:

npx tsc

1、使用ts-loader编译TS

安装:

npm install ts-loader -D

配置:

{
	test:'/\.ts$/',
	exclude: /node_modules/,
	use: [
		"ts-loader"
	]
}

2、使用babel-loader编译TS

Babel是有对TypeScript进行支持

  • 我们可以使用插件:@babel/tranform-typescript;

  • 但是更推荐直接使用preset:@babel/preset-typescript;

安装:

npm install @babel/preset-typescript -D

配置:

{
    test:'/\.ts$/',
    exclude: /node_modules/,
    use: [
        "babel-loader"
    ]
}

七、大数据中关于代码格式校验(Eslint和prettierrc )

大数据中关于eslint的配置

/*
 * @Description: eslint 配置
 * @ 规则依赖于 @umijs/fabric,在此基础上,可自行添加自己的规则进行配置
 * @Author: 贾永昌
 * @Date: 2022-05-01 13:55:14
 * @LastEditTime: 2022-05-02 17:35:45
 */
module.exports = {
  extends: [require.resolve('@umijs/fabric/dist/eslint')],
​
  // in antd-design-pro
  globals: {
    ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
    page: true,
  },
​
  rules: {
    // 强制语句有分号结尾
    semi: [2, 'always'],
    // ? 操作符前后必须有空格 bad: 1||2  good: 1 || 2
    'space-infix-ops': 2,
    // ? 对象字面量中冒号前面禁止有空格,后面必须有空格 bad: {a :'a'} good:{a: 'a'}
    'key-spacing': 2,
    // ? 花括号首尾必须有空格
    'object-curly-spacing': [2, 'always'],
    // ? 语句块(if、function、class、try...catch等的大括号) 的前面必须要有空格
    'space-before-blocks': 2,
    // ? 箭头函数的箭头与后面的{}之间需要空格
    'arrow-spacing': 2,
    // ? 禁止多余的空格
    'no-multi-spaces': 2,
    // ? 禁止代码行结束后面有多余空格
    'no-trailing-spaces': 2,
    // ? 禁止多余空行
    'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 1, maxEOF: 1 }],
    // ? 允许标识符中使用悬空下划线(标识符的开头或末尾的下划线)
    'no-underscore-dangle': 0,
    // ? 允许逻辑短路、三元运算符等表达式求值
    'no-unused-expressions': 0,
    // ? 禁止使用嵌套的三元表达式
    'no-nested-ternary': 2,
    // ? 禁止对函数参数再赋值(保证react函数式编程纯函数的概念)
    'no-param-reassign': 2,
    // ? 禁止使用 var 定义变量
    'no-var': 2,
    // ? 禁止修改const声明的变量
    'no-const-assign': 2,
    // ? 函数调用时 函数名与()之间不能有空格
    'no-spaced-func': 2,
​
    // ? jsx 属性中强制使用双引号
    'jsx-quotes': [2, 'prefer-double'],
    // ? 禁止 jsx 属性对象的引用括号里 两边加空格
    'react/jsx-curly-spacing': [2, 'never'],
    // ? JSX 中前标签传有属性换行展示的话,其后面的 > 也需换行对齐展示
    'react/jsx-closing-bracket-location': 2,
    // ? 校验 jsx 中所有换行属性值缩进
    'react/jsx-indent-props': [2, 2],
    // ? jsx 中传入属性值是Boolean值且为true时,省略传入
    'react/jsx-boolean-value': 2,
    // ? 在 JSX 属性中禁止等号前后存在空格
    'react/jsx-equals-spacing': 2,
​
    // ? 关闭此规则,允许 useEffect 的依赖为空数组
    'react-hooks/exhaustive-deps': 0,
​
    // ? 未使用的变量警告提醒
    '@typescript-eslint/no-unused-vars': ['warn'],
    // ? 禁用使用在前,保证 useEffct 使用在最前面,这时候里面如果使用了外部的函数就会报这错
    '@typescript-eslint/no-use-before-define': 0,
    // ? 允许空的 ts 接口定义  eg: interface IProps {}
    '@typescript-eslint/no-empty-interface': 0,
  },
};
大数据中 .prettierrc 配置
{
  "printWidth": 80,
  "tabWidth": 2,
  "singleQuote": true,
  "useTabs": false,
  "semi": true,
  "jsxSingleQuote": false,
  "trailingComma": "all",
  "bracketSpacing": true,
  "jsxBracketSameLine": false,
  "arrowParens": "always",
  "requirePragma": false,
  "insertPragma": false,
  "proseWrap": "preserve", 
  "htmlWhitespaceSensitivity": "css",
  "overrides": [
    {
      "files": ".prettierrc",
      "options": { "parser": "json" }
    }
  ]
}

八、DevServer

为什么需要搭建本地服务器?

我们希望可以做到,当文件发生变化时,可以自动完成编译和展示

  • webpack watch mode

  • webpack-dev-server

  • webpack-dev-middleware

1、Webpack watch(基本不用)

webpack给我们提供了watch模式:

  • 在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译(损耗性能)

开启watch的两种方式

  • 方式一:在导出的配置中,添加watch: true;

  • 方式二:在启动webpack的命令中,添加--watch的标识;

"scripts" : {
	"watch":"webpack --watch"
}

2、webpack-dev-server(必会,常用)

除了可以监听到文件的变化,还可以具备实时重新加载的功能

安装:

npm install --save-dev webpack-dev-server

配置:

"scripts" : {
	"watch":"webpack --watch"
	"serve":"webpack serve --config wk.config.js"
}

webpack-dev-server 在编译之后不会写入到任何输出文件。而是将bundle 文件保留在内存中:

  • 事实上webpack-dev-server使用了一个库叫memfs(memory-fswebpack自己写的)

3、webpack-dev-middleware(基本不用)

如果我们想要有更好的自由度,可以使用webpack-dev-middleware;

定义:

webpack-dev-middleware 是一个封装器(wrapper),它可以把webpack处理过的文件发送到一个server,webpack-dev-server 在内部使用了它,然而它也可以作为一个单独的package 来使用,以便根据需求进行更多自定义设置;

4、output的publicPath(outPut中的配置)

output中还有一个publicPath属性,该属性是指定index.html文件打包引用的一个基本路径

  • 它的默认值是一个空字符串,所以我们打包后引入js文件时,路径是bundle.js;

  • 在开发中,我们也将其设置为/,路径是/bundle.js,那么浏览器会根据所在的域名+路径去请求对应的资源;

  • 如果我们希望在本地直接打开html文件来运行,会将其设置为./,路径时./bundle.js,可以根据相对路径去查找资源;

    module.exports = {
        entry: " 指定入口路径",
        output: {
            filename:"bundle.js", // 出口名字
            path: '出口路径'
            publicPath:'./'
        }
    }

5、devServer的publicPath

devServer中也有一个publicPath的属性,该属性是指定本地服务所在的文件夹

  • 它的默认值是/,也就是我们直接访问端口即可访问其中的资源http://localhost:8080

  • 如果我们将其设置为了/abc,那么我们需要通过http://localhost:8080/abc才能访问到对应的打包后的资源

  • 并且这个时候,我们其中的bundle.js通过http://localhost:8080/bundle.js也是无法访问的:

    • 所以必须将output.publicPath也设置为/abc;

    • 官方其实有提到,建议devServer.publicPath与output.publicPath相同

6、devServer的contentBase(不常用)

主要作用是如果我们打包后的资源,又依赖于其他的一些资源,那么就需要指定从哪里来查找这个内容

  • 比如在index.html中,我们需要依赖一个abc.js文件,这个文件我们存放在public文件中

  • 在index.html中,我们应该如何去引入这个文件

    • 比如代码是这样的:<script src="./public/abc.js"></script>

    • 但是这样打包后浏览器是无法通过相对路径去找到这个文件夹的;

    • 所以代码是这样的:<script src="/abc.js"></script>;

    • 但是我们如何让它去查找到这个文件的存在呢?设置contentBase即可

devserver: {
 contentBase: path.resolve(__dirname,"why"),
 watchContentBase:true   //监听contentBase发生变化后重新编译
}

7、hotOnly、hos、port、open、compress配置

  • hotOnly是当代码编译失败时,是否刷新整个页面

  • port设置监听的端口,默认情况下是8080

  • host设置主机地址

  • open是否打开浏览器

  • compress是否为静态文件开启gzip compression

devserver: {
 	contentBase: path.resolve(__dirname,"why"),
	watchContentBase:true,   //监听contentBase发生变化后重新编译
 	hotOnly:true,
 	host: 0.0.0.0,
 	port: 3000,
 	open: true,
 	compress:true
}

8、Proxy代理(解决跨域问题)注意:在开发环境中使用

我们可以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决我们的跨域问题了

配置:

  • target:表示的是代理到的目标地址,比如/api-hy/moment会被代理到http://localhost:8888/api-hy/moment

  • pathRewrite:默认情况下,我们的/api-hy也会被写入到URL中,如果希望删除,可以使用pathRewrite;

  • secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;

  • changeOrigin:它表示是否更新代理后请求的headers中host地址;

  devserver: {
    '/api': {
      // 标识需要进行转换的请求的url
      target: 'http://172.16.188.188:8000', // 服务端域名
      changeOrigin: true, // 允许域名进行转换
      pathRewrite: {
        // 将请求url里的ci去掉
        '^/api': '',
      },
      logLevel: 'debug',
      secure: false, // 将该属性设置为false,将允许在https上运行或者运行在证书无效的后端服务器
    },
  },

9、historyApiFallback

  • historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误。

  • boolean值:默认是false

    • 如果设置为true,那么在刷新时,返回404错误时,会自动返回index.html的内容;

  • object类型的值,可以配置rewrites属性:

    • 可以配置from来匹配路径,决定要跳转到哪一个页面;

  devserver: {
    '/api': {
      // 标识需要进行转换的请求的url
      target: 'http://172.16.188.188:8000', // 服务端域名
      changeOrigin: true, // 允许域名进行转换
      pathRewrite: {
        // 将请求url里的ci去掉
        '^/api': '',
      },
      logLevel: 'debug',
      secure: false, // 将该属性设置为false,将允许在https上运行或者运行在证书无效的后端服务器
    },
    historyApiFallback: {
        rewrites:[
            {from: /abc/, to:"/index.html"}
        ]
    }
  },

九、模块热替换(HMR)

什么是HMR

  • HMR的全称是Hot Module Replacement,翻译为模块热替换;

  • 模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;

如何使用HMR

  • 默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可;

  • 在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading;

1、开启HMR

// 在webpack.config.js 中添加以下配置
devserver: {
    hot:true
}

同时还需要指定发生更新的模块

if(module.hot) {
    module.hot.accept("./*文件路径",() => {
        console.log()
    })
}

2、框架中的HMR

项目中已经有非常成熟的方案,别操心了

  • vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验

  • react开发中,有React HotLoader,实时调整react组件(目前React官方已经弃用了,改成使用reactrefresh);

3、HMR的原理

HMR的原理是什么

  • webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket服务(net.Socket)

  • express server负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析);

HMR Socket Server,是一个socket的长连接:(想想webSocket,需要及时通信)

  • 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)

  • 当服务器监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk);

  • 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器)

  • 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新;

十、resolve模块解析

resolve用于设置模块如何被解析:

  • resolve可以帮助webpack从每个require/import 语句中,找到需要引入到合适的模块代码;

  • webpack 使用enhanced-resolve来解析文件路径;

webpack能解析三种文件路径:

  • 绝对路径

  • 相对路径

  • 模块路径:在resolve.modules中指定的所有目录检索模块,默认值是['node_modules'],所以默认会从node_modules中查找文件;

1、extensions和alias配置

extensions是解析到文件时自动添加扩展名:

  • 默认值是['.wasm','.mjs','.js','.json'];

    配置:

    module.exports = {
        entry: " 指定入口路径",
        output: {
            filename:"bundle.js", // 出口名字
            path: '出口路径'
        },
        resolve:{
        	extensions:['.wasm','.mjs','.js','.json','.jsx','.ts'],
        }
    }

我们可以使用alias给某些常见的路径起一个别名;

    resolve:{
    	extensions:['.wasm','.mjs','.js','.json','.jsx','.ts'],
    	alias: {
    		"@":resolveApp('./src'),
    		pages:resolveApp('./src/pages')
    	}
    }

十一、环境分离和代码分离

1、入口文件解析

context的作用是用于解析入口(entry point)和加载器(loader)

默认是webpack的启动目录

module.exports = {
	context:path.resolve(__dirname,'./')
	entry:"../src/index.js"
}

2、配置文件的分离

  • 将原来的webpack.config.js划分为webpack.comm.conf.js(通用配置)、webpack.dev.conf.js(开发环境)、webpack.prod.conf.js(生产环境)三部分

  • 利用mode配置项,区分开发环境和生产环境。用利用merge将用到的环境配置和通用配置合并

3、认识代码分离

代码分离(CodeSplitting)主要的目的是将代码分离到不同的bundle中,之后我们可以按需加载,或者并行加载这些文件;

Webpack中常用的代码分离有三种

  • 入口起点:使用entry配置手动分离代码;

  • 防止重复:使用EntryDependencies或者SplitChunksPlugin去重和分离代码;

  • 动态导入:通过模块的内联函数调用来分离代码;

1、多入口起点

entry: {
	index:"./src/index.js",
	main:"./src/main.js"
}
output: {
	filename:"[name].bundle.js",
	path:resolveApp("./build")
}	

2、EntryDependencies(入口依赖)

假如我们的index.js和main.js都依赖两个库:lodash、dayjs

  • 如果我们单纯的进行入口分离,那么打包后的两个bunlde都有会有一份lodash和dayjs;

    entry: {
    	index: {import:"./src/index.js",dependOn:"shared"},
    	main:{import:"./src/main.js",dependOn:"shared"},
    	shared:['lodash','axios']
    }
    output: {
    	filename:"[name].bundle.js",
    	path:resolveApp("./build"),
    	publicPath: ""
    }

3、SplitChunks

另外一种分包的模式是splitChunk,它是使用SplitChunksPlugin来实现的:

Webpack提供了SplitChunksPlugin默认的配置,我们也可以手动来修改它的配置:

  • 比如默认配置中,chunks仅仅针对于异步(async)请求,我们可以设置为initial或者all;

optimization: {
	splitChunks: {
		chunks:'all'
	}
}

4、SplitChunks自定义配置

SplitChunks: {
	chunks:'all',
	// 拆分包的大小,至少为minsize
	// 如果一个包拆分出来不到minsize,那么将不会被拆分
	minsize:100,
	// 将大于maxMize的包,拆分成不小于minSize 的包
	maxsize:1000,
	// 至少包被引入的次数
	minChunks:2,
	// 最大异步请求数量
	maxAsyncRequests:30,
	// 最大的初始化请求数量
	cacheGroups: {
		venders: {
			test:/[\\/]node_modules[\\/]/,
			priority: -10,
			filename: "[id]_[hash:6]_vendor.js"
		},
		foo: {
			test:/foo/,
			priority: -20,
			filename: "foo_[id]_[name]_.js"
		}
	}
}

配置解析:

  • Chunks

    • 默认值是async

    • 另一个值是initial,表示对通过的代码进行处理

    • all表示对同步和异步代码都进行处理

  • minSize

    • 拆分包的大小, 至少为minSize;

    • 如果一个包拆分出来达不到minSize,那么这个包就不会拆分;

  • maxSize

    • 将大于maxSize的包,拆分为不小于minSize的包;

  • minChunks

    • 至少被引入的次数,默认是1;

    • 如果我们写一个2,但是引入了一次,那么不会被单独拆分;

  • name:设置拆包的名称

    • 可以设置一个名称,也可以设置为false;

    • 设置为false后,需要在cacheGroups中设置名称;

  • cacheGroups

    • 用于对拆分的包就行分组,比如一个lodash在拆分之后,并不会立即打包,而是会等到有没有其他符合规则的包一起来打包;

    • test属性:匹配符合规则的包;

    • name属性:拆分包的name属性;

    • filename属性:拆分包的名称,可以自己使用placeholder属性;

5、动态导入(dynamic import)

使用ECMAScript中的import()语法来完成,也是目前推荐的方式;

注意:

  • 在webpack中,通过动态导入获取到一个对象;

  • 真正导出的内容,在改对象的default属性中,所以我们需要做一个简单的解构;

动态导入的文件命名

它的命名我们通常会在output中,通过chunkFilename属性来命名

output: {
    filename: "[name].bundle.js",
    path:resolveApp("./build"),
    chunkFilename: "chunk_[id]_[name].js"
}

默认情况下我们获取到的[name]是和id的名称保持一致的

  • 我们希望修改name的值,可以通过magic comments(魔法注释)的方式

import(/* webpackChunkName:"bar"*/ "./bar").then(({default:bar}) => {
    bar()
})

6、optimization.chunkIds配置

optimization.chunkIds配置用于告知webpack模块的id采用什么算法生成。

  • natural:按照数字的顺序使用id;

  • named:development下的默认值,一个可读的名称的id;

  • deterministic:确定性的,在不同的编译中不变的短数字id

最佳实践:

  • 开发过程中,我们推荐使用named;

  • 打包过程中,我们推荐使用deterministic;

7、optimization. runtimeChunk配置

配置runtime相关的代码是否抽取到一个单独的chunk中:

  • 抽离出来后,有利于浏览器缓存的策略:

  • 设置的值

    • true/multiple:针对每个入口打包一个runtime文件;

    • single:打包一个runtime文件;

    • 对象:name属性决定runtimeChunk的名称;

    optimization:{
        chunkIds:"deterministic",
        runtimeChunk: {
            name:"runtime"
        }
    }

8、Prefetch和Preload

  • webpack v4.6.0+增加了对预获取和预加载的支持。

    • prefetch(预获取):将来某些导航下可能需要的资源

    • preload(预加载):当前导航下可能需要资源

    import(/* webpackChunkName:"bar"*/ 
    	/* webpackpeload:true */
    	"./bar").then(({default:bar}) => {
    	bar()
    })
  • 区别

    • preload chunk 会在父chunk 加载时,以并行方式开始加载。prefetch chunk 会在父chunk 加载结束后开始加载。

    • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。

    • preload chunk 会在父chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。

9、CDN

CDN称之为内容分发网络(ContentDeliveryNetwork或ContentDistributionNetwork,缩写:CDN)

开发中的应用方式:

  • 方式一:打包的所有静态资源,放到CDN服务器,用户所有资源都是通过CDN服务器加载的;

  • 方式二:一些第三方资源放到CDN服务器上;

方式一花钱,直接说方式二

一些比较出名的开源框架都会将打包后的源码放到一些比较出名的、免费的CDN服务器上

使用方法:

  • 第一步,我们可以通过webpack配置,来排除一些库的打包:

    externals: {
    	lodash: "_",
    	dayjs: "dayjs"
    }
  • 第二步,在html模块中,加入CDN服务器地址:

    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.js"></script>

10、认识shimming

  • shimming是一个概念,是某一类功能的统称:

    • 比如我们现在依赖一个第三方的库,这个第三方的库本身依赖lodash,但是默认没有对lodash进行导入(认为全局存在lodash),那么我们就可以通过ProvidePlugin来实现shimming的效果;

  • 注意:webpack并不推荐随意的使用shimming

    • Webpack背后的整个理念是使前端开发更加模块化;

    • 也就是说,需要编写具有封闭性的、不存在隐含依赖(比如全局变量)的彼此隔离的模块;

11、MiniCssExtractPlugin

MiniCssExtractPlugin可以帮助我们将css提取到一个独立的css文件中,该插件需要在webpack4+才可以使用。

安装:

npm install mini-css-extract-plugin -D

配置:

plugins: [
	new MiniCssExtractPlugin({
		filename:"css/[name].[contenthash:8].css",
		chunkfilename: "css/[name].[contenthash:8].css"
	})
],
module:{
	rules: [
		{
			test:/\.css$/i,
			use:[MiniCssExtractPlugin.loader,'css-loader']
		}
	]
}

12、Hash、ContentHash、ChunkHash

  • hash值的生成和整个项目有关系:

    • 比如我们现在有两个入口index.js和main.js;

    • 它们分别会输出到不同的bundle文件中,并且在文件名称中我们有使用hash;

    • 这个时候,如果修改了index.js文件中的内容,那么hash会发生变化;

    • 那就意味着两个文件的名称都会发生变化;

  • chunkhash可以有效的解决上面的问题,它会根据不同的入口进行借来解析来生成hash值:

比如我们修改了index.js,那么main.js的chunkhash是不会发生改变的;

  • contenthash表示生成的文件hash名称,只和内容有关系:

    • 比如我们的index.js,引入了一个style.css,style.css有被抽取到一个独立的css文件中;

    • 这个css文件在命名时,如果我们使用的是chunkhash;

    • 那么当index.js文件的内容发生变化时,css文件的命名也会发生变化;

    • 这个时候我们可以使用contenthash;

十二、DLL_Tree Shaking

认识DLL库(了解一下)

  • DLL全程是动态链接库(Dynamic Link Library),是为软件在Windows中实现共享函数库的一种实现方式;

  • webpack中也有内置DLL的功能,它指的是我们可以将可以共享,并且不经常改变的代码,抽取成一个共享的库;

Terser介绍和安装(一般使用默认配置)

  • Terser是一个JavaScript的解释(Parser)、Mangler(绞肉机)/Compressor(压缩机)的工具集;

    • 早期我们会使用uglify-js来压缩、丑化我们的JavaScript代码,但是目前已经不再维护,并且不支持ES6+的语法;

  • Terser可以帮助我们压缩、丑化我们的代码,让我们的bundle变得更小。

安装:

npm install terser -g // 可以进行局部安装,也可以全局安装

命令行使用

terser [input files] [options]

常见的配置项:

Compress option

  • arrows:class或者object中的函数,转换成箭头函数;

  • arguments:将函数中使用arguments[index]转成对应的形参名称;

  • dead_code:移除不可达的代码(tree shaking);

  • 等等其他属性,详情看官方文档

Mangle option

  • toplevel:默认值是false,顶层作用域中的变量名称,进行丑化(转换)

  • keep_classnames:默认值是false,是否保持依赖的类名称;

  • keep_fnames:默认值是false,是否保持原来的函数名称;

Terser在webpack中配置使用

  • (注意)真实开发中,我们不需要手动的通过terser来处理我们的代码,我们可以直接通过webpack来处理:

    • 在webpack中有一个minimizer属性,在production模式下,默认就是使用TerserPlugin来处理我们的代码的;

    • 如果我们对默认的配置不满意,也可以自己来创建TerserPlugin的实例,并且覆盖相关的配置;(基本不会手动配置)

module.exports = {
    optimization: {
        minimize:true,
        minimizer: [
            new TerserPlugin({
                parallel:true, // 使用多进程并发运行提高构建的速度,默认值是true,
                extractComments:false, // 默认值为true,表示会将注释抽取到一个单独的文件中
                terserOptions: { // 设置我们的terser相关的配置
                    compress: { // 设置压缩相关的选项;
                        arguments:true,
                        dead_code:true
                    },
                    mangle:true, // 设置丑化相关的选项,可以直接设置为true;
                    toplevel:true, // 底层变量是否进行转换
                    keep_classnames:false,// 保留类的名称
                    keep_fnames:false// 保留函数的名称;
                }
            })
        ]
    }
}

CSS的压缩

安装:

npm install css-minimizer-webpack-plugin -D

在optimization.minimizer中配置:

minimizer: [
    new CssMinimizerplugin({
        parallel: true
    })
]

提升作用域 Scope Hoisting

  • Scope Hoisting从webpack3开始增加的一个新功能,功能是对作用域进行提升,并且让webpack打包后的代码更小、运行更快;

  • 默认情况下webpack打包会有很多的函数作用域,Scope Hoisting可以将函数合并到一个模块中来运行

  • 使用:

    • 在production模式下,默认这个模块就会启用;

    • 在development模式下,我们需要自己来打开该模块;

    new webpack.optimize.ModuleConcatenationPlugin()

Tree Shaking

定义:最早的想法起源于LISP,用于消除未调用的代码(纯函数无副作用,可以放心的消除,这也是为什么要求我们在进行函数式编程时,尽量使用纯函数的原因之一)

webpack实现TreeShaking

两种方法:

  • 在optimization中配置usedExports为true,来帮助Terser进行优化;

  • 在package.json中配置sideEffects,直接对模块进行优化;

usedExports

在usedExports设置为true时,会有一段注释:unused harmony export mul,这段注释告知Terser在优化时,可以删除掉这段代码

注意:

  • 配置该属性时,需要将mode设置为development模式

  • usedExports实现tree Shaking是结合terse来完成的

sideEffects

sideEffects用于告知webpack compiler哪些模块时有副作用的(副作用的意思是这里面的代码有执行一些特殊的任务,不能仅仅通过export来判断这段代码的意义;)

  • 在package.json中设置sideEffects的值:

    • false:告知webpack可以安全的删除未用到的exports;

    • 如果有一些希望保留,可以设置数组

"sideEffects": [
    "./src/util/format.js",
    "*.css"
]

CSS实现TreeShaking

我们可以使用一个库来完成CSS的Tree Shaking:PurgeCSS,帮助我们删除未使用的CSS的工具

安装:

npm install purgecss-webpack-plugin -D

配置:

  • paths:表示要检测哪些目录下的内容需要被分析,这里我们可以使用glob;

  • 默认情况下,Purgecss会将我们的html标签的样式移除掉,如果我们希望保留,可以添加一个safelist的属性;

  • purgecss也可以对less文件进行处理(所以它是对打包后的css进行tree shaking操作)

new  PurgecssPlugin({
    paths:glob.sync(`${resolveApp(./src)}/**/*`,{nodir:true}),
    safelist: function() {
        return {
            standard:['html']
        }
    }
})

HTTP压缩

定义:HTTP压缩是一种内置在服务器和客户端之间的,以改进传输速度和带宽利用率的方式

流程:

第一步:HTTP数据在服务器发送前就已经被压缩了

第二步:兼容的浏览器在向服务器发送请求时,会告知服务器自己支持哪些压缩格式

第三步:服务器在浏览器支持的压缩格式下,直接返回对应的压缩后的文件,并且在响应头中告知浏览器;

目前的压缩格式

  • compress–UNIX的“compress”程序的方法(历史性原因,不推荐大多数应用使用,应该使用gzip或deflate);

  • deflate–基于deflate算法(定义于RFC1951)的压缩,使用zlib数据格式封装;

  • gzip–GNUzip格式(定义于RFC1952),是目前使用比较广泛的压缩算法;

  • br–一种新的开源压缩算法,专为HTTP内容的编码而设计;

Webpack对文件压缩

webpack中相当于是实现了HTTP压缩的第一步操作,我们可以使用CompressionPlugin。

安装:

npm install compression-webpack-plugin -D

配置:

new CompressionPlugin({
    test:/\.(css|js)$/,  // 匹配哪些文件需要压缩
    threshold:500, // 设置文件多大开始压缩
    minRatio: 0.7, // 至少采用的压缩比例
    algorithm: "gzip" // 采用的压缩算法
})

HTML文件中代码的压缩

我们之前使用了HtmlWebpackPlugin插件来生成HTML的模板,事实上它还有一些其他的配置:

  • inject:设置打包的资源插入的位置

    • true、false、body、head

  • cache:设置为true,只有当文件改变时,才会生成新的文件(默认值也是true)

  • minify:默认会使用一个插件html-minifier-terser

InlineChunkHtmlPlugin

可以辅助将一些chunk出来的模块,内联到html中

安装

npm install react-dev-utils -D

在production的plugins中进行配置:

module.exports = {
    plugin:[
        new InlineChunkHtmlPlugin(HtmlWebpackPlugin,[/runtime.+\.js/])
    ]
}

十三、webpack打包分析

分析一:打包的时间分析

speed-measure-webpack-plugin 可以帮助我们看到每一个loader、每一个plugin的打包时间

安装:

npm install speed-measure-webpack-plugin -D

配置:

const smp = new SpeedMeasurePlugin();
cpnst webpackConfig = smp.wrap({
    plugins: [new MyPlugin(),new MyOtherPlugin()]
})

分析二:打包后文件分析

使用webpack-bundle-analyzer工具

安装:

npm install webpack-bundle-analyzer -D

配置

module.exports = {
    plugins: [
        new BundleAnalyzerPlugin()
    ]
}

Compiler和Compilation的区别(面试题)

  • Compiler中webpack构建的之初就会创建的一个对象, 并且在webpack的整个生命周期都会存在(before -run -beforeCompiler-compile -make -finishMake-afterCompiler-done)

    • 只要是做webpack的编译, 都会先创建一个Compiler

  • Compilation是到准备编译模块(比如main.js), 才会创建Compilation对象

    • watch -> 源代码发生改变就需要重新编译模块

    • 主要是存在于compile -make 阶段主要使用的对象

  • Compiler可以继续使用(如果我修改webpack的配置, 那么需要重新执行run run build)

  • Compilation需要创建一个新的Compilation对象

猜你喜欢

转载自blog.csdn.net/znhyXYG/article/details/129176248