一:背景介绍

工程化项目需要、前端要求提高

二:安装、打包过程

安装:npm i webpack webpack-cli -D 打包:npx webpack 、 全局webpack、package中设置

三:webpack实战、配置

1、entry 入口

字符串、数组、对象

2、output 输出

  1. const path = require('path')
  2. output:{
  3. filename:'bundle.js',
  4. path:path.resolve(__dirname,"dist")
  5. }

3、mode 模式

可选: none、development、production

4、loader 加载器/解析器

webpack默认只能处理js和json文件,我们需要通过loader去转换成webpack可以处理的文件
test: 处理文件扩展名的正则表达式
use: 要使用的模块加载器的名称
include/exclude: 手动指定必须要处理的文件或者屏蔽的文件
options:为loaders配置额外设置选项

  • 解析css文件 style-loader、css-loader 、postcss-loader // 处理css3私有前缀 , 需要配合autoprefixer(私有前缀插件)
  • 解析less less 、less-loader
  • 解析sass node-sass、sass-loader
  • 解析stylus stylus、stylus-loader
  • 分离css的插件 生产环境下 mini-css-extract-plugin
    ```javascript module:{ rules:[ {
    1. test:/\.css$/,
    2. use:["style-loader",{
    3. loader:"css-loader",
    4. options:{
    5. importLoaders:1 // 添加一个加载器帮忙处理
    6. }
    7. },
    8. "postcss-loader",
    9. "less-loader"
    10. ]
    11. }
    ] }

// postcss.config.js modules.exports ={ plugins:[{ require(‘autoprefixer’) }] } // .browserslistrc cover 99.5%

  1. <a name="cp158"></a>
  2. #### 5、plugin 插件 —— 扩展功能
  3. - html-webpack-plugin 自动引入html,并产生打包后的文件,添加hash指纹等
  4. ```javascript
  5. const HtmlWebpackPlugin = require('html-webpack-plugin')
  6. plugins:[
  7. new HtmlWebpackPlugin({
  8. template:"./index.html",
  9. hash:true,
  10. minify:{
  11. removeAttributeQuotes:true,
  12. collapseWhitespace:true,
  13. removeComments:true
  14. },
  15. filename:'index.html',
  16. chunks:["index"], // 应用于多入口时,指定引入的js文件名
  17. inject:"body"
  18. })
  19. ]
  • webpack-dev-server 创建一个本地的开发服务,自动打开html文件

    1. devServer:{
    2. port:6666,
    3. open:true,
    4. compress: true, // 开启gzip压缩
    5. contentBase: path.resolve(__dirname,'dist') // 修改访问静态资源的路径
    6. }
  • clean-webpack-plugin 清除输出目录,可以指定排除

    1. const CleanWebpackPlugin = require('clean-webpack-plugin')
    2. plugins:[
    3. new CleanWebpackPlugin({
    4. cleanOnceBeforeBuildPatterns:["aa/**/*","!aa/c.js"] // 清理aa下的文件,排除aa下c.js
    5. })
    6. ]
  • mini-css-extract-plugin 分离css,添加hash ```javascript

配合:然后把loader中的 style-loader换成{loader:MiniCssExtractPlugin.loader}

plugins:[ new MiniCssExtractPlugin({ filename:”css/[name].[contentHash:4].css” // 4 是设置hash长度 }) ]

  1. <a name="oCMXc"></a>
  2. #### 6、配置多入口、多出口打包
  3. ```javascript
  4. entry:{
  5. index:'./src/index.js',
  6. other:'./src/other.js'
  7. }
  8. output:{
  9. filename:'[name].js',
  10. path:path.resolve(__dirname,"dist")
  11. }
  12. // 配置HtmlWebpackPlugin chunks, 很多的时候可以通过数组遍历插入
  13. let htmlPlugin = ['index','other'].map((chunkName)=>{
  14. return new HtmlWebpackPlugin({
  15. template:`./${chunkName}.html`,
  16. hash:true,
  17. minify:{
  18. removeAttributeQuotes:true,
  19. collapseWhitespace:true,
  20. removeComments:true
  21. },
  22. filename:`./${chunkName}.html`,
  23. chunks:[chunkName], // 应用于多入口时,指定引入的js文件名
  24. inject:"body"
  25. })
  26. })
  27. // 使用
  28. plugins:[
  29. ...htmlPlugin
  30. ]

