Skip to content

webpack

https://xxpromise.gitee.io/webpack5-docs/

基本开发配置

含义:

webpack是一个静态资源打包工具

-- 以一个或多个文件为打包入口,将我们整个项目的文件编译组合成一个或者多个文件发布出去

我们将webpack打包好的文件叫bundel

功能介绍

webpack本身功能有限,开发模式只能编译js的ES Module语法, 生产模式也只是能压缩下

基本配置

五大核心模块

  1. entry --- 指示入口文件
    1. 单个入口文件 entry: './src/main.js'
    2. 多入口文件 entry:
  2. output-- 指示出口命名以及地址
    1. 单个出口 output:
    2. 多个出口 output:
  3. loader --- 加载机 使webpack具有解析其他资源的能力
    1. module:
  4. plugins --- 插件 拓展webpack功能
    1. plugins: [] -------- 数组
  5. mode ---模式 development production

开始使用

  1. 安装
    1. npm i webpack webpack-cli -g 全局装全局用
    2. 局部装 npm i webpack webpack-cli -D 局部装 局部使用指令
      1. npx webpack 入口文件地址 --mode=development (npx 作用就是找node_modules下的bin目录下的指令去操作)
  2. npm init -y 生成package.json文件
  3. 根目录下创建webpack.config.js 去进行基础的配置
  4. 配置完毕 执行指令 全局执行 webpack -w 局部 npx webpack
  5. 处理css资源 less资源 scss资源 npm i css-loader less-loader sass-loader sass node-sass(被sass-loader依赖) -D
  6. 处理图片资源 webpack5版本已经内置 file-loader url-loader 直接配置无需安装
  7. 处理字体图标 视频音频文件
  8. 处理js资源文件
    1. Eslint处理代码规范
      1. ----下
    2. babel处理兼容性
javascript
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: {
        'main':'./src/main.js'
    },
    output: {
        path: path.join(process.cwd(), './myDist'),
        //  所有文件打包之后的目录     
        filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
        // 我们想让入口文件打包的js资源单独出来  ‘js/main.js’  
        // filename: 'main.js'

        // 自动清空上次打包内容
        clean: true
    },
    module: {
        // loader配置
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader','css-loader']
            },
            {
                test: /\.less$/,
                use: ['style-loader','css-loader','less-loader']
            },
            {
                test: /\.s[ca]ss$/,
                use: ['style-loader','css-loader','sass-loader']
            },
            // 图片加载机
            {
                test: /\.(png|jpg|jpeg|gif|webp)$/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 4 * 1024 // 4kb   小于4kb的base64编码   注意图片打包后不会把之前打包的删除   所以要手动删除
                    }
                },
                generator: {
                    // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                    filename: 'static/img/[hash: 6][ext][query]'
                }

            },
            // 字体图标   音视频
            {
                test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
                type: "asset/resource",
                generator: {
                      // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                      filename: 'static/media/[hash: 6][ext][query]'
                  }

            },
            //// babel 使用
            {
                test: /\.m?js$/,
                exclude: /node_modules/,
                
                loader: 'babel-loader',
                options: {
                    // 表示排除编译的文件   如果只有一个  直接是字符串
                    // exclude: /node_modules/
                    // exclude: [],  // 排除多个
                    presets: ['@babel/preset-env']
                  }
                
              }
            
          

        ]
    },
    plugins: [
        // 插件配置
        // eslint插件
        new EslintPlugin({
            context: path.join(process.cwd(), './src')   // 表示从哪里开始检查  ,一般是入口文件
        }),
        new HtmlWebpackPlugin({
          // 以那个文件为模板进行打包
          //  新的html文件结构与原来一致  还会自动引入打包输出的文件
          template: path.join(process.cwd(), './public/index.html')
        })

    ],
    mode: 'development'
}

Eslint

配置文件

  1. .eslintrc.* 创建文件进行配置 1. .eslintrc 2. .eslintrc.js 3. .eslintrc.json

区别在于配置格式不同

  1. package.json中 eslintConfig 不需要创建文件进行配置

具体配置

.eslintrc.js 方式

javascript
module.exports = {
  extends: ["react-app"],  // 直接继承react官网规则  
  parserOptions: {
    ecmaVersion: 6,  //es语法版本
    sourceType: "module",  // es模块化
    ecmaFeatures: {       // es其他特性
      jsx: true    // react项目要开启jsx语法
    }
  },
  rules: {
    "no-var": 2  // 有var直接error
  },
  
  
  // https://eslint.bootcss.com/docs/user-guide/configuring
  // 规则文档 *  
    // https://eslint.bootcss.com/docs/rules/
}
  1. parseOptions 解析选项
    1. https://eslint.bootcss.com/docs/user-guide/configuring
  2. rules 配置规则 需要的时候直接去配置规则中寻找
    1. "off"或者 0 关闭规则
    2. "warn" 或者 1 开启规则 --- 使用警告级别错误
    3. "error"或者 2 开启规则 ---- 出现错误 中断运行
    4. https://eslint.bootcss.com/docs/rules/
  3. extends 继承 --- 开发中写规则太费劲 可以直接继承现有的规则
    1. Eslint官方规则 eslint:recommended
    2. Vue cli 官方规则 plugin: vue/essential
    3. React cli 官方规则 react-app
    4. **** 注意我们写的规则会覆盖继承的规则

