webpack笔记

406 阅读12分钟

@TOC

webpack 五个核心概念

  • Entry

    • 入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图
      • 单入口 输出一个chunk 和 多入口 输出多个chunk
  • Output

    • 输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
  • Loader

    • Loader 让 webpack 能 够 去 处 理 那 些 非 JavaScript 文 件 (webpack 自 身 只 理 解JavaScript)
  • Plugins

    • 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
  • Mode 模式(Mode)指示 webpack 使用相应模式的配置。

选项描述特点
development会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和NamedModulesPlugin。能让代码本地调试运行的环境
production会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。能让代码优化上线运行的环境

初始化配

  • 初始化 package.json
    • 输入指令: npm ini
  • 下载并安装 webpack
    • npm install webpack@4.41.6 webpack-cli@3.3.11 -g
    • npm install webpack@4.41.6 webpack-cli@3.3.11 -D
    • 最新版本问题:会造成中突导致运行失败 可运行版本:
      • npm i webpack@4.16.5 webpack-cli@3.1.0 html-webpack-plugin@3.2.0

编译打包应用命令

  • 开发环境指令:webpack src/js/index.js -o build/js/built.js --mode=development
    • 功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成浏览器能识别的语法。
  • 生产环境指令:webpack src/js/index.js -o build/js/built.js --mode=production
    • 功能:在开发配置功能上多一个功能,压缩代码。
  • 结论 webpack 能够编译打包 js 和 json 文件。能将 es6 的模块化语法转换成浏览器能识别的语法。能压缩代码。
  • 问题 不能编译打包 css、img 等文件。不能将 js 的 es6 基本语法转化为 es5 以下语法。 需要插件 Loader

Loader下载

  • css的loader
    • style-loader -- 把封装的css只要以style标签引入
    • css-loader -- 打包css资源
    • less-loader -- 解析less变为css 配置 less使用
    • mini-css-extract-plugin -- 把css独立封装出来
    • optimize-css-assets-webpack-plugin -- 压缩css资源
    • postcss-loader@3.0.0 & postcss-preset-env 处理css的兼容性,需要在package.json里配置东西
  • HTML和图片等其它资源的loader
    • url-loader -- 可以用来处理css的图片资源,不可以处理HTML图片
    • html-loader || html-withimg-loader -- 处理HTML的图片
    • file-loader -- 处理文字图标等资源
    • html-webpack-plugin -- 处理 HTML 的
  • js的loader
    • eslint-loader & eslint & eslint-config-airbnb-base & eslint-plugin-import -- 检测 js 语法 需要在package.json配置东西
    • babel-loader & @babel/core & core-js & babel -- 处理 js 兼容性问题
    • 基本js兼容性处理 --> @babel/preset-env -- 问题:只能转换基本语法,如promise高级语法不能转换
    • 全部js兼容性处理 -> @babel/polyfill -问题:只解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了
    • 需要做兼容性处理的就做:按需加载 --> core-js
  • webpack-dev-server -- 开发模式插件 在module.exports添加 target: 'web' // webpack5 bug 不能自动刷新 解决方案

webpack初体验

/*
       index.js webpack 入口起点文件
    1. 运行指令
        开发环境 : webpack  ./src/index.js -o  ./build/built.js --mode=development
            webpack 会义 ./src/index.js 为入口文件,打包后输出到 ./build/built.js
            整体打包环境,是开发环境
        生产环境 : webpack  ./src/index.js -o  ./build/built.js  --mode=production
            整体打包环境,生产环境
     2. 结论
        1. webpack 能处理js、json文件,不能处理css/img 等其它资源
        2. 生产环境和开发环境将 ES6 模块化编译成浏览器能识别的代码
        3. 生产环境比开发环境多一个压缩js代码

*/
import data from "./data.json"
// import  "./index.css"

console.log(data);
function add (x,y) {
    return x + y ;
}

console.log(add(1,3));

webpack 文件打包配置

