webpack

why webpack

现代web开发的问题

  • 采用模块化开发
  • 使用新特性提供效率
  • 开发过程中实时监听,热更新
  • 编码完成打包压缩优化

webpack 功能

安装webpack和webpack-cli

  • 打包:将不同类型资源按模块处理进行打包
  • 静态:打包后最终产出静态资源
  • 模块:webpack支持不同规范的模块化开发
  • webpack会默认去项目下的src找index.js,然后对index.js里面的资源进行查找,进行统一打包(ESModule或者commonJS规范的模块都会统一处理),并在项目下产出一个dist目录,生成一个打包好的js文件
  • “build”: “webpack --config lg.webpack.js” 通过–config可以自定义修改webpack.config.js
  • webpack默认只会读取src/index.js中已经引用了的文件或模块,进行打包

为什么需要loader

  • 处理非js文件就需要loader

loader是什么

  • loader也是一个模块里面是js代码逻辑,能够将对应类型的文件转换成webpack能够处理的文件

css-loader

  • css-loader只是将css代码处理为当前js可以识别的模块类型,里面可能存在@import/background/url等操作,css-loader更多的是处理这些,并不能将样式放在页面上使用想要在页面看到样式效果要么使用style属性,或style标签,要么通过link链接,而css-loader无法做到这些,需要另外的loader(style-loader)

-1.安装css-loader npm i css-loader -D
-2.方式一:通过行内使用(在前面加上css-loader前缀) import ‘css-loader!../css/login.css’
-3.方式二:通过配置文件配置css-loader

const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      // {
      //   test: /\.css$/, // 一般就是一个正则表达式,用于匹配我们需要处理的文件类型
      //   use: [
      //     {
      //       loader: 'css-loader'
      //     }
      //   ]
      // },
      // {
      //   test: /\.css$/,
      //   loader: 'css-loader'
      // },
      {
        test: /\.css$/,
        use: ['css-loader']
      }
    ]
  }
}

style-loader

  • npm i style-loader -D
  • 注意在配置文件中loader的使用顺序; loader的执行顺序默认是从右往左;从下往上
const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

less-loader

const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  }
}

browserslistrc

如何实现兼容

  • 1.js ⇒ babel
  • 2.css ⇒Autoprefixer/postcss-preset-env

兼容哪些平台

  • 3.从caniuse.com获取

配置方法一:

  • 在package.json里添加配置
    "browserslist:[">1%,"last 2 version","not dead"]"

配置方法二:

  • 新建.browserslist文件
> 1% 
last 2 version
not dead

postcss

1.postcss: js转换样式的工具
2.postcss在css-loader之前进行工作(进行兼容性处理)

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

importLoaders

  • css-loader里面的options添加importLoaders属性,1表示css-loader工作时,又找到了css需要进行处理,就往前找一个;0表示和没写一样
const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}

file-loader处理图片

import oImgSrc from '../img/01.wb.png'
import '../css/img.css'


function packImg() {
  // 01 创建一个容器元素
  const oEle = document.createElement('div')

  // 02 创建 img 标签,设置 src 属性
  const oImg = document.createElement('img')
  oImg.width = 600
  // oImg.src = require('../img/01.wb.png').default
  // oImg.src = require('../img/01.wb.png')  require不用.default时需要配置options:{esModule:false}
  oImg.src = oImgSrc
  oEle.appendChild(oImg)

  // 03 设置背景图片
  const oBgImg = document.createElement('div')
  oBgImg.className = 'bgBox'
  oEle.appendChild(oBgImg)

  return oEle
}

document.body.appendChild(packImg())

const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false 
              // css-loader处理url('xxxxxx')时,会自动替换为require语
              //法,require语法会导出一个esModule,require语法要配合.default,
              //但是css中无法使用.default,所以要配置esModule:false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: 'img/[name].[hash:6].[ext]',
              // outputPath: 'img' (可简写为上面img/xxx的写法)
            }
          }
        ]
      }
    ]
  }
}
/**
 * [ext]: 扩展名(原扩展名输出)
 * [name]: 文件名(原文件名输出)
 * [hash]: 文件内容
 * [contentHash]:等同[hash]
 * [hash:<length>]
 * [path]:
 */

url-loader处理图片

1.file-loader

  • 把图片的名称/路径返回,把要打包的图片资源拷贝到指定的目录,分开请求

2.url-loader

  • 默认会把要打包的图片资源以base64 uri的方式加载到代码当中

3.优缺点

  • url-loader相比file-loader能和减少请求次数,因为所有图片都放在一个js文件中
  • url-loader内部其实也可以调用file-loader
  • limit属性