webpack使用eslint

  1. 根目录下创建 .eslintrc.js 配置eslint (可以安装个eslint插件看)
  2. npm i eslint-webpack-plugin eslint -S
  3. 在webpack.config.js中
    1. 引入插件 const EslintPlugin = require('eslint-webpack-plugin')
    2. 在webpack配置项plugins: []中 实例化插件 plugins: [new EslintPlugin(option),
  4. 如果安装了eslint插件,打包之后,会发现打包后的文件会报错,而我们不希望检查打包后的文件 所以
    1. 新建 .eslintignore
    2. 直接写 dist 表示这个目录不检查

Babel

配置文件 搞一个就行

  1. babel.config.*
    1. babel.config.js
    2. babel.config.json
  2. .babelrc.*
    1. .babelrc
    2. .babelrc.js
    3. .babelrc.json
  3. package.json中的babel

具体配置 babel.config.js例子

javascript
module.export = {
  // 预设
  presets: ['@babel/preset-env', ]
}
  1. presets 预设
    1. @babel/preset-env 允许使用最新js
    2. @babel/preset-react 编译jsx
    3. @babel/preset-typescript 编译ts

在webpack使用

https://webpack.docschina.org/loaders/babel-loader/

  1. -D babel-loader @babel/core @babel/preset-env
  2. loader方式添加
javascript
//// babel 使用
            {
                test: /\.js$/,
                exclude: /node_modules/,
                
                loader: 'babel-loader',
                 //  options可以写babel.config.js中
                options: {
                  // 表示排除编译的文件   如果只有一个  直接是字符串
                  // exclude: /node_modules/
                  // exclude: [],  // 排除多个
                  presets: ['@babel/preset-env']
                }
                }
              }
  1. options配置可以写在外边
    1. 新建babel.config.js
    2. 在这个文件中配置就可以 方便之后修改

处理html资源

我们现在js css资源是手动引入的 将来可能会发生变化 所以我们需要自动

  1. npm i html-webpack-plugin -D
  2. webpack.config.js中
    1. const HtmlWebpackPlugin = require('html-webpack-plugin')
    2. 在plugin中new HtmlWebpackPlugin ({})下就行 具体看配置对象 有 template等属性

开发服务器&自动化

目的: 每次开发完都要手动输入指令才能编译,我们需要自动化

使用: webapck -w 应该也行

  1. npm i webpack-dev-server -D
  2. webpack.config.js中进行配置
  3. 启动指令webpack serve 局部 npx webpack serve
javascript
devServer: {
  host: 'localhost',
  port: '3000',
  open: true
}

基础配置

javascript
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: {
        'main':'./src/main.js'
    },
    output: {
        path: path.join(process.cwd(), './myDist'),
        //  所有文件打包之后的目录     
        filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
        // 我们想让入口文件打包的js资源单独出来  ‘js/main.js’  
        // filename: 'main.js'

        // 自动清空上次打包内容
        clean: true
    },
    module: {
        // loader配置
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader','css-loader']
            },
            {
                test: /\.less$/,
                use: ['style-loader','css-loader','less-loader']
            },
            {
                test: /\.s[ca]ss$/,
                use: ['style-loader','css-loader','sass-loader']
            },
            // 图片加载机
            {
                test: /\.(png|jpg|jpeg|gif|webp)$/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 4 * 1024 // 4kb   小于4kb的base64编码   注意图片打包后不会把之前打包的删除   所以要手动删除
                    }
                },
                generator: {
                    // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                    filename: 'static/img/[hash: 6][ext][query]'
                }

            },
            // 字体图标   音视频
            {
                test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
                type: "asset/resource",
                generator: {
                      // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                      filename: 'static/media/[hash: 6][ext][query]'
                  }

            },
            //// babel 使用
            {
                test: /\.js$/,
                exclude: /node_modules/,
                
                loader: 'babel-loader',
                // 表示排除编译的文件   如果只有一个  直接是字符串
                // exclude: /node_modules/
                options: {
                    // 表示排除编译的文件   如果只有一个  直接是字符串
                    // exclude: /node_modules/
                    // exclude: [],  // 排除多个
                    presets: ['@babel/preset-env']
                }
                
                
          }
            
          

        ]
    },
    plugins: [
        // 插件配置
        // eslint插件
        new EslintPlugin({
            context: path.join(process.cwd(), './src')   // 表示从哪里开始检查  ,一般是入口文件
        }),
        new HtmlWebpackPlugin({
          // 以那个文件为模板进行打包
          //  新的html文件结构与原来一致  还会自动引入打包输出的文件
          template: path.join(process.cwd(), './public/index.html')
        })

    ],
     // 配置开发服务器
    devServer: {
      host: 'localhost',
      port: '3000',
      open: true
    },
    mode: 'development'
}