7、处理图片

file-loader url-loader
使用file-loader,会将图片进行打包,并将打包后的路径返回
url-loader,会

  1. /*{
  2. test:/\.(jpe?g|png|gif)$/,
  3. use:{
  4. loader:'file-loader',
  5. options:{
  6. name:`img/[name].[ext]`
  7. }
  8. }
  9. },*/
  10. {
  11. test:/\.(jpe?g|png|gif)$/,
  12. use:{
  13. loader:'url-loader',
  14. options:{
  15. // 小于30kb用base64的形式处理,大于30kb用file-loader处理
  16. limit:30*1024,
  17. name:`img/[name].[ext]`
  18. }
  19. }
  20. },
  • html-withimg-loader 处理, 图片的引用路径就不是之前的了,就变成之后的路径了

    1. {
    2. test:/\.(html)$/i,
    3. use:{
    4. loader:'html-withimg-loader'
    5. }
    6. }
  • 处理icon-font

    1. // index.js
    2. import "./icon/iconfont.css";
    3. let i = document.createElement("i");
    4. i.className = "iconfont icon-course";
    5. document.body.appendChild(i);
    1. {
    2. test:/\.(eot|svg|ttf|woff2)$/,
    3. use:"file-loader"
    4. }

    8、处理js

    @babel/core 核心模块
    @babel/preset-env 将es6处理成es5插件的集合
    babel-loader webpack和babel的桥梁

布丁
core-js@3 使用高版本js的布丁
@babel/plugin-proposal-class-properties 安装一下这个插件处理class
@babel/plugin-proposal-decorators 保留装饰器

优化 加快构建效率,减少冗余代码
@babel/plugin-transform-runtime
@babel/runtime

  1. // index.js
  2. let a1 = Object.assign({},{"a":1})
  3. console.log(a1)
  4. [1,2,3].includes(1)
  5. async function fn(){
  6. console.log(1)
  7. }
  8. @log
  9. class People{
  10. a = 1;
  11. };
  12. function log(target){
  13. console.log(target);
  14. }
  15. new People();


  1. // webpack.config.js
  2. {
  3. test:/\.js$/,
  4. use:"babel-loader",
  5. exclude:/node_modules/
  6. }
  7. // .babelrc 文件
  8. {
  9. "presets":[
  10. ["@babel/preset-env",{
  11. "useBuiltIns":"usage", // 按需加载 使用了api就会转化哪个
  12. "corejs": 3
  13. }]
  14. ],
  15. "plugins":[
  16. "@babel/plugin-transform-runtime",
  17. "@babel/runtime",
  18. ["@babel/plugin-proposal-decorators",{ "legacy":true }], // 保留装饰器
  19. ["@babel/plugin-proposal-class-properties",{"loose":true}], // 处理class
  20. ]
  21. }
  • 添加eslint

    npm i eslint eslint-loader -D
    可以去eslint选择配好在下载
    也可以npx eslint —init 初始化配置文件 手动选择
    image.png

    1. // webpack.config.js
    2. {
    3. test:/\.js$/,
    4. use:"eslint-loader",
    5. enforce:"pre" // 优先处理
    6. }


    9、合并配置文件 区分本地还是开发环境