/*
    webpack.config.js  webpack配置文件
        作用: 指示webpack  干那些活(当你运行 webpack 指令时,会加载里面的配置)
    所有构建工具都是基于  nodejs 平台运行大的~模块化默认采用commonjs。
*/
// resolve 用来并接绝对路径的方法
const { resolve } = require('path');

module.exports = {
    // webpack 配置
    // 入口起点
    entry : './src/index.js',
    // 输出
    output : {
        // 输出 文件名
        filename : 'built.js',
        // 输出路径
        // __dirname nodejs的变量,代表当前文件的目录绝对路径
        path : resolve(__dirname,'build')
    },
    // loader 配置
    module: {
        rules : [
            // 详细的loader配置  不同的文件需要不同的loader 处理
            {
                // 匹配那些文件  正则表达式
                test: /\.css$/,
                // 使用那些loader进行处理
                use: [
                    // use 数组中loader执行顺序: 从右到左,从上到下 依次执行
                    // 创建style标签,将js中的样式资源插入进行,添加到head中生效
                    'style-loader',
                    // 将css文件将commonjs模块加载到js中,里面的内容是样式字符串
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader' // 将 less文件 编译成css文件  需要 less 和 less-loader
                ]
            }
        ]
    },
    // plugins(插件) 配置
    plugins: [

    ],
    // mode 模式的设置
    mode : 'development'  // 开发模式
    // mode : 'production'  // 生产模式
}

打包HTML资源