基本生产配置

把开发生产模式分开

  1. 根目录创建config目录
  2. config目录包含webpack.dev.js webpack.prod.js
  3. 把之前的开发配置复制到dev,并进行修改
javascript
// webpack.dev.js
// 我们运行指令还是在根目录运行
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: {
        'main':'./src/main.js'
    },
    output: {
        // path: path.join(process.cwd(), './myDist'),
        // 因为开发不需要输出
        path: undefined,
        //  所有文件打包之后的目录     
        filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
        // 我们想让入口文件打包的js资源单独出来  ‘js/main.js’  
        // filename: 'main.js'

        // 自动清空上次打包内容
        //clean: true
    },
    module: {
        // loader配置
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader','css-loader']
            },
            {
                test: /\.less$/,
                use: ['style-loader','css-loader','less-loader']
            },
            {
                test: /\.s[ca]ss$/,
                use: ['style-loader','css-loader','sass-loader']
            },
            // 图片加载机
            {
                test: /\.(png|jpg|jpeg|gif|webp)$/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 4 * 1024 // 4kb   小于4kb的base64编码   注意图片打包后不会把之前打包的删除   所以要手动删除
                    }
                },
                generator: {
                    // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                    filename: 'static/img/[hash: 6][ext][query]'
                }

            },
            // 字体图标   音视频
            {
                test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
                type: "asset/resource",
                generator: {
                      // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                      filename: 'static/media/[hash: 6][ext][query]'
                  }

            },
            //// babel 使用
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                
                loader: 'babel-loader',
                // 表示排除编译的文件   如果只有一个  直接是字符串
                // exclude: /node_modules/
                //exclude: [],  // 排除多个
                presets: ['@babel/preset-env']
                  
                
              }
            
          

        ]
    },
    plugins: [
        // 插件配置
        // eslint插件
        new EslintPlugin({
            context: path.join(process.cwd(), './src')   // 表示从哪里开始检查  ,一般是入口文件
        }),
        new HtmlWebpackPlugin({
          // 以那个文件为模板进行打包
          //  新的html文件结构与原来一致  还会自动引入打包输出的文件
          template: path.join(process.cwd(), './public/index.html')
        })

    ],
     // 配置开发服务器
    devServer: {
      host: 'localhost',
      port: '3000',
      open: true
    },
    mode: 'development'
}
  1. 修改之后,想运行全局开发指令 webpack serve --config ./config/webpack.dev.js,局部 npx webpack serve --config ./config/webpack.dev.js
  2. 配置生产模式下的配置
