webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

1.入门

npm i webpack

npm i webpack-cli

npx webpack “./src/main.js” –mode development

基本的webpack.config.js文件

const path = require('path');

module.exports = {
    //入口
  entry: "./src/index.js",
    //打包的输出
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
    //插件
  plugins:[

  ],
    //开发模式 和 生产模式
  mode: "development",
    //模块
  module: {
    rules: [
    ]
  },
};
{
  "name": "shopping",
  "version": "1.0.0",
  "description": "shopping",
  "main": "./src/index.js",
  "scripts": {
    "package": "webpack",
    "dev": "webpack server",
    "built": "webpack"
  },
  "author": "richu",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.26.0",
    "@babel/preset-env": "^7.26.0",
    "babel-loader": "^9.2.1",
    "copy-webpack-plugin": "^12.0.2",
    "css-loader": "^7.1.2",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.6.3",
    "style-loader": "^4.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^5.96.1",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^5.1.0"
  }
}

2.资源的处理

1.处理css资源

npm i css-loader style-loader -D

const path = require('path');

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  plugins:[
	  {  
        test: /\.css$/, //处理css资源
        use: [
              'style-loader', //将js中css通过创建style标签添加html文件中生效
              'css-loader'   //将css资源编译成commonJS的模块到JS中
             ], 
      },
  ],
  mode: "development",
  module: {
    rules: [
    ]
  },
};

2.处理less资源

npm i less less-loader –save-dev

module: {
    rules: [
        {
            {
            test: /\.less$/,
            use: ['style-loader', 'css-loader','less-loader'], 
		}
	]
},

3.处理sass资源

npm i sass sass-loader -D

{
    test: /\.s[ac]ss$/,
        use: ['style-loader', 'css-loader','sass-loader'], 
}

4.处理图片资源

将图片资源转为base64

{
    test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
    type:"asset",
    parser:{
    dataUrlCondition:{
    	maxSize:10*1024
        }
   }
}

5.修改打包输出的文件目录

1.修改js的输出目录

output: {
  path: path.resolve(__dirname, "dist"),
  filename: "js/bundle.js",
},

2.修改图片的输出目录

{
    test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
    type:"asset",
     parser:{
          dataUrlCondition:{
               maxSize:10*1024
             }
        },
        generator:{
          //输出图片名字
        filename:"images/[hash][ext]"
    }
}

6.自动清空上次打包的内容

output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/bundle.js",
    //将path对应的目录整个清空
    clean:true
},

7.处理Html资源

npm i –save-dev html-webpack-plugin

new HtmlWebpackPlugin({
    filename:'index.html',
    template:'./src/index.html'
}),

8.搭建开发服务器

npm i webpack-dev-server -D

devServer: {
  static: {
      directory: path.join(__dirname, 'src'), //静态文件目录
    },
  compress: true, //压缩
  port: 9000, //端口
},

3.eslint

1.基本配置

1.parserOptions 解析选项

2.rules 具体规则

  • off 或0 关闭规则
  • warn 或 1 使用警告级别的错误
  • error 或 2 使用错误级别的错误

3.extends继承

2.入门

npm i eslint-webpack-plugin –save-dev

npm i eslint –save-dev

  • 创建.eslintrc.js文件
module.exports = {
    extends: [
        "eslint:recommended",  //继承eslint
    ],
    env:{
        node:true,  //启用全局node
        browser:true, //启用浏览器全局变量
    },
    parserOptions: {
        ecmaVersion:6, //es6
        sourceType:"module", //es module
    },
    plugins: [
    ],
    rules: {
        "no-var":2,  //使用var的错误级别为2
    },
}
  • 创建.eslintignore文件
dist  //忽略dist文件夹

4.babel

将es6转为向后兼容的js代码

npm i -D babel-loader @babel/core @babel/preset-env
{
    test: /\.js$/, 
    exclude: /node_modules/,  //不需要加载node_modules文件内的文件
    use: [{
    	loader: 'babel-loader',
    	options:{
    		presets:["@babel/preset-env"]
    	}
    }],
},

或者将OPtions写到babel.config.js

  • 创建babel.config.js文件
module.exports = () => ({
    presets: [require("@babel/preset-env")],  //只能预设
  });

生产模式的配置

优化代码的性能

优化代码的打包速度

mode: “production”,

5.优化

1.将css打包成单独文件

npm i –save-dev mini-css-extract-plugin