const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: 'img/[name].[hash:6].[ext]',
              limit: 25 * 1024 //大于25kb就拷贝,小于25就转成base64
            }
          }
        ]
      }
    ]
  }
}
/**
 * 01 url-loader base64 uri 文件当中,减少请求次数
 * 02 file-loader 将资源拷贝至指定的目录,分开请求
 * 03 url-loader 内部其实也可以调用 file-loader
 * 04 limit
 *
 */

asset处理图片

webpack5 内置的资源处理模块

  • asset/resource 拷贝目标资源到指定目录 等同于 file-loader
  • asset/inline 把资源添加到代码中 等同于 url-loader
  • asset/source
const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    // 全局只要是通过asset处理的资源都放到此目录下
    // assetModuleFilename: "img/[name].[hash:4][ext]"  
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      // {
      //   test: /\.(png|svg|gif|jpe?g)$/,
      //   type: 'asset/resource',
      // 单独配置图片文件的打包存放目录
      //   generator: {
      //     filename: "img/[name].[hash:4][ext]"
      //   }
      // },

      
      // {
      //   test: /\.(png|svg|gif|jpe?g)$/,
      //   type: 'asset/inline'
      // }
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      }
    ]
  }
}
/**
 * 01 url-loader base64 uri 文件当中,减少请求次数
 * 02 file-loader 将资源拷贝至指定的目录,分开请求
 * 03 url-loader 内部其实也可以调用 file-loader
 * 04 limit
 *
 */

asset处理字体图标

webpack5之前在处理字体图标时,同样时按照静态资源处理,使用file-loader或url-loader处理,webpack5可以直接使用asset处理

const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    // assetModuleFilename: "img/[name].[hash:4][ext]"
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      //处理图片资源
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      //处理字体图标
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      }
    ]
  }
}
/**
 * 01 url-loader base64 uri 文件当中,减少请求次数
 * 02 file-loader 将资源拷贝至指定的目录,分开请求
 * 03 url-loader 内部其实也可以调用 file-loader
 * 04 limit
 *
 */

插件的使用

  • loader :
    1.功能:针对特定的文件类型的转换
    2.当识别或读取文件内容时,就开始进行工作
  • plugin
    1.可以实现更多东西
//和module并列
//每一个插件就是一个类,使用的时候new这个类
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


 plugins: [
    new CleanWebpackPlugin()
  ]

html-webpack-plugin使用

自动在打包之后的目录中生成html文件

const path = require('path')
//用于识别html模板中的变量,如BASE_URL
const { DefinePlugin } = require('webpack')  
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    // assetModuleFilename: "img/[name].[hash:4][ext]"
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'html-webpack-plugin',
      template: './public/index.html'
    }),
    new DefinePlugin({
      BASE_URL: '"./"' //注意写法
    })
  ]
}


/**
 * class MyPlugin{
 *  constructor(){}
 *  apply()
 * }
 */

html模板文件:

<!DOCTYPE html>
<html lang="">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">

  <link rel="icon" href="<%= BASE_URL %>favicon.ico">

  <title>
    <%= htmlWebpackPlugin.options.title %>
  </title>
</head>

<body>
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
        Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- built files will be auto injected -->
</body>

</html>

copy-webpack-plugin

对于项目中不需要处理的文件需要copy到自定义的目录下(.ico文件,说明文档等等)

const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/main.js',
    path: path.resolve(__dirname, 'dist'),
    // assetModuleFilename: "img/[name].[hash:4][ext]"
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'copyWebpackPlugin',
      template: './public/index.html'
    }),
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          //表示从public拷贝,to可以简写,简写了就和output输出目录一致
          from: 'public', 
          //忽略public下的html文件,html文件不做拷贝,
          // 上面HtmlWebpackPlugin已经拿html文件做模板了
          globOptions: {
            ignore: ['**/index.html']
          }
        }
      ]
    })
  ]
}

babel

  • 为什么需要babel
    1.jsx语法 TS ES6等等 需要在浏览器平台直接使用,就需要转换;功能与postcss类似;处理js的兼容

注意

  • 直接使用babel是不会转换的,无法进行兼容性处理,需要针对不同的内容安装特定的Babel插件
  • 例如处理箭头函数需要安装plugin-transform-arrow-functions
    npm i @babel/plugin-transform-arrow-functions -D
  • 处理const let需要安装plugin-transform-block-scoping
    npm i @babel/plugin-transform-block-scoping -D
  • 为了防止每次都要安装对应的依赖插件,@babel/preset-env集成了一些常用的插件用于处理新语法, 不需要每次对应的去安装 npm i @babel/preset -env -D
  • babel同样会根据.browserslistrc里面的内容进行符合条件的处理