javascript
// webpack.prod.js
// 我们运行指令还是在根目录运行
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
//  处理html 自动引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 提取css为单独文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 压缩css
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
    entry: {
        'main':'./src/main.js'
    },
    output: {
        path: path.join(process.cwd(), './myDist'),
        //  所有文件打包之后的目录     
        filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
        // 我们想让入口文件打包的js资源单独出来  ‘js/main.js’  
        // filename: 'main.js'

        // 自动清空上次打包内容
        clean: true
    },
    module: {
        // loader配置
        rules: [
            {   // MiniCssExtractPlugin.loader提取css为单独的文件
                test: /\.css$/,
                use: [
                  MiniCssExtractPlugin.loader,
                  'css-loader',
                  {
                    loader: 'postcss-loader',
                      options: {
                        postcssOptions: {
                          plugins: [
                            "postcss-preset-env",  //可以解决大多样式问题
                          ]
                        }
                      }
                  }	
                  
                ]
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader,'css-loader',
                      {
                        loader: 'postcss-loader',
                          options: {
                            postcssOptions: {
                              plugins: [
                                "postcss-preset-env",  //可以解决大多说样式问题
                              ]
                            }
                          }
                      },	
                      
                      'less-loader']
            },
            {
                test: /\.s[ca]ss$/,
                use: [MiniCssExtractPlugin.loader,'css-loader',
                      {
                        loader: 'postcss-loader',
                          options: {
                            postcssOptions: {
                              plugins: [
                                "postcss-preset-env",  //可以解决大多说样式问题
                              ]
                            }
                          }
                      },	
                      'sass-loader']
            },
            // 图片加载机
            {
                test: /\.(png|jpg|jpeg|gif|webp)$/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 4 * 1024 // 4kb   小于4kb的base64编码   注意图片打包后不会把之前打包的删除   所以要手动删除
                    }
                },
                generator: {
                    // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                    filename: 'static/img/[hash: 6][ext][query]'
                }

            },
            // 字体图标   音视频
            {
                test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
                type: "asset/resource",
                generator: {
                      // 定义图片资源打包输出目录  相对于output的path   hash:6  取前六位
                      filename: 'static/media/[hash: 6][ext][query]'
                  }

            },
            //// babel 使用
            {
                test: /\.m?js$/,
                exclude: /node_modules/,
               
                loader: 'babel-loader',
                options: {
                  // 表示排除编译的文件   如果只有一个  直接是字符串
                  // exclude: /node_modules/
                  //exclude: [],  // 排除多个
                  presets: ['@babel/preset-env']
                }
                
              }
            
          

        ]
    },
    plugins: [
        // 插件配置
        // eslint插件
        new EslintPlugin({
            context: path.join(process.cwd(), './src')   // 表示从哪里开始检查  ,一般是入口文件
        }),
        new HtmlWebpackPlugin({
          // 以那个文件为模板进行打包
          //  新的html文件结构与原来一致  还会自动引入打包输出的文件
          template: path.join(process.cwd(), './public/index.html')
        }),
          // 拆分css
        new MiniCssExtractPlugin({
          filename: 'static/css/main.css'   // 打包到的位置
        }),
          // 压缩css
        new new CssMinimizerPlugin()

    ],
     // 配置开发服务器
    devServer: {
      host: 'localhost',
      port: '3000',
      open: true
    },
    
    mode: 'production'
}
  1. 生产指令与开发指令类似
  2. 因为指令长,我们可以在package.json进行配置
javascript
”script“: {
  "start": "npm run dev",
  "dev": "webpack serve --config ./config/webpack.dev.js",
  "prod": "webpack serve --config ./config/webpack.prod.js"
}

css文件处理

提取css文件为单独文件

因为目前css被打包到js中,js加载时,会创建一个style标签生成样式,对于网站来说,会有闪屏现象,我们倾向于link引入单独文件

  1. npm i mini-css-extract-plugin -D
  2. 在生产配置中配置
    1. 引入const MiniCssExtractPlugin = require('mini-css-extract-plugin')
    2. plugins: [new MiniCssExtractPlugin()]
    3. 每个样式loader中,把之前的style-loader修改成 use: [MiniCssExtractPlugin.loader, "css-loader"]

样式兼容性处理

  1. npm i postcss-loader postcss postcss-preset-env -D
  2. 在生产配置中配置
    1. 在每个css-loader后边进行配置 --- 位置不能变 必须在less或者scssloader之前
    2. 加上
javascript
{
  loader: 'postcss-loader',
    options: {
      postcssOptions: {
        plugins: [
          "postcss-preset-env",  //可以解决大多说样式问题
        ]
      }
    }
}
  1. package.json中增加配置,告诉这个loader要做兼容性做到什么程度
    1. "browserslist": [ "last 2 version", "> 1%", "not dead" ] 实际开发这么写 /////// ["ie >= 8"] 不常用 除非有特殊实际需求
    2.                             所有浏览器          覆盖99%     不是一些已经挂了的版本   ---  交集
      

css压缩

https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/

  1. npm i css-minimizer-webpack-plugin -D
  2. 在生产配置中配置
    1. 引入 const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
    2. plugins:[new CssMinimizerPlugin ()]

html与js文件压缩

mode改成生产模式就默认压缩了

高级优化

介绍

  1. 提升开发体验(SourceMap HMR )
  2. 提升打包构建速度(oneOf Include/Exclude Eslint Babel的缓存 cache 多进程优化 )
  3. 减少代码体积(Tree Shaking Babel辅助文件引用 图片压缩image Minimizer)
  4. 优化代码运行性能

SourceMap -- 优化报错

为什么?

开发时,运行的代码是编译后的,如果出现错误,不知道哪里出错,所以出错时需要告诉我们在源代码哪里出错,哪一行那一列

是什么?

SourceMap -- 源代码映射 是一个用来生成源代码与构建后代码一一映射的文件的方案

它会生成一个***.map文件, 里面包含源代码和构建后代码每一行,每一列的映射关系,当代码构建出错了,会通过这个map文件,从构建后代码出错的位置找到映射后源代码出错的位置,从而让浏览器提示源代码出错位置,帮助我们找到错误根源

怎么用?https://webpack.docschina.org/configuration/devtool/

开发模式配置文件中 devtool: "cheap-module-source-map" 只包含行

生产模式配置文件中 devtool: "source-map" 包含行和列 因此打包编译慢一点

提升代码构建速度

HMR -- HotModuleReplacement