{
	test: /\.css$/,
	use: [MiniCssExtractPlugin.loader, 'css-loader'], 
},
{
	test: /\.less$/,
	use: [MiniCssExtractPlugin.loader, 'css-loader','less-loader'], 
},
{
	test: /\.s[ac]ss$/,
	use: [MiniCssExtractPlugin.loader, 'css-loader','sass-loader'], 
},
    
    new MiniCssExtractPlugin()

指定输出文件目录

new MiniCssExtractPlugin({
    filename:"css/main.css"
})

2.css兼容性处理

npm i postcss-loader postcss postcss-preset-env -D

{
  test: /\.css$/,
  use: [MiniCssExtractPlugin.loader, 'css-loader',{
    loader:"postcss-loader",
    options:{
      postcssOptions:{
        plugins:[
          "postcss-preset-env"
        ]
      }
    }
  }], 
},

在package.json中加入

"browserslist":[
  "ie >= 8"
]

3.封装样式Loader函数

function GetStyleLoader(pre) {
  return [
    MiniCssExtractPlugin.loader, 'css-loader', {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: [
            "postcss-preset-env"
          ]
        }
      }
    },
    pre
  ].filter(Boolean)
}
{
  test: /\.css$/,
  use: GetStyleLoader(),
},
{
  test: /\.less$/,
  use: GetStyleLoader('less-loader'),
},
{
  test: /\.s[ac]ss$/,
  use: GetStyleLoader('sass-loader')
},

4.css压缩

npm i css-minimizer-webpack-plugin –save-dev

new CssMinimizerPlugin()

5.sourceMap源代码映射

当我们在开发过程中,可能会出现错误,但是当我们在浏览器调试时会发现,报错的位置是打包后的位置,因此我们需要sourceMap定位源代码错误的位置,更方便开发

devtool: "eval-cheap-module-source-map"   ///开发环境使用
devtool: "source-map"                     ///生成环境使用

6.提升打包速度

devServer: {
  static: {
    directory: path.join(__dirname, 'src'),
  },
  compress: true,
  port: 9000,
  hot:true
},

7.OneOf

每个文件只能被其中一个Loader(配置)处理

module: {
  rules: [
    {
      oneOf:[
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader',
              options: {
                presets: ["@babel/preset-env"]
              }
            }
          ],
        },
        {
          test: /\.css$/,
          use: GetStyleLoader(),
        },
        {
          test: /\.less$/,
          use: GetStyleLoader('less-loader'),
        },
        {
          test: /\.s[ac]ss$/,
          use: GetStyleLoader('sass-loader')
        },
        {
          test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
          type: "asset",
          parser: {
            dataUrlCondition: {
              maxSize: 10 * 1024
            }
          },
          generator: {
            //输出图片名字
            filename: "images/[hash][ext]"
          }
        }
      ]
    }
  ]
},

8.Include/Exclude

Exclude 排除,除了指定的都处理

Include 制定 处理制定的

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

9.Bable和Eslint的缓存

Bable

{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [
    {
      loader: 'babel-loader',
      options: {
        cacheDirectory: true, //开启缓存
        cacheCompression: false //关闭缓存文件压缩
        /* presets: ["@babel/preset-env"] */
      }
    }
  ],
},

Eslint

new ESLintWebpackPlugin(
  {
    context: path.resolve(__dirname, "src"),
    exclude:/node_modules/,
    cache:true,
    cacheLocation:path.join(__dirname,'../node_modules/.cache/eslintcache')
  }
),

10.多进程打包

npm i thread-loader -D

const os = require('os')
const threads = os.cpus().length
test: /\.js$/,
            exclude: /node_modules/,
            use: [
              {
                loader:'thread-loader',
                options: {
                  works: threads,
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: true, //开启缓存
                  cacheCompression: false //关闭缓存文件压缩
                  /* presets: ["@babel/preset-env"] */
                }
              }
            ],
},
new ESLintWebpackPlugin(
    {
        context: path.resolve(__dirname, "src"),
        exclude:"node_modules",
        cache:true,
        cacheLocation:path.join(__dirname,'../node_modules/.cache/eslintcache'),
        threads,
    }
),

压缩js

const TerserWebpackPlugin = require('terser-webpack-plugin')
new TerserWebpackPlugin({
	parallel:threads
})

更改压缩插件的位置

optimization:{
  //压缩的操作
  minimizer:[
    new CssMinimizerPlugin(),  //压缩css
    new TerserWebpackPlugin({ //压缩js
      parallel:threads 
    })
  ]
},
mode: "development",

11.减少图片体积

npm i image-minimizer-webpack-plugin imagemin -D

无损压缩

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo –save-dev

const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");