/*
    loader :  1. 下载  2. 使用(配置loader)
    plugins : 1. 下载  2. 引入 3. 使用
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');  // html-webpack-plugin 下载后引入

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path:  resolve(__dirname,'build')
    },
    module : {
        rules: [
            // loader 配置
        ]
    },
    plugins: [
        // 配置plugins  nodejs下载 : html-webpack-plugin
        // 功能 : 默认会创建一个空的HTML文件,自动引入打包输出的所有资源(js/css)
        // 需求 : 需要有结构的
        new HtmlWebpackPlugin({
            // 复制  "./src/index.html" 文件。并自动引入打包输出的所有资源(js/css)
            template: "./src/index.html"
        })
    ],
    mode: 'development'
}

打包图片资源


const {resolve} = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
    entry: './src/index.js',
    output: {
        filename: "built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.less$/,
                // 要使用多个loader处理use
                use: ['style-loader','css-loader','less-loader']
            },
            {
                // 问题:默认处理不了HTML的img图片
                // 处理图片资源 单个直接使用 loader 不用 use | 处理依赖 url-loader file-loader
                test: /\.(jpg|png|gif)$/,
                loader: "url-loader",
                options: {
                    // 图片大小小于 8kb 就会被base64处理
                    // 优点: 减小请求数量(减轻服务器压力) 缺点:图片体积会更大(文件请求速度更慢)
                    limit: 8* 1024,
                    // 问题: 因为url-loader默认使用es6模块化解析,而html-loader引入图片时commonjs
                    // 解析时会出问题: 关闭url-loader的es6模块化,使用commonjs解析 就可以解决了
                    esModule : false,
                    // 给图片进行重命名    [hash:10].[ext] 取前十位保持原有的扩展名
                    name : "[hash:10].[ext]"

                }
            },
            {
                test: /\.html$/,
                // 处理 HTML文件的图片图片 (负责引入img,从而能被(url-loader/url-withimg-loader)进行处理)
                // 当 url-loader 处理不来图片时可以 使用 html-withimg-loader
                // loader: " url-loader " 处理不了
                loader: "html-withimg-loader"

            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    mode: "development"

}

其它资源打包(字体图标等)

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry:'./src/index.js',
    output: {
        filename: "built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
                {
                    test: /\.css$/,
                    use: ['style-loader','css-loader']
                },
                // 如何打包自他资源(除了html/css/js资源以外的资源)
                {
                    // 排除css/js/html资源
                    exclude: /\.(css|js|html|less)$/,
                    loader: "file-loader",
                    options : {
                        name: "[hash:10].[ext]"
                    }
                }
            ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    mode: "development"
}

derServer 帮助服务器开发

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry:'./src/index.js',
    output: {
        filename: "built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
                {
                    test: /\.css$/,
                    use: ['style-loader','css-loader']
                },
                {
                    // 打包自他资源(除了html/css/js资源以外的资源)
                    exclude: /\.(css|js|html|less)$/,
                    loader: "file-loader",
                    options : {
                        name: "[hash:10].[ext]"
                    }
                }
            ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    mode: "development",

    // 开发服务器 devServer :用来自动化(自动化编译,自动打开浏览器,自动刷新浏览器)
    // 特点:只会在内存中编译打包,不会有任何输出  启动 devServer 指令为 :  npx webpack-dev-server
    // 因为依赖问题最新版的webpack-cli没有 所以需要版本为: "webpack-cli": "^3.3.11"
    devServer: {
        contentBase: resolve(__dirname, 'build'),        // 项目构建后路径
        compress: true,        // 启动gzip压缩
        port: 3000,        // 端口号
        open: true        // 自动打开浏览器
    }
}

综合练习

/*
    开发环境配置 :让代码运行
        运行项目的指令:
            webpack  会将打包结果输出
            npx webpack-dev-server  只会在内存中编译打包不会输出结果
*/
const {resolve} = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: "js/built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,  // 处理css资源
                use: ['style-loader','css-loader']
            },
            {
                test: /\.less$/,   // 处理less资源
                use: ['style-loader','css-loader','less-loader']
            },
            {
                test: /\.(jpg|png|gif)/, // 处理图片资源
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]',
                    esModule: false,  // 关闭es6模块化的解析
                    outputPath: 'imgs'
                }
            },
            {
                test: /.html$/,  // 处理HTML的img图片
                loader: 'html-withimg-loader',
            },
            {
                exclude: /\.(css|js|html|jpg|less|png|gif)$/,  // 处理其它资源(如字体图等)
                loader: "file-loader",
                options: {
                    name: '[hash:10].[ext]]',
                    outputPath: 'media'
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    mode: 'development',
    devServer: {
        contentBase: resolve(__dirname,'build'),
        compress: true,
        port: 30000,
        open: true
    }

}

css文件单独提取

const {resolve} = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// mini-css-extract-plugin 拆分css文件独立
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: "js/built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // 创建一个style标签将样式放入
                    // 'style-loader',
                    // 这个loader的作用是取代style-loader来单独提取css文件
                    MiniCssExtractPlugin.loader,
                    // 将css文件整合到js文件中
                    'css-loader'
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        }),
        new MiniCssExtractPlugin({
            // 对输出的文件重命名 并创建文件夹
            filename: 'css/built.css'
        })
    ],
    mode: 'development'
}

css兼容性处理

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// mini-css-extract-plugin 拆分css文件独立
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 设置nodejs环境变量

// process.env.NODE_ENV = "development";

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: "js/built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    /*
                        css兼容性处理 postcss --> postcss-loader@3.0.0  postcss-preset-env
                          帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
                           "browserslist": {
                           // 开发环境 --》 设置node环境变量 : process.env.NODE_ENV = "development"
                                "development": [
                                  "last 1 chrome version",
                                  "last 1 firefox version",
                                  "last 1 safari version",
                                  "last 1  IE version"
                                ],
                                // 生产环境 : 默认是看生产环境
                                "production": [
                                  ">0.2%",
                                  "not dead",
                                  "not op_mini all"
                                ]
                              }
                    */
                    // 使用默认配置 loader  : 'postcss-loader'
                    // 修改loader配置
                    {
                        loader: "postcss-loader",
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require("postcss-preset-env")
                            ]
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.css'
        })
    ],
    mode: 'development'
}

css 压缩

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// mini-css-extract-plugin 拆分css文件独立
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 设置nodejs环境变量
// process.env.NODE_ENV = "development";
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
// optimize-css-assets-webpack-plugin  压缩css的插件

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: "js/built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        loader: "postcss-loader",
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require("postcss-preset-env")()
                            ]
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.css'
        }),
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'development'
}