为什么?

开发中,修改一点就需要重新打包,如果项目很大,那么修改一次就要打包好几分钟,所以我们需要提升打包构建速度

是什么?

HotModuleReplacement -- 热模块替换 就是程序运行中,替换 添加 删除模块, 无需重新加载整个页面

怎么用?

只需要在开发模式使用热模块替换

devServer: {

 hot: true   //  就行  默认就是开的  

}

注意 热模块替换开启后,只是说除了js之外的能实现,如果是js变化,还是会重新加载整个页面

开启js热模块需要我们在入口文件最下边写代码

javascript
if(module.hot) {
  module.hot.accept('./js/count.js')  // 让这个js模块开启
  module.hot.accept('./js/sum.js', () => {
    热加载时执行这个回调
  })  // 让这个js模块开启
}

oneOf

--- 就类似遍历的时候你已经得到了结果,不需要继续遍历 这里 oneOf就是这样的效果,如果这个文件已经被一个loader处理了,就不需要继续看下边的了

使用:开发 生产模式都可以

把所有loader拿出来,改造rules

javascript
rules: [
  {
    //  每个文件只能被一个loader处理   提升性能
    oneOf: [
      loader....   那些loader
      
    ]
  }
]

Include/Exclude --- 只针对js文件

我们需要使用一些三方库或者插件,所有的文件都在node_modeuls 这些文件不需要编译打包,所以有这俩去处理, js文件只有babel和eslint在干活 所以只处理babel eslint

include: 包含 只处理xxx文件
exclude: 排除 除了xxx之外的都处理

实例: ------

在babel-loader时, 还有语法检查new EslintPlugin({})时也要写 --- 看最后配置(开发 生产都需要)

Eslint Babel的缓存 cache

因为每次打包js文件时,都要经过Eslint检查和babel编译,所以速度比较慢

我们可以缓存之前的Eslint检查和babel编译结果,这样二次打包速度就快了

babel 处理

在babel-loader配置项options: {}中增加属性 (开发模式,生产模式都可以配置),打包后缓存生成在node_modules下的.cache中

javascript
options: {
  // 表示排除编译的文件   如果只有一个  直接是字符串
  // exclude: /node_modules/
  //exclude: [],  // 排除多个
  presets: ['@babel/preset-env'],
  cacheDirectory: true, // 开启babel缓存
  cacheCompression: false // 关闭缓存压缩, 因为我们为了速度,压缩缓存也需要时间
}
eslint处理
javascript
new EslintPlugin({
    context: path.join(process.cwd(), './src'),   // 表示从哪里开始检查  ,一般是入口文件
    exclude: 'node_modules',
    cache: true,  //  开启缓存
    cacheLocation: path.join(process.cwd(), './node_modules/.cache/eslint_cache')// 定义缓存存储路径
}),

多进程打包

当项目越来越庞大,打包速度越来越慢,虽然有了一些优化,但是随着项目的增大,还是越来越慢,所以我们还需要提升js打包速度,因为一个项目中大部分都是js,js的话主要就是eslint babel terser三个工具,所以我们要提升他们的运行速度,所以我们可以多进程打包,比单进程块

多进程打包:就是指的电脑多个进程同时干一件事,

使用:

  1. 引入os模块,获取cpu核心数
    1. const os = require('os')
    2. const threads = os.cpus().length // threads cpu核心数
  2. 下载包 npm i thread-loader -D
  3. 改造babel-loader, 我们一定要把thread-loader放在要处理的babel-loader之前
  4. 改造eslint 直接写threads就可以
  5. 引入插件terser-webpack-plugin (内置的无需下载) const TerserWebpackPlugin = require('terser-webpack-plugin')
javascript
  //  处理bable-loader  多进程编译
{
  test: /\.js$/,
  exclude: /node_modules/,

  use: [
    {
      loader: 'thread-loader',
      options: {
        works: threads  // 进程数
      }
    },
    {
      loader: 'babel-loader',
      options: {
        // 表示排除编译的文件   如果只有一个  直接是字符串
        // exclude: /node_modules/
        //exclude: [],  // 排除多个
        presets: ['@babel/preset-env']
      }
    }
  ]

}

// 处理eslint   多进程语法校验

new EslintPlugin({
  ...
  threads   // 开启多进程
})

// 处理压缩 ---   多进程压缩
// 在插件部分
new TerserWebpackPlugin({
  parallel: threads
})

减少代码体积

Tree Shaking

-- 移除js中没有使用上的代码

webpack已经默认开启,不需要任何操作

Babel

文件因为babel为编译的都插入了辅助代码,使得代码体积过大,我们能将这些辅助代码作为一个独立模块,避免重复引入

@babel/plugin-transform-runtime: 禁用babel自动对每个文件的runtime注入,改成引入, 并且使所有辅助代码从这里引用