module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ["gifsicle", { interlaced: true }],
              ["jpegtran", { progressive: true }],
              ["optipng", { optimizationLevel: 5 }],
              [
                "svgo",
                {
                  plugins: [
                    "preset-default",
                    "prefixIds",
                    {
                      name: "sortAttrs",
                      params: {
                        xmlnsOrder: "alphabetical",
                      },
                    },
                  ],
                },
              ],
            ],
          },
        },
      }),
    ],
  },
};

12.进一步解决兼容性问题

coreJs npm i core.js

babel.config.js

module.exports = function(api) {
  api.cache(true);
  return {
    presets: [
      [
        "@babel/preset-env", // 预设名称
        {
          useBuiltIns: 'usage', // 选项
          corejs: 3 // 选项
        }
      ]
    ]
  };
};

13.PWD

npm install workbox-webpack-plugin –save-dev

const WorkboxPlugin = require('workbox-webpack-plugin');
new WorkboxPlugin.GenerateSW({
    // 这些选项帮助快速启用 ServiceWorkers
    // 不允许遗留任何“旧的” ServiceWorkers
    clientsClaim: true,
    skipWaiting: true,
}),

注册 Service Worker

npx SW registered

启动服务

npm i server -g
npx server dist

6.CodeSplit

1.CodeSplit

添加多入口

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    another: './src/another-module.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};
const path = require('path');

module.exports = {
    entry: './src/index.js',
    mode: 'development',
    entry: {
        index: './src/index.js', //主入口
        another: './src/another-module.js', //其他js文件
    },
    output: {
        filename: 'main.js',  //主入口打包的输出目录
        filename: '[name].bundle.js', //其他js文件打包的输出目录
        path: path.resolve(__dirname, 'dist'),
    },
};

动态导入(Dynamic Imports)

通过模块的内联函数调用来分离代码。例如,使用import()函数:

import(/* webpackChunkName: 'subPageA' */'./subPageA').then(function(res){
  console.log('import()', res)
});

防止依赖重复

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    index: {
      import: './src/index.js',
      dependOn: 'shared',
    },
    another: {
      import: './src/another-module.js',
      dependOn: 'shared',
    },
    shared: 'lodash',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
 optimization: {
   runtimeChunk: 'single',
 },
};

2.SplitChunksPlugin

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        index: './src/index.js',
        another: './src/another-module.js',
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
        splitChunks: {
            chunks: 'all',
        },
    },
};

3.preload、prefetch

npm i –save-dev @vue/preload-webpack-plugin

const PreLoadWebpackPlugin = require('@vue/preload-webpack-plugin');

new PreLoadWebpackPlugin({
    rel:"preload",
    as:"script",
}),
/*new PreLoadWebpackPlugin({
    rel:"prefetch",
    as:"script",
}),*/

7.typeScript

npm install –save-dev typescript ts-loader

tsconfig.js

{
  "compilerOptions": {
    "outDir": "./dist/",
   "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react",
    "allowJs": true,
    "moduleResolution": "node",
  }
}

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

8.react-cli

1.webpack.dev.js

基本配置

const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')

function GetStyleLoader(pre) {
    return [
        MiniCssExtractPlugin.loader, 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/main.js",
    output: {
        path: undefined,
        filename: "js/[name].bundle.js",
        chunkFilename: "static/js/[name].chunk.js",
        assetModuleFilename: "static/media/[hash:10][ext][query]"
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false
                }
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
    ],
    mode: "development",
    devtool: "cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        }
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
    }
}

.eslintrc.js

module.exports = {
    extends: [
        "react-app",  //react
    ],
    parserOptions: {
        babelOptions:{
            presets:[
                ["react-app",false],
                "babel-preset-react-app/prod"
            ]
        }
    },
}

babel.config.js

module.exports = {
  presets: ['react-app'],
};

npm i eslint-webpack-plugin html-webpack-plugin css-loader postcss-loader style-loader less-loader postcss-preset-env sass stylus-loader sass-loader -D

npm i -D babel-loader @babel/core babel-preset-react-app eslint-config-react-app -D

npm i webpack-dev-server webpack webpack-cli -D

npm i react react-dom

2.解决启动后报错的问题

Error: [BABEL] D:\MyTest\cli\src\index.js: Using `babel-preset-react-app` requires that you specify `NODE_ENV` or `BABEL_ENV` environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "D:\\MyTest\\cli\\node_modules\\babel-preset-react-app\\index.js")

npm i –save-dev cross-env