js语法检测

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        /*
                   语法检测 : 统一代码样式  使用的插件 : eslint-loader  eslint
                       注意:只检测自己写的源代码,第三方的库是不用检测的
                       设置检测规则:
                            package.json中eslintConfig中设置
                            "eslintConfig" : {
                                "extends": "airbnb-base"
                            }
                            airbnb --》 eslint-config-airbnb-base  eslint  eslint-plugin-import
                */
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          // 自动修复错误
          fix: true,
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),

  ],
  mode: 'development',
};

js兼容性处理

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      /*
        js的兼容性处理 : babel-loader @babel/core  @babel/preset-env
          1. @babel/preset-env : 处理js基本兼容性问题
            问题:只能转换基本语法,如promise不能转换
          2. 全部js的兼容性处理 : @babel/polyfill
            问题: 只要解决部分兼容性问题,但是将所有的兼容性代码全部引入,体积太大
          3. 需要做兼容性处理的就做:按需加载 --》 core-js
      */
     {
       test: /\.js$/,
       loader: 'babel-loader',
       exclude: /node_modules/,
       options : {
        //  预设: 指示babel做怎么样的兼容性处理
        //  presets: ['@babel/preset-env']
         presets: [
               [
                 '@babel/preset-env',
                 {
                   // 按需加载
                   useBuiltIns: 'usage',
                   // 指定 corejs 的版本
                   corejs: {
                     version: 3
                   },
                   // 指定兼容性做到哪个版本的浏览器
                   targets : {
                      chrome : "60",
                      firefox: "50",
                      ie: "9",
                      safari: "10",
                      edge: "17",
                   }
                 }
               ]
         ]
       }
     }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),

  ],
  mode: 'development',
};

js & html压缩

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 压缩HTML配置
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true
      }
    }),
  ],
  mode: 'production', // 开启 production 自动压缩js
};

生产环境配置


const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.js',
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        // 在package.json 配置文件
                        loader: 'postcss-loader',
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require("postcss-preset-env")()
                            ]
                        }
                    }
                ]
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        // 在package.json 配置文件
                        loader: 'postcss-loader',
                        options: {
                            ident: 'postcss',
                            plugins: () => [
                                require("postcss-preset-env")()
                            ]
                        }
                    },
                    'less-loader'
                ]
            },
            /*
                正常讲:一个文件只能被一个loader处理
                    当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序 先执行 eslint 再 babel
            */
            {
                // 在package.json中配置eslintConfig --》 airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    presets: [
                        [
                           '@babel/preset-env',
                            {
                                useBuiltIns: 'usage',
                                corejs: {version: 3},
                                targets: {
                                    chrome: "60",
                                    ie: "8",
                                    firefox: "60"
                                }
                            }
                        ]
                    ]
                }
            },
            {
                test: /\.(jpg|png|gif)/,
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]',
                    esModule: false,
                    outputPath: 'imgs'
                }
            },
            {
                test: /\.html$/,
                loader: 'html-withimg-loader',
                options: {
                    outputPath: 'imgs'
                }
            },
            {
                exclude: /\.(js|css|less|html|jpg|png|gif)/,
                loader: 'file-loader',
                options: {
                    outputPath: 'media'
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.css'
        }),
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production'
}

HMR 热模块替换(更新修改文件)