webpack.base.js 公共的配置文件
webpack.dev.js 开发环境的配置文件
webpack.prod.js 生产环境下的配置文件
通过webpack-merge合并
npm i webpack-merge -D

  1. "scripts":{
  2. "dev": "npx webpack --env.development --config webpack.base",
  3. "build": "npx webpack --env.production --config webpack.base",
  4. "dev:server":""
  5. }
  1. // webpack.base.js
  2. const dev = require('./webpack.dev');
  3. const prod = require('./webpack.prod');
  4. const { merge } = require("webpack-merge");
  5. module.exports = (env) =>{
  6. console.log(env)
  7. const isDev = env.development;
  8. let base = {
  9. }
  10. if(isDev){
  11. // 合并base 和 dev
  12. return merge(base,dev)
  13. }else{
  14. // 合并base 和 prod
  15. return merge(base,prod)
  16. }
  17. }

image.png
image.png
image.png
image.png

image.png

10、压缩代码

optimize-css-asstes-webpack-plugin 压缩css代码
terser-webpack-plugin 压缩js代码

  1. // 生产环境下
  2. // webpack.prod.js 文件
  3. const OptimizeCsAsset = require('optimize-css-asstes-webpack-plugin')
  4. const TerserWebpackPlugin = require("terser-webpack-plugin")
  5. module.exports = {
  6. mode:"production",
  7. optimization:{
  8. minimizer:[
  9. new OptimizeCsAsset(),
  10. new TerserWebpackPlugin()
  11. ]
  12. }
  13. }

11、优化,删除无用的css

purgecss-webpack-plugin
glob // 设置查找文件的路径和匹配的文件

  1. // 生产环境下
  2. // webpack.prod.js 文件
  3. const PurgecssPlugin = require("purgecss-webpack-plugin")
  4. module.exports = {
  5. mode:"production",
  6. plugins:[
  7. new PurgecssPlugin({
  8. paths:glob.sync(`${path.resolve(__dirname,"src")}/**/*`,{nodir:true})
  9. })
  10. ]
  11. }

12、安装配置react

安装@babel/preset-react -D
安装 react react-dom

  1. // .babelrc 文件
  2. {
  3. "presets":[
  4. ["@babel/preset-env",{
  5. "useBuiltIns":"usage", // 按需加载 使用了api就会转化哪个
  6. "corejs": 3
  7. },
  8. "@babel/preset-react"
  9. ]
  10. ],
  11. "plugins":[
  12. "@babel/plugin-transform-runtime",
  13. "@babel/runtime",
  14. ["@babel/plugin-proposal-decorators",{ "legacy":true }], // 保留装饰器
  15. ["@babel/plugin-proposal-class-properties",{"loose":true}], // 处理class
  16. ]
  17. }
  1. import './index.css';
  2. import React from "react";
  3. import ReactDOM from "react-dom";
  4. React.DOM.render(<h1 className="a1">zf</h1>,document.getElementById("root");

13、Tree-shaking && scope-Hosting

把没用的内容摇晃掉

  1. // package.json中
  2. 设置sideEffects唯一一个需要在package.json中去设置的
  3. "sideEffects":[
  4. "**/*.css"
  5. ]
  1. // 引入不使用,不会打包进去
  2. import "./index.css"
  3. import "./count"

scope-Hosting (了解、自带)作用 减少代码体积,节约内存

webpack配置优化重点考虑 1)打包的大小 2)打包的速度 3)模块的拆分

14、DllPlugin && DllReferencePlugin 主要开发环境下用

DllPlugin 设置动态链接库(webpack自带)
DllReferencePlugin 引用动态链接库 mainfest.json

// 把js文件插入到html文件中
npm i add-asset-html-webpack -D