设置启动脚本为 “serve”: “cross-env NODE_ENV=production webpack serve –config ./config/webpack.dev.js”,

//在webpack.config.js中配置
resolve: {
	extensions: ['.js','.jsx','.json'],
},

3.更新局部代码

npm i -D @pmmmwh/react-refresh-webpack-plugin react-refresh

const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
{
                test: /\.jsx?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                    plugins:[
			'react-refresh/babel'
		]
	}
},
                
                
new ReactRefreshPlugin()

4.解决路由报错问题

在我们使用react-router-dom的时候会出现这样的问题

Cannot GET /about

devServer:{
    host:"localhost",
    port:3000,
    open:true,
    hot:true,
    historyApiFallback:true //解决前端路由刷新404的问题
},

5.生产模式

const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');

function GetStyleLoader(pre) {
    return [
         MiniCssExtractPlugin.loader, 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname,"../dist"),
        filename: "js/[name].[contenthash:10].js",
        chunkFilename: "static/js/[name].chunk.js",
        assetModuleFilename : "static/media/[hash:10][ext][query]",
        clean:true,
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                }
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
        new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
        }),
    ],
    mode: "production",
    devtool: "source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        },
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
            test: /\.js(\?.*)?$/i,
        })]
    },
    resolve: {
        extensions: ['.js','.jsx','.json'],
    },
}

npm i css-minimizer-webpack-plugin mini-css-extract-plugin terser-webpack-plugin -D

6.图标的解析

npm i copy-webpack-plugin -D

const CopyWebpackPlugin = require('copy-webpack-plugin');
new CopyWebpackPlugin({
            patterns: [
              {
                from: path.join(__dirname, '../public'),
                to: path.resolve(__dirname,'../dist'),
                globOptions:{
                    //忽略这个文件
                    ignore:["**/index.html"]
                }
              }
            ]
        }),

7.合并配置

webpack.config.js

const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const isproduction = process.env.NODE_ENV === "production"

function GetStyleLoader(pre) {
    return [
         isproduction?MiniCssExtractPlugin.loader:'style-loader', 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/index.js",
    output: {
        path: isproduction?path.resolve(__dirname,"../dist"):undefined,
        filename: isproduction?"js/[name].[contenthash:10].js":"js/[name].bundle.js",
        chunkFilename: isproduction?"static/js/[name].chunk.js":"static/js/[name].chunk.js",
        assetModuleFilename : "static/media/[hash:10][ext][query]",
        clean:true
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                    plugins:[
                        !isproduction  && 'react-refresh/babel'
                    ].filter(Boolean)
                }
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
        !isproduction && new ReactRefreshPlugin(),
        isproduction && new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
        }),
        isproduction && new CopyWebpackPlugin({
            patterns: [
              {
                from: path.join(__dirname, '../public'),
                to: path.resolve(__dirname,'../dist'),
                globOptions:{
                    //忽略这个文件
                    ignore:["**/index.html"]
                }
              }
            ]
        }),
    ].filter(Boolean),
    mode: isproduction?"production": "development",
    devtool: isproduction?"source-map":"cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        },
        minimize:isproduction,
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
            test: /\.js(\?.*)?$/i,
        })]
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true //解决前端路由刷新404的问题
    },
    resolve: {
        extensions: ['.js','.jsx','.json'],
    },
}

8.优化

npm i antd

//webpack.config.js
function GetStyleLoader(pre) {
    return [
         isproduction?MiniCssExtractPlugin.loader:'style-loader', 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre && {
            loader:pre,
            options:pre === "less-loader"?{
                //antdesign自定义主题色
                lessOptions:{
                    modifyVars:{"@primary-color":"#1DA57A"},
                    javascriptEnabled:true
                }
            }:{}
        }
    ].filter(Boolean)
}

分开打包cacheGroups

optimization: {
        splitChunks: {
            chunks: "all",
            cacheGroups:{
                react:{
                    test:/[\\/]node_modules[\\/]react(.*)?[\\/]/,
                    name:"chunk-react",
                    priority:40,
                },
                antd:{
                    test:/[\\/]node_modules[\\/]antd[\\/]/,
                    name:"chunk-antd",
                    priority:30,
                },
                libs:{
                    test:/[\\/]node_modules[\\/]/,
                    name:"chunk-libs",
                    priority:20,
                }
            }
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        },
        minimize:isproduction,
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
            test: /\.js(\?.*)?$/i,
        })]
    },
module.exports = {
	...
    performance:false  //关闭性能分析,提升打包效率
}

9.VueCli

1.vue-loader

npm install -D vue-loader vue-template-compiler