babel-loader

const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'js/main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      },
      //babel-loader不会对文件进行兼容处理,需要用到preset-env插件集合,在babel.config.js导出
      {
        test: /\.js$/,
        use: ['babel-loader']
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'copyWebpackPlugin',
      template: './public/index.html'
    }),
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public',
          globOptions: {
            ignore: ['**/index.html']
          }
        }
      ]
    })
  ]
}

  • babel.config.js
module.exports = {
  presets: ['@babel/preset-env']
}

polyfill

  • -----------为什么需要polyfill?-------
  1. webpack5之前是集成的,webpack5为了优化打包速度单独出去了
  2. 上面提到了preset-env预设插件,但是需要注意的是,预设插件只是预设的一些常见的(箭头函数、const关键字)用于处理兼容的插件;
    如果遇到了像promise,generator生成器函数,symbol等更新的语法的时候preset-env就无法进行转换,这时就需要用到poyfill
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        // false: 不对当前的JS处理做 polyfill 的填充(默认)
        // usage: 依据用户源代码当中所使用到的新语法进行填充
        // entry: 依据我们当前筛选出来的浏览器决定填充什么
        useBuiltIns: 'entry',
        corejs: 3
      }
    ]
  ]
}

webpack-dev-server

  • 启本地服务器
    1.通过live server插件配合 --watch或者在配置文件通过命令watch:true
    不足:
    1.所有源代码都会重新编译
    2.每次编译成功之后都需要进行文件读写
    3. 依赖live server
    4.不能局部刷新
  "serve": "webpack serve --config lg.webpack.js"
webpack-dev-middleware

在本地开启一个服务,将webpack打包后的文件交给这个服务,然后浏览器直接访问

// server.js 开启服务
const express = require('express')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')

const app = express()

// 获取webpack配置文件
const config = require('./webpack.config.js')
//调用webpack函数得到compiler,webpack会执行配置文件进行打包
const compiler = webpack(config)

app.use(webpackDevMiddleware(compiler))

// 开启端口上的服务
app.listen(3000, () => {
  console.log('服务运行在 3000端口上')
}) 
HMR hot module replacement模块热替换
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  //会和.browserslistrc冲突,
  //target:'web' 表示开发阶段忽略browserslistrc
  mode: 'development', 
  devtool: false,
  entry: './src/index.js',
  output: {
    filename: 'js/main.js',
    path: path.resolve(__dirname, 'dist')
  },
  target: 'web',
  // 所有跟webpack-dev-server相关的配置卸载devServer里
  devServer: {
    hot: true //开启热更新
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'copyWebpackPlugin',
      template: './public/index.html'
    }),
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public',
          globOptions: {
            ignore: ['**/index.html']
          }
        }
      ]
    })
  ]
}

打包的入口文件src/index.js

import './title'

//表示给当前模块开启热更新
if (module.hot) {
  //第一个参数表示要开启热更新的模块
  module.hot.accept(['./title.js'], () => {
    console.log('title.js模块更新')
  })
}
react组件热更新
  • 1.webpack识别react组件 用到babel-loader @babel/preset-env @babel/preset-react
  • 2.支持js的热更新 hot:true 且target:‘web’
  • 3 react组件热更新用到的插件
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    filename: 'js/main.js',
    path: path.resolve(__dirname, 'dist')
  },
  target: 'web',
  devServer: {
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      },
      {
        test: /\.jsx?$/,
        use: ['babel-loader']
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'copyWebpackPlugin',
      template: './public/index.html'
    }),
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public',
          globOptions: {
            ignore: ['**/index.html']
          }
        }
      ]
    }),
    new ReactRefreshWebpackPlugin()
  ]
}

module.exports = {
  presets: [
    ['@babel/preset-env'],
    ['@babel/preset-react'],
  ],
  plugins: [
    ['react-refresh/babel']
  ]
}