/*
    HMR:hot module replacement 热模块替换 、 模块热替换 (修改部分更新)
        作用:一个模块发生变化。只会重新打包这一个模块(而不是打包所有的模块) 极大提升构建速度
        样式文件 : 可以使用HMR功能 : 因为style-loader内部实现了
        js文件 :默认不使用 HMR -> 需要修改 js 代码
            注意:HMR功能对 js 的处理,只能处理非入口文件的其他文件
        HTML文件 :默认不使用HMR功能。同时会导致问题: HTML不能热更新!(不需要做HMR功能)
            方案: 修改 entry 入口 将 HTML文件引入 (解决热更新)
*/
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    // target: process.env.NODE_ENV === 'development' ? 'web' : 'browserslist',
    target: 'web', // webpack5 bug 不能自动刷新 解决方案
    entry: ['./src/js/index.js', './src/index.html'],

    output: {
        filename: "js/built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,  // 处理css资源
                use: ['style-loader','css-loader']
            },
            {
                test: /\.less$/,   // 处理less资源
                use: ['style-loader','css-loader','less-loader']
            },
            {
                test: /\.(jpg|png|gif)/, // 处理图片资源
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]',
                    esModule: false,  // 关闭es6模块化的解析
                    outputPath: 'imgs'
                }
            },
            {
                test: /\.html$/,  // 处理HTML的img图片
                loader: 'html-withimg-loader',
            },
            {
                exclude: /\.(css|js|html|jpg|less|png|gif)$/,  // 处理其它资源(如字体图等)
                loader: "file-loader",
                options: {
                    name: '[hash:10].[ext]]',
                    outputPath: 'media'
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    mode: 'development',
    devServer: {
        contentBase: resolve(__dirname,'build'),
        compress: true,
        port: 12345,
        open: true,
        // 开启 HMR 功能  修改配置重启服务
        hot: true
    }
}

source-map 压缩后代码映射源代码


const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    // target: process.env.NODE_ENV === 'development' ? 'web' : 'browserslist',
    target: 'web', // webpack5 bug 不能自动刷新 解决方案
    entry: ['./src/js/index.js', './src/index.html'],

    output: {
        filename: "js/built.js",
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                test: /\.css$/,  // 处理css资源
                use: ['style-loader','css-loader']
            },
            {
                test: /\.less$/,   // 处理less资源
                use: ['style-loader','css-loader','less-loader']
            },
            {
                test: /\.(jpg|png|gif)/, // 处理图片资源
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]',
                    esModule: false,  // 关闭es6模块化的解析
                    outputPath: 'imgs'
                }
            },
            {
                test: /\.html$/,  // 处理HTML的img图片
                loader: 'html-withimg-loader',
            },
            {
                exclude: /\.(css|js|html|jpg|less|png|gif)$/,  // 处理其它资源(如字体图等)
                loader: "file-loader",
                options: {
                    name: '[hash:10].[ext]]',
                    outputPath: 'media'
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    mode: 'development',
    devServer: {
        contentBase: resolve(__dirname,'build'),
        compress: true,
        port: 12345,
        open: true,
        hot: true
    },
    devtool: 'eval-source-map'
}
/*
    source-map:一种提供源代码到构建后代码映射技术 (如果构建后代码出错了,通过映射可以追踪到源代码的错误)
        [inline-|hidden-|eval-] [nosources-] [cheap-[module]] source-map
        source-map : 外部
            错误代码的准确信息 和 源代码的错误位置
        inline-source-map : 内联
            1. 只生成一个 内联 source-map
             错误代码的准确信息 和 源代码的错误位置
        hidden-source-map : 外部  防泄露
             错误代码的错误原因 但是没有错误的位置 不能追踪源代码错误,只能提示到构建后代码的位置
        eval-source-map : 内部
            1. 每一个文件都生成对应的source-map ,都在 eval
             错误代码的准确信息 和 源代码的错误位置 hash值
        nosources-source-map : 外部   防泄漏
            错误代码的准确信息  但是没有任何源代码信息
        cheap-source-map : 外部
             错误代码的准确信息 和 源代码的错误位置
             只能精确到行
        cheap-module-source-map : 外部
              错误代码的准确信息 和 源代码的错误位置
              module会将loader和source-map加入
        内联 和 外部 的区别 :1. 外部生成文件,内联没有 2. 内联构建速度快

        开发环境: 速度快,调试更友好
            速度快(eval > inline > cheap)
            eval-cheap-source-map
            eval-source-map
            调试更友好 :source-map > cheap-module-source-map > cheap-source-map
            --> eval-source-map  /  eval-cheap-module-source-map
        生产环境 :源代码要不要隐藏?调试要不要更友好
            内联会让代码的体积变大 所以在生产环境不同内联
            nosources-source-map  全部隐藏
            hidden-source-map 只隐藏源代码,会提示构建后的代码错误

            --> source-map 、 cheap-module-source-map
*/

oneOf

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const CssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        // 在package.json 配置文件
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.js',
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                // 在package.json中配置eslintConfig --》 airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                // 一下loader只会匹配一次
                // 注意: 不能有两项配置处理同一种类型文件
                oneOf: [
                    {
                        test: /\.css$/,
                        use: [...CssLoader]
                    },
                    {
                        test: /\.less$/,
                        use: [   ...CssLoader, 'less-loader']
                    },
                    /*
                        正常讲:一个文件只能被一个loader处理
                            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序 先执行 eslint 再 babel
                    */
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        useBuiltIns: 'usage',
                                        corejs: {version: 3},
                                        targets: {
                                            chrome: "60",
                                            ie: "8",
                                            firefox: "60"
                                        }
                                    }
                                ]
                            ]
                        }
                    },
                    {
                        test: /\.(jpg|png|gif)/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]',
                            esModule: false,
                            outputPath: 'imgs'
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-withimg-loader',
                        options: {
                            outputPath: 'imgs'
                        }
                    },
                    {
                        exclude: /\.(js|css|less|html|jpg|png|gif)/,
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.css'
        }),
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production'
}