npm i eslint-webpack-plugin html-webpack-plugin vue-style-loader css-loader postcss-loader sass less-loader sass-loader stylus-loader babel-loader postcss-preset-env webpack webpack-cli webpack-dev-server cross-env -D

// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  module: {
    rules: [
      // ... 其它规则
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin()
  ]
}

.eslintrc.js

module.exports = {
    root:true,
    env:{
        node:true
    },
    extends: [
      "plugin:vue/vue3-essential","eslint:recommended"
    ],
    parserOptions:{
        parser:"@babel/eslint-parser",
    }
  }

babel.config.js

module.exports = {
    presets: ['@vue/cli-plugin-babel/preset'],
};

npm i @babel/eslint-parser eslint-plugin-vue -D

2.给vue赋值全局变量

const {DefinePlugin} = require("webpack")

plugins: [
    ...
    new DefinePlugin({
        __VUE_OPTIONS_API__:true,
        __VUE_PROD_DEVTOOLS__:false,
        __VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
    })
],

3.整体开发模式下vue的配置

const path = require("path")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const {DefinePlugin} = require("webpack")

function GetStyleLoader(pre) {
    return [
         'vue-style-loader', 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/index.js",
    output: {
        path: undefined,
        filename: "static/js/[name].bundle.js",
        chunkFilename: "static/js/[name].chunk.js",
        assetModuleFilename : "static/media/[hash:10][ext][query]"
    },
    module: {
        rules: [
            {
                test: /\.js?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
        new VueLoaderPlugin(),
        new DefinePlugin({
            __VUE_OPTIONS_API__:true,
            __VUE_PROD_DEVTOOLS__:false,
            __VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
        })
    ],
    mode: "development",
    devtool: "cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        }
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true //解决前端路由刷新404的问题
    },
    resolve: {
        extensions: ['.js','.vue','.json'],
    },
}

4.生产模式配置

const path = require("path")
const {DefinePlugin} = require("webpack")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')
function GetStyleLoader(pre) {
    return [
         MiniCssExtractPlugin.loader, 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname,"../dist"),
        filename: "static/js/[name].[contenthash:10].js",
        chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
        assetModuleFilename : "static/media/[hash:10][ext][query]",
        clean:true,
    },
    module: {
        rules: [
            {
                test: /\.js?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
        new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
        }),
        new CopyWebpackPlugin({
            patterns: [
              {
                from: path.join(__dirname, '../public'),
                to: path.resolve(__dirname,'../dist'),
                globOptions:{
                    //忽略这个文件
                    ignore:["**/index.html"]
                }
              }
            ]
        }),
        new VueLoaderPlugin(),
        new DefinePlugin({
            __VUE_OPTIONS_API__:true,
            __VUE_PROD_DEVTOOLS__:false,
            __VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
        })
    ],
    mode: "production",
    devtool: "source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        },
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
            test: /\.js(\?.*)?$/i,
        })]
    },
    resolve: {
        extensions: ['.js','.vue','.json'],
    },
}

npm i mini-css-extract-plugin css-minimizer-webpack-plugin terser-webpack-plugin copy-webpack-plugin html-webpack-plugin -D

5.合并配置

webpack.config.js

const path = require("path")
const {DefinePlugin} = require("webpack")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')

const isproduction = process.env.NODE_ENV === "production"

function GetStyleLoader(pre) {
    return [
        isproduction ? MiniCssExtractPlugin.loader:"vue-style-loader", 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/index.js",
    output: {
        path: isproduction?path.resolve(__dirname,"../dist"):undefined,
        filename: isproduction?"static/js/[name].[contenthash:10].js":"static/js/[name].js",
        chunkFilename: isproduction?"static/js/[name].[contenthash:10].chunk.js":"static/js/[name].chunk.js",
        assetModuleFilename : "static/media/[hash:10][ext][query]",
        clean:true,
    },
    module: {
        rules: [
            {
                test: /\.js?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
        isproduction && new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
        }),
        isproduction && new CopyWebpackPlugin({
            patterns: [
              {
                from: path.join(__dirname, '../public'),
                to: path.resolve(__dirname,'../dist'),
                globOptions:{
                    //忽略这个文件
                    ignore:["**/index.html"]
                }
              }
            ].filter(Boolean)
        }),
        new VueLoaderPlugin(),
        new DefinePlugin({
            __VUE_OPTIONS_API__:true,
            __VUE_PROD_DEVTOOLS__:false,
            __VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
        })
    ],
    mode: isproduction ? "production":"development",
    devtool: isproduction ?"source-map":"cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        },
        minimize:isproduction,
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
            test: /\.js(\?.*)?$/i,
        })]
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true //解决前端路由刷新404的问题
    },
    resolve: {
        extensions: ['.js','.vue','.json'],
    },
}