使用:

  1. npm i @babel/plugin-transform-runtime -D
  2. 配置 开发 生产都可以
javascript
{
  loader: 'babel-loader',
  options: {
    // 表示排除编译的文件   如果只有一个  直接是字符串
    // exclude: /node_modules/
    //exclude: [],  // 排除多个
    presets: ['@babel/preset-env'],
    plugins: ['@babel/plugin-transform-runtime']  // 减少代码体积
  }
}

图片压缩 Image Minimizer

如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢,我们可以对图片进行压缩,减少图片体积

注意:如果项目中图片都是在线连接,不需要进行处理,本地项目静态图片才需要进行压缩

image-minimizer-webpack-plugin: 压缩图片的插件

使用:---- 生产模式下配置就行

  1. npm i image-minimizer-webpack-plugin imagemin -D
  2. 下载剩余包,有两种模式
    1. 无损压缩npm i imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
    2. 有损压缩npm i imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
  3. 以无损为例 配置 有损配置不同
javascript
// 压缩图片   放在压缩的位置
            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",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
  1. 打包发现抱错,可能会有两个程序没有下载下载来,所以我们需要手动把程序放在指定位置
    1. jpegtran.exe 把它放在node_modules/jpegtran-bin/ventor目录下 http://jpegclub.org/jpegtran/
    2. optipng.exe 把他放在node_modules/optipng-bin/vendor目录下 http://optipng.sourceforge.net/
  2. 之后进行打包就行了

优化代码运行性能

Code Split 生产模式

打包js时会将所有js文件打包到一个文件中,体积太大,如果我们只需要渲染首页,就应该只加载首页js文件,其他文件不应该加载,所以我们把打包生成的代码进行分割,生成多个js文件,渲染那个页面就只加载某个js文件,这样加载资源就少,速度就快

代码分割:

  1. 分割文件: 将打包后的文件进分割,生成多个js文件
  2. 按需加载: 需要那个文件就加载那个
使用方式1 --- 多入口
使用方式2 --- 单入口

npm install eslint-plugin-import -D ----- 让eslint支持import

  1. 直接把配置放在optimization中, 如果我们将来用到了node_modules中的会把它单独打包成一个文件

如果用到了动态导入语法 也会单独打包成一个文件

javascript
// 代码分割配置
    splitChunks: {
      chunks: "all", // 对所有模块都进行分割
  },
// 如果我们将来用到了node_modules中的会把它单独打包成一个文件
// 如果用到了动态导入语法  也会单独打包成一个文件  import(/* webpackChunkName: "count" */'./count.js').then().catch()
    // 动态导入语法默认eslint不能识别   所以需要配置  npm install eslint-plugin-import -D   -----  让eslint支持import
  1. .eslintrc.js中新增 plugins: ["import"] 支持动态导入
  2. 动态导入 - 单独打包出的文件配置名字
    1. webpack魔法命名 直接在动态导入的地方通过注释的方式命名 import(/* webpackChunkName: "count" */'./count.js').then().catch()
    2. 在output配置项中添加配置 chunkFilename: 'static/js/[name].chunk.js'
  3. 统一命名配置
    1. 把output中主文件名用[name].js兼容
    2. 把output中其他文件名[name].chunk.js 进行区分
    3. 把用asset处理的资源统一命名
      1. 把图片和字体图标等用asset处理的资源的filename注释
      2. output中新增assetModuleFilename: "static/media/[hash:10][ext][query]"
    4. 把拆分css插件配置filename导出文件名进行适配
javascript
    newMiniCssExtractPlugin({
        // 适配将来的多入口
        filename: 'static/css/[name].css',
        // 动态导入了css
        chunkFilename: 'static/css/[name].chunk.css'
    }),

preload/prefetch

当我们使用动态导入时,比如我们点击之后才加载这个资源,如果此时加载的资源很大,那么会有明显的卡顿,所以我们想在浏览器空闲时间加载后续需要使用的资源,我们就需要preload prefetch技术

preload: 告诉浏览器立即加载资源

prefetch: 告诉浏览器在空闲时才开始加载资源

共同点: 都只是加载资源,并不执行,都有缓存

不同:preload: 加载优先级高,并且只能加载当前页面用到的资源

prefetch:加载优先级低,但是可以预加载下个页面资源 兼容性较差

https://caniuse.com/ 查询兼容性问题网站