缓存babel

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
/*
    缓存:
        babel缓存
            cacheDirectory: true
            --> 让第二次打包构建速度更快
    文件资源缓存
        hash: 每次webpack构建时会生成一个唯一的hash值
            问题: 因为js和css同时使用一个hash值。如果重新打包会导致所有缓存失效。(可能只改动一个文件)
        chunkhash: 根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值一样
            问题: js 和 css 的hash值还是一样的
                因为css 是在 js 中被引入的,所以同属于一个chunk
        contenthash : 根据文件的内容生成hash值,不同的文件的hash值一定不一样
               --> 让代码上线运行缓存更好使用


 */

const CssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        // 在package.json 配置文件
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                // 在package.json中配置eslintConfig --》 airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                // 一下loader只会匹配一次
                // 注意: 不能有两项配置处理同一种类型文件
                oneOf: [
                    {
                        test: /\.css$/,
                        use: [...CssLoader]
                    },
                    {
                        test: /\.less$/,
                        use: [   ...CssLoader, 'less-loader']
                    },
                    /*
                        正常讲:一个文件只能被一个loader处理
                            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序 先执行 eslint 再 babel
                    */
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        useBuiltIns: 'usage',
                                        corejs: {version: 3},
                                        targets: {
                                            chrome: "60",
                                            ie: "8",
                                            firefox: "60"
                                        }
                                    }
                                ]
                            ],
                            // 开启 babel缓存  第二次构建是会去读取之前的缓存
                            cacheDirectory: true
                        }
                    },
                    {
                        test: /\.(jpg|png|gif)/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]',
                            esModule: false,
                            outputPath: 'imgs'
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-withimg-loader',
                        options: {
                            outputPath: 'imgs'
                        }
                    },
                    {
                        exclude: /\.(js|css|less|html|jpg|png|gif)/,
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.[contenthash:10].css'
        }),
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production',
    devtool: "source-map"
}

tree shaking

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
/*
    tree shaking : 去除无用代码
        前提: 1. 必须使用 ES6 模块化 2. 开启 production 环境
        作用: 减小代码的体积
        在package.json中配置 
            "sideEffects" : false 所有的代码都没有副作用(都可以进行 tree shaking)
             问题:可能会把 css 、 @babel/polyfill (副作用)文件干掉
 */
const CssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        // 在package.json 配置文件
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                // 在package.json中配置eslintConfig --》 airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                // 一下loader只会匹配一次
                // 注意: 不能有两项配置处理同一种类型文件
                oneOf: [
                    {
                        test: /\.css$/,
                        use: [...CssLoader]
                    },
                    {
                        test: /\.less$/,
                        use: [   ...CssLoader, 'less-loader']
                    },
                    /*
                        正常讲:一个文件只能被一个loader处理
                            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序 先执行 eslint 再 babel
                    */
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        useBuiltIns: 'usage',
                                        corejs: {version: 3},
                                        targets: {
                                            chrome: "60",
                                            ie: "8",
                                            firefox: "60"
                                        }
                                    }
                                ]
                            ],
                            // 开启 babel缓存  第二次构建是会去读取之前的缓存
                            cacheDirectory: true
                        }
                    },
                    {
                        test: /\.(jpg|png|gif)/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]',
                            esModule: false,
                            outputPath: 'imgs'
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-withimg-loader',
                        options: {
                            outputPath: 'imgs'
                        }
                    },
                    {
                        exclude: /\.(js|css|less|html|jpg|png|gif)/,
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.[contenthash:10].css'
        }),
        new OptimizeCssAssetsWebpackPlugin()
    ],
    mode: 'production',
    devtool: "source-map"
}

code split 代码分隔

demo1

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    // entry : './src/js/index.js'  // 单入口文件
    entry: {
        // 多入口: 有一个入口,最终输出就有一个 budle
        index : './src/js/index.js',
        test: './src/js/test.js'
    },
    output: {
        // 取文件名 [name] 
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    mode: 'production',
}

demo2

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    // entry : './src/js/index.js',  // 单入口文件
    entry: {
        index: './src/js/index.js',
        test: './src/js/test.js'
    },
    output: {
        // 取文件名 [name] 
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    /* 
        1. 可以将node_modules中的代码单独打包一个chunk最终输出
        2. 自动分析多入口chunk中。有没有公共的文件。如有就会打包成一个单独的chunk
        
    */
    optimization: {
        splitChunks: {
            chunks : "all"
        }
    },
    mode: 'production',
}

demo3

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    entry : './src/js/index.js',  // 单入口文件
    output: {
        // 取文件名 [name] 
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    /* 
        1. 可以将node_modules中的代码单独打包一个chunk最终输出
        2. 自动分析多入口chunk中。有没有公共的文件。如有就会打包成一个单独的chunk
        
    */
    optimization: {
        splitChunks: {
            chunks : "all"
        }
    },
    mode: 'production',
}

lazy loding 懒加载

webpack.config.js:

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    entry : './src/js/index.js',  // 单入口文件
    output: {
        // 取文件名 [name] 
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    /* 
        1. 可以将node_modules中的代码单独打包一个chunk最终输出
        2. 自动分析多入口chunk中。有没有公共的文件。如有就会打包成一个单独的chunk
        
    */
    optimization: {
        splitChunks: {
            chunks : "all"
        }
    },
    mode: 'production',
}

js:

console.log("index.js被加载了!");

// import {mul} from "./test"


document.getElementById('btn').onclick = function() { //按钮点击触发
  // 懒加载~:当文件需要使用时才加载~
  // 预加载 prefetch:会在使用之前,提前加载js文件 
  // 正常加载可以认为是并行加载(同一时间加载多个文件)  
  // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  import(/* webpackChunkName: 'test', webpackPrefetch: true*/'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

pwa离线加载

webpack.config.js:

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
/*
    PWA: 渐进式网络开发应用程序(离线可访问)
        workbox --> workbox-webpack-plugin
 */
const CssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        // 在package.json 配置文件
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                // 在package.json中配置eslintConfig --》 airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                // 一下loader只会匹配一次
                // 注意: 不能有两项配置处理同一种类型文件
                oneOf: [
                    {
                        test: /\.css$/,
                        use: [...CssLoader]
                    },
                    {
                        test: /\.less$/,
                        use: [   ...CssLoader, 'less-loader']
                    },
                    /*
                        正常讲:一个文件只能被一个loader处理
                            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序 先执行 eslint 再 babel
                    */
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        useBuiltIns: 'usage',
                                        corejs: {version: 3},
                                        targets: {
                                            chrome: "60",
                                            ie: "8",
                                            firefox: "60"
                                        }
                                    }
                                ]
                            ],
                            // 开启 babel缓存  第二次构建是会去读取之前的缓存
                            cacheDirectory: true
                        }
                    },
                    {
                        test: /\.(jpg|png|gif)/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]',
                            esModule: false,
                            outputPath: 'imgs'
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-withimg-loader',
                        options: {
                            outputPath: 'imgs'
                        }
                    },
                    {
                        exclude: /\.(js|css|less|html|jpg|png|gif)/,
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.[contenthash:10].css'
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new WorkboxWebpackPlugin.GenerateSW({
            /* 
                1. 帮助 serviceworker 快速启动
                2. 删除旧的 serviceworker
                生成一个serviceworker 配置文件
            */
            clientsClaim: true,
            skipWaiting: true
        }) 
    ],
    mode: 'production',
    devtool: "source-map"
}