6.element-plus的使用

npm i element-plus

//全部引入
import { createApp } from 'vue'
import App from './App'
import router from './router';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

const Vue = createApp(App)
Vue.use(router)
Vue.use(ElementPlus)

Vue.mount(document.getElementById("app"));

按需导入

npm install -D unplugin-vue-components unplugin-auto-import

npm i unplugin-vue-components@0.25.2 -D npm install unplugin-auto-import@0.16.1

// webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}

按需导入的使用

<template>
    <div class="about">AboutTitle
        <el-button type="primary">click me</el-button>
    </div>
</template>

<script>
import {ElButton} from 'element-plus'
export default{
    name:"About",
    componenets:{
        ElButton
    }
}
</script>

<style>
    .about{
        color: rgb(188, 14, 72);
    }
</style>

7.使用路径别名

resolve: {
    extensions: ['.js','.vue','.json'],
        alias:{
            "@":path.resolve(__dirname,"../src")
        }
},

8.优化

splitChunks: {
    chunks: "all",
        cacheGroups:{
            vue:{
                test:/[\\/]node_modules[\\/]vue(.*)?[\\/]/,
                name:"chunk-vue",
                priority:40,
            },
            elementPlus:{
               test:/[\\/]node_modules[\\/]element-plus[\\/]/,
               name:"chunk-elementPlus",
               priority:30,
            },
            libs:{
                test:/[\\/]node_modules[\\/]/,
                name:"chunk-libs",
                priority:20,
            }
       }
},
module.exports = {
	...
    performance:false  //关闭性能分析,提升打包效率
}

最后的完整配置

const path = require("path")
const {DefinePlugin} = require("webpack")
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

const isproduction = process.env.NODE_ENV === "production"

function GetStyleLoader(pre) {
    return [
        isproduction ? MiniCssExtractPlugin.loader:"vue-style-loader", 'css-loader', {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        pre
    ].filter(Boolean)
}

module.exports = {
    entry: "./src/main.js",
    output: {
        path: isproduction?path.resolve(__dirname,"../dist"):undefined,
        filename: isproduction?"static/js/[name].[contenthash:10].js":"static/js/[name].js",
        chunkFilename: isproduction?"static/js/[name].[contenthash:10].chunk.js":"static/js/[name].chunk.js",
        assetModuleFilename : "static/media/[hash:10][ext][query]",
        clean:true,
    },
    module: {
        rules: [
            {
                test: /\.js?$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/,
                use: GetStyleLoader(),
            },
            {
                test: /\.less$/,
                use: GetStyleLoader('less-loader'),
            },
            {
                test: /\.s[ac]ss$/,
                use: GetStyleLoader('sass-loader')
            },
            {
                test: /\.(png|jpg|jpe?g|gif|webp|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024
                    }
                }
            },
            {
                test: /\.(woff2|ttf)/,
                type: "asset/resource",

            }
        ]
    },
    plugins: [
        new ESLintWebpackPlugin(
            {
                context: path.resolve(__dirname, "../src"),
                exclude: "node_modules",
                cache: true,
                cacheLocation: path.join(__dirname, '../node_modules/.cache/eslintcache'),
            }
        ),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html")
        }),
        isproduction && new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
        }),
        isproduction && new CopyWebpackPlugin({
            patterns: [
              {
                from: path.join(__dirname, '../public'),
                to: path.resolve(__dirname,'../dist'),
                globOptions:{
                    //忽略这个文件
                    ignore:["**/index.html"]
                }
              }
            ].filter(Boolean)
        }),
        new VueLoaderPlugin(),
        new DefinePlugin({
            __VUE_OPTIONS_API__:true,
            __VUE_PROD_DEVTOOLS__:false,
            __VUE_PROD_HYDRATION_MISMATCH_DETAILS__:false
        }),
        AutoImport({
            resolvers: [ElementPlusResolver()],
          }),
        Components({
            resolvers: [ElementPlusResolver()],
        }),
    ],
    mode: isproduction ? "production":"development",
    devtool: isproduction ?"source-map":"cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all",
            cacheGroups:{
                vue:{
                    test:/[\\/]node_modules[\\/]vue(.*)?[\\/]/,
                    name:"chunk-vue",
                    priority:40,
                },
                elementPlus:{
                    test:/[\\/]node_modules[\\/]element-plus[\\/]/,
                    name:"chunk-elementPlus",
                    priority:30,
                },
                libs:{
                    test:/[\\/]node_modules[\\/]/,
                    name:"chunk-libs",
                    priority:20,
                }
            }
        },
        runtimeChunk:{
            name: entrypoint => `runtime~${entrypoint.name}.js`,
        },
        minimize:isproduction,
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin({
            test: /\.js(\?.*)?$/i,
        })]
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true //解决前端路由刷新404的问题
    },
    resolve: {
        extensions: ['.js','.vue','.json'],
        alias:{
            "@":path.resolve(__dirname,"../src")
        }
    },
    performance:false
}