使用:

  1. npm i @vue/preload-webpack-plugin -D

  2. 资源预加载 防止按需引入卡顿const PreloadWebpackPlugin=require('@vue/preload-webpack-plugin')

  3. js 按需引入资源预加载 在plugins:[]配置

    newPreloadWebpackPlugin({
    
        rel: 'preload',
    
        as: 'script'
    
        // rel: 'prefetch'
    

{)

缓存优化 runtime

代码分割可能会导致缓存失效,所以要runtime,因为我们的main.js文件依赖count.js文件,所以当我们按需引入的count文件变化时,main也会变化,不会使用缓存,原因是count的hash值的变化,所以我们想到一个办法,把这个hash值存在另一个文件,main中每次去另一个文件runtime拿hash,这样main就可以使用缓存

-- 会把文件之间依赖的hash值提取到一个文件保管 --

使用:在optimization中配置

javascript
runtimeChunk: {
  name: (entrypoint) => `runtime~${entrypoint.name}.js`
}

Core-js

  1. 我们之前使用babel的时候,使用智能预设对js进行兼容性处理,但是它只能将es6的一些语法进行编译转换,但是像async promise 数组的一些方法includes等 没办法处理
  2. core-js 是专门用来做ES6以及以上的API的polyfill(补丁)的
  3. 使用:
    1. npm i core-js
    2. 引入方式
      1. 入口文件 --- 全部引入 在入口文件中 import 'core-js' 就完事
      2. 入口文件 --- 按需引入 如 import 'core-js/es/promise' 只引入一个语法
      3. wenpack配置babel ----我们也可以通过配置解决 --- 常用
javascript
// babel配置中找
presets: [
  ["@babel/preset-env", {useBuiltIns: 'usage',corejs: 3// corejs版本}]
]

PWA技术

开发web App项目,项目一旦处于网络离线情况,就没办法访问了,我们希望可以离线访问

PWA 一种渐进式网络应用程序:一种可以提供类似一native app 原生应用程序体验的web app 技术

通过 Service Workers实现的

使用:

  1. npm i workbox-webpack-plugin -D
  2. webpack配置 const WorkboxPlugin = require('workbox-webpack-plugin')
  3. plugins:[] 配置 new WorkboxPlugin.GenerateSW ({clientsCliaim: true, skipWaiting: true})
  4. 入口文件中配置
javascript
 if ('serviceWorker' in navigator) {
   window.addEventListener('load', () => {
     navigator.serviceWorker.register('/service-worker.js').then(registration => {
       console.log('SW registered: ', registration);
     }).catch(registrationError => {
       console.log('SW registration failed: ', registrationError);
     });
   });
 }

注意:我们想使用这个技术 那么必须把打包出的项目部署在服务器环境下,可利用serve

高级优化总结

  • Source Map
  • HotModuleReplacement
  • OneOf
  • Include/Exclude
  • Cache
  • Thead
  • Tree Shaking
  • @babel/plugin-transform-runtime
  • Image Minimizer
  • Code Split
  • Preload / Prefetch
  • Network Cache
  • Core-js
  • PWA

react-cli

开发模式

npm i eslint eslint-webpack-plugin html-webpack-plugin style-loader css-loader less-loader sass-loader sass postcss-preset-env postcss-loader stylus-loader babel-loader @babel/core babel-preset-react-app eslint-config-react-app webpack-dev-server webpack webpack-cli -D

npm i react react-dom 不要添加到开发依赖

. 报错

运行报错

解决:

  1. npm i cross-env -D
  2. 在package.json启动webpack前边加上 cross-env NODE_ENV=devlopment
javascript
"scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  },
js修改,HMR热更新不起作用

解决:https://www.npmjs.com/package/@pmmmwh/react-refresh-webpack-plugin

  1. npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh
  2. webpack.dev.js中
    1. const ReactRefreshWebpackPlugin=require('@pmmmwh/react-refresh-webpack-plugin');
    2. babel-loader的options中添加plugins: ['react-refresh/babel']
    3. 在webpack配置的plugins中 new ReactRefreshWebpackPlugin()
当我们使用BrowserRouter路由的时候,在路由页面刷新,会找不到404, HashRouter没有这个问题

解决:https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback

javascript
devServer: {   // 直接配置
    historyApiFallback: true,
  },
问题4. 我们发现现在路由和其他js文件打包到了一个js中,我们开发想让路由单独打包,--- 路由懒加载

解决:导入路由用懒加载 可以webpack魔法命名 只要设置了splitChunk 就行

javascript
const Home = lazy(() => import(/* webpackChunkName: "home_chunk" */ './pages/Home/home'));
const About = lazy(() => import(/* webpackChunkName: "about_chunk" */ './pages/About/about'))
问题5. I

解决: import 上移就行

生产模式

npm i mini-css-extract-plugin css-minimizer-webpack-plugin image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

问题1. 我们配置完发现,public下面的文件除了index.html都不能被打包,我们会在public中放icon, 以及其他的一些静态资源,他们也需要打包,但是需要排除index.html

解决:

javascript
const CopyPlugin = require("copy-webpack-plugin");

plugins:[]中配置
plugins: [
    new CopyPlugin({
      patterns: [
        { 
          from: path.join(process.cwd(), './pubilc'), 
          to: path.join(process.cwd(), './dist') ,
           globOptions: {
            ignore: ["**/index.html"],
          },
        }
      ],
    }),
  ],

合并开发生产 复用代码

process.env.NODE_ENV ===' production'

优化

问题1. node_modules单独打包出的文件比较大

思路: 将react相关的打包在一起 将antd打包在一起 将其他打包在一起

配置:

javascript
splitChunks: {
            chunks: 'all',
            // ndoe_modules文件打包出来的比较大
            cacheGroups: {
                // 根据项目实际情况  进行打包
                // react react-dom  react-router-dom  打包
                react: {
                    test: /[\\/]node_modules[\\/]react(.*)?/,
                    name: 'chunk_react',
                    // 打包的权重肯定要比node_modules高  要不就没用了
                    priority: 40
                },
                // antd单独打包
                antd: {
                    test: /[\\/]node_modules[\\/]antd?/,
                    name: 'chunk_antd',
                    // 打包的权重肯定要比node_modules高  要不就没用了
                    priority: 30 // 权重
                },
                // 剩余单独打包
                libs: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'chunk_libs',
                   
                    priority: 20
                }
            }
        },

开发模式

npm i vue-loader vue-template-compiler vue-style-loader eslint-webpack-plugin eslint-plugin-vue html-webpack-plugin css-loader postcss-loader postcss-preset-env less-loader sass-loader sass stylus-loader babel-loader @vue/cli-plugin-babel webpack webpack-cli webpack-dev-server eslint @babel/eslint-parser -D

npm i vue

vue-loader的使用 --- 处理.vue文件
  1. npm i vue-loader vue-template-compiler -D
  2. 添加规则
javascript
{
  test: /\.vue$/,
  loader: 'vue-loader'
}
  1. 引入插件,并调用
javascript
const {VueLoaderPlugin} = require('vue-loader')

plugins:[new VueLoaderPlugin()]
  1. 拓展名extensions:['.vue', '.js', '.json']
  2. style-loader 修改成vue-style-loader
问题1. Feature flags are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle. 运行起来浏览器报警告,

解决:

  1. const {DefinePlugin} from "webpack"
  2. 在plugins:[] 配置
javascript
new DefinePlugin({
  __VUE_OPTIONS_API__: true,
  __VUE_PROD_DEVTOOLS__: false
})

生产模式

npm i mini-css-extract-plugin css-minimizer-webpack-plugin image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

图片压缩比较难下载, -f 当下载报错采用强制下载

合并

process.env.NODE_ENV

优化

使用element-plus
  1. 全部引入
    1. npm i element-plus
    2. 入口文件 import ElementPlus from 'element-plus'
    3.               import 'element-plus/dist/index.css
      
    4. app.use(ElementPlus )
    5. 在项目中直接使用,无需其他配置
  2. 按需引入 需要配置 --npm i element-plus
    1. npm i -D unplugin-vue-components unplugin-auto-import
    2. 配置webpack
      1. const AutoImport = require('unplugin-auto-import/webpack')
      2. const Components = require('unplugin-vue-components/webpack')
      3. const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
      4. plugins: [] 配置
        1. AutoImport({ resolvers: [ElementPlusResolver()], }),
        2. Components({ resolvers: [ElementPlusResolver()], }),
    3. 之后在组件中引入并注册组件才能使用
      1. import{ElButton} from 'element-plus'
      2. 使用
修改elementplus的主题配置 用到的时候再来看

https://www.bilibili.com/video/BV14T4y1z7sw?p=63&vd_source=4d085ef79ab7d7c6bec8ce5f945651da

优化打包

配置 在optimization的splitChunks中

javascript
splitChunks: {
            chunks: 'all',
            // ndoe_modules文件打包出来的比较大
            cacheGroups: {
                // 根据项目实际情况  进行打包
                // react react-dom  react-router-dom  打包
                vue: {
                    test: /[\\/]node_modules[\\/]vue(.*)?/,
                    name: 'chunk_vue',
                    // 打包的权重肯定要比node_modules高  要不就没用了
                    priority: 40
                },
                // antd单独打包
                antd: {
                    test: /[\\/]node_modules[\\/]element-(.*)?/,
                    name: 'chunk_element',
                    // 打包的权重肯定要比node_modules高  要不就没用了
                    priority: 30 // 权重
                },
                // 剩余单独打包
                libs: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'chunk_libs',
                   
                    priority: 20
                }
            }
        },
关闭性能分析
javascript

    performance: false, //不展示文件大小性能  提升打包速度  关闭性能分析
vue-loader做缓存
javascript
{
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    cacheDirectory: path.join(process.cwd(), './node_modules/.cache/vueLoaderCache')
                }
            },

made with ❤️ by ankang