path、devServer

  • output
    output中的path是打包后的文件路径,publicPath是告诉index.html以什么样的规则去查找filename
    域名端口+publicPath(默认为’‘)+path路径
  • devServer
    publicPath:指定本地服务所在的目录
    contentBase:我们打包之后的资源如果说依赖了其他的资源(没有被打包的),此时就告知去哪找
    watchContentBase:没有被打包的内容发生修复也能监控到
    hotOnly:当前界面存在多个状态,其中某一个状态在执行过程中发生改变(发生语法错误),如果不配置hotOnly的情况下根据报错位置修改好了以后,默认会对整个页面执行一次刷新操作
    compress:开启服务端的gzip压缩
    historyApiFallback:处理history模式 后端没有对应的资源情况
  • output里面的publicPath和devServer里的publicPath保持一致
 output: {
    filename: 'js/main.js',
    path: path.resolve(__dirname, 'dist')
  },
  target: 'web',
  devServer: {
    hot: true,
    hotOnly: true,
    port: 4000,
    open: false,
    compress: true,
    historyApiFallback: true
  },
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    filename: 'js/main.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/lg'
  },
  target: 'web',
  devServer: {
    hot: true,
    publicPath: '/lg',
    contentBase: path.resolve(__dirname, 'public'),
    watchContentBase: true
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              esModule: false
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024
          }
        }
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      },
      {
        test: /\.jsx?$/,
        use: ['babel-loader']
      },
      {
        test: /\.vue$/,
        use: ['vue-loader']
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'copyWebpackPlugin',
      template: './public/index.html'
    }),
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public',
          globOptions: {
            ignore: ['**/index.html']
          }
        }
      ]
    }),
    new VueLoaderPlugin()
  ]
}

proxy

  devServer: {
    hot: true,
    hotOnly: true,
    port: 4000,
    open: false,
    compress: true,
    historyApiFallback: true,
    proxy: {
      // /api/users
      // http://localhost:4000/api/users
      // https://api.github.com/info/users
      // /api/users---> 返回
      //https://api.github.com/users 后端接口直接就是/users所以需要重写
      //如果是https://api.github.com/info/users就需要重写为pathRewrite: { "^/api": "info" }
      '/api': {
        target: 'https://api.github.com',
        pathRewrite: { "^/api": "" },
        changeOrigin: true
      }
    }
  },

/api 开头的接口请求都走代理设置
通过http://localhost:4000转发请求

代码懒加载

在这里插入图片描述

  • 预获取

会在浏览器空闲的时候加载

const oBtn = document.createElement('button')
oBtn.innerHTML = '点击加载元素'
document.body.appendChild(oBtn)

// 按需加载
oBtn.addEventListener('click', () => {
  import(
    /*webpackChunkName:'utils' */
    /*webpackPreFetch:true */
    './utils').then(({ default: element }) => {
      console.log(element)
      document.body.appendChild(element)
    })
})
  • 预加载

preLoad会在当前引用的页面加载的时候并行加载

const oBtn = document.createElement('button')
oBtn.innerHTML = '点击加载元素'
document.body.appendChild(oBtn)

// 按需加载
oBtn.addEventListener('click', () => {
  import(
    /*webpackChunkName:'utils' */
    /*webpackPreFetch:true */
    './utils').then(({ default: element }) => {
      console.log(element)
      document.body.appendChild(element)
    })
})
  • splitChunks
  optimization: {
    minimizer: [
      new TerserPlugin({
        extractComments: false,
      }),
    ],
    splitChunks: {
      chunks: 'initial',  // async initial all
      minSize: 20000, //达不到minSize就不拆
      maxSize: 20000, //拆体积大于maxSize的 且拆出来的不能小于minSize
      minChunks: 1,  //至少被引用了一次才会被拆,跟minSize和maxSize不一起出现
      cacheGroups: { //第三方的引用有很多的时候,某一个拆包好了放入缓存等其他的也好了再打包到一起
        syVendors: {
          test: /[\\/]node_modules[\\/]/,
          filename: 'js/[id]_vendor.js', //写入的目录,打包的名称
          priority: -10, // 谁大就走谁
        },
        default: {
          minChunks: 2,
          filename: 'js/syy_[id].js',
          priority: -20,// 谁大就走谁
        }
      }
    }
  },
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require("terser-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')  //css文件单独抽离
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") //css文件压缩插件
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const CompressionPlugin = require("compression-webpack-plugin")
const InlineChunkHtmlPlugin = require('inline-chunk-html-plugin')
const resolveApp = require('./paths')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true, // 标记不被使用的函数
    minimize: true,
    minimizer: [
      new TerserPlugin({
        extractComments: false,
      }),
      new CssMinimizerPlugin()
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public',
          globOptions: {
            ignore: ['**/index.html']
          }
        }
      ]
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css'
    }),
    // css tree-shaking
    new PurgeCSSPlugin({
      paths: glob.sync(`${resolveApp('./src')}/**/*`, { nodir: true }),
      safelist: function () {
        return {
          standard: ['body', 'html', 'ef']
        }
      }
    }),
    //文件压缩  
    new CompressionPlugin({
      test: /\.(css|js)$/, //只对css和js压缩
      minRatio: 0.8,
      threshold: 0,
      algorithm: 'gzip'
    }),
    new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js/])
  ]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值