10.原理分析

1.构建Loader

npm i html-webpack-plugin webpack webpack-cli -D

const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry:"./src/main.js",
    output:{
        path:path.resolve(__dirname,"./src"),
        filename:"js/[name].js",
        clean:true
    },
    module:{
        rules:[

        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "./public/index.html")
        }),
    ],
    mode:"development"
}

定义loader文件

//loaders/test-loaders.js
module.exports = function (content){
    console.log(content)
    return content
}

在rules中配置

//webpack.config.js
module:{
    rules:[
        {
            test: /\.js$/,
            loader:'./loaders/test-loader.js'
        }
    ]
},

输出结果

let arr = [1,2,3,4] ===>该文件为mian.js中的文件

/*
// content 文件管理的内容
// map sourceMap
// meta 别的loader传递的数据
*/
module.exports = function (content,map,meta){
    console.log(content,map,meta) 
    return content
}

2.同步loader

module.exports = function(content,map,meta){
    /*
    1    代表是否有错误
    2    content 处理的内容
    3    map 继续传递sourcemap
    4    meta 给下一个loader传递参数
    */
    this.callback(null,content,map,meta)
}

3.异步loader

module.exports = function(content,map,meta){
    const callback = this.async();

    setTimeout(()=>{
        console.log("test2")
        callback(null,content,map,meta)
    },1000)
}

4.raw loader

//<Buffer 6c 65 74 20 61 72 72 20 3d 20 5b 31 2c 32 2c 33 2c 34 5d 0d 0a>'
//接收到的是Buffer数据内容

module.exports = function(content){
    console.log(content)
    return content
}

module.exports.raw = true;

5.pitch loader

优先执行pitch内的方法

module.exports = function(content){
    return content
}

module.exports.pitch = function(){
    console.log("pitch")
}

6.自定义loader

清除所有的console.log()语句

module.exports = function(content){
    return content.replace(/console\.log\(.*\);?/g,"")
}

schema.json

{
    "type":"object",
    "properties":{
        "author":{
            "type":"string"
        }
    },
    "additionalProperties":false  //不能新增字段
}

banner-loader.js

const schema = require("./schema.json")
module.exports = function(conten){
    const options = this.getOptions(schema)
    const prefix = `
        /*
            Author:${options.author}
        */
    `
    return prefix + conten
}

webpack.config.js

{
    test: /\.js$/,
    loader:"./loaders/banner-loader.js",
    options:{
       "author":"RC"
    }
},

7.file-loader

file-loader.js

const loaderUtils = require("loader-utils")

module.exports = function(content){
    let interpolateName = loaderUtils.interpolateName(this,"[hash].[ext][query]",{
        content:content
    })
    interpolateName =`images/${interpolateName}`
    this.emitFile(interpolateName,content)

    return `module.exports = "${interpolateName}"`
}

module.exports.raw = true

webpack.config.js

{
    test: /\.(png|jpe?g|gif)$/,
    loader: "./loaders/file-loader.js",
    type:"javascript/auto"
},

8.style.lodaer

module.exports.pitch = function(remainingRequest){
    const path = remainingRequest.split('!').map(absolutePath => {
        return this.utils.contextify(this.context,absolutePath)
    }).join('!')

    const script =`
        import style from "!!${path}"
        const style1 = document.createElement('style')
        style1.innerHTML = style
        document.head.appendChild(style1)
        `
    //终止后面loader的执行
    return script
}

11.plugin

1.自定义plugin

class TestPlugin{
    constructor(){
        console.log("this is constructor")
    }
    apply(compile){
        console.log("compile")
    }
}
module.exports = TestPlugin

2.hooks的创建