把第三模块或者第三方库单独打包成动态链接库,以后构建的时候只需要查找构建好的库就行了

  1. // package.json
  2. "srcipts":{
  3. "dll":"npx webpack --config ./webapacl.dll"
  4. }
  1. // webpack.dll.js 文件 动态链接库
  2. // 还需要一个缓存列表 指向打包后的文件
  3. const path = require("path")
  4. const Dllplugin = require("webpack").DllPlugin; // 动态链接库
  5. module.exports={
  6. mode:"development",
  7. //entry:"./src/count.js",
  8. entry:['react','react-dom'],
  9. output:{
  10. library:"react", // 用来接收打包后自执行函数返回值的名字
  11. libraryTarget:"commonjs2", // commonjs umd 默认var接收
  12. filename:'react.dll.js',
  13. path:path.resolve(__dirname,"dll")
  14. },
  15. plugins:[
  16. new DllPlugin({
  17. name:'react',
  18. path:path.resolve(__dirname,'dll/mainfest.json') // 缓存文件的路径
  19. })
  20. ]
  21. }
  1. // webpack.base.js
  2. const DllReferencePlugin = require('webpack').DllReferencePlugin
  3. const AddAsserHtmlPlugin = require('add-asser-html-plugin'
  4. plugins:[
  5. new DllReferencePlugin({
  6. mainfest: path.resolve(__dirname,"dll/mainfest.json") // 引入动态链接库缓存列表
  7. }),
  8. new AddAsserHtmlPlugin({
  9. filepath:path.resolve(__dirname,"dll/react.dll.js")
  10. })
  11. ]

image.png
image.png
建议在开发环境下采用这种方案,有利于我们提升构建速度的,生产环境有更优的方案
dll这种方案在开发之前先抽离好,以后就不用打包了

15、 动态加载组件

实现点击后动态加载文件

  • webpackChunkName
  • webpackPrefetch 利用浏览器空闲时间把动态模块加载完成并引入进来
  • webpackPreload 根主模块的代码同时加载
    1. // index.js
    2. let btn = document.createElement("button");
    3. btn.addEventListener("click",()=>{
    4. import(/* webpackChunkName:'count' */"./count").then(data=>{
    5. console.log(data.add(10,20))
    6. })
    7. })
    8. btn.innerHTML = "点我"
    9. document.body.appendChild(btn)
    ```javascript // count.js
  1. <a name="FGB9p"></a>
  2. #### 16、souce-map (代码排查错误)
  3. 开发环境推荐 cheap-module-eval-source-map<br />生产环境推荐 cheap-module-source-map
  4. ```javascript
  5. devtool:"cheap-module-eval-source-map"

17、 打包文件分析工具

bundle-analyzer-plugin

  1. const { BundleAnalyzerPlugin} = require('bundle-analyzer-plugin')
  2. plugins:[
  3. new BundleAnalyzerPlugin()
  4. ]

18、splitChunks

在编译时可以抽离第三方莫款或公共模块,不要根dllPlugin同时用,会出问题
和上面的分析工具一起用
image.png

  1. optimization: {
  2. splitChunks: {
  3. chunks: 'async',
  4. minSize: 30000,
  5. maxSize: 0,
  6. minChunks: 1,
  7. maxAsyncRequests: 5,
  8. maxInitialRequests: 3,
  9. automaticNameDelimiter: '~',
  10. name: true,
  11. cacheGroups: {
  12. vendors: {
  13. test: /[\\/]node_modules[\\/]/,
  14. priority: -10
  15. },
  16. default: {
  17. minChunks: 2,
  18. priority: -20,
  19. reuseExistingChunk: true
  20. }
  21. }
  22. }

19、externals 如果cdn引入,这样引用不会加大体积

  1. externals:{
  2. 'jquery':'$'
  3. }

20、热更新

代码修改后不刷新就可以更改样式

  1. devServer:{
  2. hot:true // 开发时需要,
  3. }
  1. if(module.hot){
  2. module.hot.accept("./count",()=>{
  3. p.innerHTML = add(10,20)
  4. })
  5. }

21、费时更新

speed-measure-webpack-plugin
image.png

22、 resolve 设置别名

  1. resolve:{
  2. extensions:['.js','.jsx','.json','.css'], // 按顺序解析后缀名
  3. alias:{} // 设置别名
  4. }

23、 happypack 多线程打包

npm i happypack -D
image.png

24、参考

由浅入深配置webpack4
webpack的异步加载原理及分包策略