js:

import '../css/box.css';
import '../css/index.css';
import '../css/iconfont.css';
import { mul } from './test';

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}
/*
  1. eslint 不认识 window ,navigation 全局变量
    解决: 需要修改package.json 中的 eslintConfig配置
      "env": {
        "browser": true  // 支持浏览器端全局变量 node:true
      }
  2. sw 代码必须运行在服务器上
      --> nodejs
      --> npm i serve -g
      serve -s build 启动服务器,将build目录下的所有的资源作为静态资源暴露出去
*/
// eslint-disable-next-line
console.log(mul(2,3));

// eslint-disable-next-line
console.log(sum(1,2,3,4));

// 注册 serviceWorker  处理兼容问题
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js').then(() => {
      console.log('sw注册成功~');
    }).catch(() => {
      console.log('sw注册失败了~');
    });
  });
}

多线程

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
/*
    PWA: 渐进式网络开发应用程序(离线可访问)
        workbox --> workbox-webpack-plugin
 */
/* 
    thread-loader 
*/
const CssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        // 在package.json 配置文件
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                require("postcss-preset-env")()
            ]
        }
    }
]
module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname,'build')
    },
    module: {
        rules: [
            {
                // 在package.json中配置eslintConfig --》 airbnb
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                // 一下loader只会匹配一次
                // 注意: 不能有两项配置处理同一种类型文件
                oneOf: [
                    {
                        test: /\.css$/,
                        use: [...CssLoader]
                    },
                    {
                        test: /\.less$/,
                        use: [ ...CssLoader, 'less-loader']
                    },
                    /*
                        正常讲:一个文件只能被一个loader处理
                            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序 先执行 eslint 再 babel
                    */
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        use: [
                            /* 
                                开启多线程打包
                                进程启动大概为600ms,进程通信也有开销。只有工作消耗时间较长,才需要多进程打包
                            */
                            {
                                loader : 'thread-loader',
                                options : {
                                    Workers : 2 // 进程 2 个
                                }
                            },
                            {
                                loader: 'babel-loader',
                                options: {
                                    presets: [
                                        [
                                            '@babel/preset-env',
                                            {
                                                useBuiltIns: 'usage',
                                                corejs: {version: 3},
                                                targets: {
                                                    chrome: "60",
                                                    ie: "8",
                                                    firefox: "60"
                                                }
                                            }
                                        ]
                                    ],
                                    // 开启 babel缓存  第二次构建是会去读取之前的缓存
                                    cacheDirectory: true
                                 }
                            }
                        ]
                        
                    },
                    {
                        test: /\.(jpg|png|gif)/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:10].[ext]',
                            esModule: false,
                            outputPath: 'imgs'
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-withimg-loader',
                        options: {
                            outputPath: 'imgs'
                        }
                    },
                    {
                        exclude: /\.(js|css|less|html|jpg|png|gif)/,
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.[contenthash:10].css'
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new WorkboxWebpackPlugin.GenerateSW({
            /* 
                1. 帮助 serviceworker 快速启动
                2. 删除旧的 serviceworker
                生成一个serviceworker 配置文件
            */
            clientsClaim: true,
            skipWaiting: true
        }) 
    ],
    mode: 'production',
    devtool: "source-map"
}