apply(compile){
    console.log("compile")

    compile.hooks.environment.tap("TestPlugin",()=>{
        console.log(1)
    })
    compile.hooks.emit.tap("TestPlugin",()=>{
        console.log(2)
    })
    compile.hooks.emit.tapAsync("TestPlugin",()=>{
        setTimeout(()=>{
            console.log(3)
        },1000)
    })
}
class TestPlugin {
    constructor() {
        console.log("this is constructor")
    }
    apply(compiler) {
        console.log("compile")

        compiler.hooks.environment.tap("TestPlugin", () => {
            console.log(1)
        })
        compiler.hooks.emit.tap("TestPlugin", (compilation) => {
            console.log(2)

        })
        compiler.hooks.emit.tapAsync("TestPlugin", (compilation,callback) => {
            compilation.hooks.seal.tap("TestPlugin", () => {
                console.log("Test")
            })
            setTimeout(() => {
                console.log(3)
                callback()
            }, 1000)
        })
    }
}

3.在文件前添加注释的plugin

class BannerPlugin{
    apply(compiler){
        compiler.hooks.emit.tap("BannerPlugin",(compilation)=>{
            //过滤图片资源,只保留js和css资源
            const extension = ['js','css']
            //过滤资源
            const assets = Object.keys(compilation.assets).filter((path)=>{
                let splitted = path.split('.')
                const ext = splitted[splitted.length-1]
                return extension.includes(ext)
            })
            //需要添加的注释前缀
            let prefix = `/*
    Author:"RC"
*/`
            assets.forEach(asset=>{
                //获取原来的内容
                let source = compilation.assets[asset].source()
                //更改文件内容
                let content = prefix+=source
                compilation.assets[asset] = {
                    //source就是文件的具体内容
                    source(){
                        return content
                    },
                    //资源大小
                    size(){
                        return content.length
                    }
                }

            })
        })
    }
}

module.exports = BannerPlugin

传递参数

new BannerPlugin({
    "name":"wsRC"
})
constructor(options = {}){
    this.options = options
}

4.clean-plugins

清空输出目录的资源

class CleanPlugin{
    
    apply(compiler){
        const outputPath = compiler.options.output.path
        const fs = compiler.outputFileSystem
        compiler.hooks.emit.tap("CleanPlugin",(compilation)=>{
            this.removeFiles(fs,outputPath)
        })
    }
    //删除文件
    removeFiles(fs,output){
        const files = fs.readdirSync(output)
        files.forEach(file => {
            let filepath = `${output}/${file}`
            const fileStat = fs.statSync(filepath)
            if (fileStat.isDirectory()){
                //是文件夹
                this.removeFiles(fs,filepath)
            }else{
                //是文件
                fs.unlinkSync(filepath)
            }
        });
        
    }
}

module.exports = CleanPlugin

5.AnalyzeWebpackPlugin

分析包的大小

class AnalyzePlugin{
    apply(compiler){
        compiler.hooks.emit.tap("AnalyzePlugin",(compilation)=>{
            const assets = Object.entries(compilation.assets)
let content = `| 名称 | 大小 |
| --- | --- |`   
            assets.forEach(([filename,file])=>{
                content+=`\n| ${filename} | ${Math.ceil(file.size() / 1024)}kb |`
            })

            compilation.assets["analyze.md"] = {
                source(){
                    return content
                },
                size(){
                    return content.length
                }
            }
        })
    }
}

module.exports = AnalyzePlugin

6.inlinePlugin

将runtime文件内联到html文件

const HtmlWebpackPlugin = require("safe-require")("html-webpack-plugin")

class InlinePlugin{
    apply(compiler){
        compiler.hooks.compilation.tap("InlinePlugin",(compilation)=>{
            const hooks = HtmlWebpackPlugin.getHooks(compilation)

            /* 
            {
                tagName: 'script',
                voidTag: false,
                meta: { plugin: 'html-webpack-plugin' },
                attributes: { defer: true, src: 'js/main.js' }
            }
            */
            hooks.alterAssetTagGroups.tap(
                "InlinePlugin",(assets)=>{
                    assets.headTags = this.getInlineChunk(assets.headTags,compilation.assets)
                    assets.bodyTags = this.getInlineChunk(assets.bodyTags,compilation.assets)
                }
            )
        })
        
    }
    getInlineChunk(tags,assets){
        return tags.map((tag)=>{
            if (tag.tagName != "script") return tag
            const filepath = tag.attributes.src
            if (!filepath) return tag
            if (!/runtime(.*)\.js$/g.test(filepath)) return tag

            return   {
                tagName: 'script',
                innerHTML: assets[filepath].source(),
                closeTag:true
            }
        })
    }
}

module.exports = InlinePlugin