webpack5

307 阅读5分钟
  • 现代Web开发“问题”
    • 采用模块化
    • 使用新特性提高效率保证安全性
    • 实时监听开发过程使用热更新
    • 项目结果打包压缩优化
  • 为现代JavaScript应用提供静态模块打包
    webPack功能
    1.打包:将不同类型资源按模块处理进行打包
    2.静态:打包后最终产出静态资源
    3.模块: Webpack支持不同规范的模块开发

配置文件

webpack的配置文件写在webpack.config.js

const path = require('path')
module.exports = {
  // 入口
  entry: './src/index.js',
  // 出口
  output: {
    filename: 'build.js',
    // path.resolve() 将相对路径转换为绝对路径,__dirname 获取当前文件所属目录的绝对路径
    path: path.resolve(__dirname, 'dist')
  }
}

运行配置 在package.json配置build,之后运行npm run build就会运行webpack配置

"scripts": {
    "build": "webpack"
  },

loader

webpack不是所有类型的文件都能当模块进行使用,因此我们需要使用loader对它进行转换

CSS-loader使用

默认情况下webpack是不能处理css文件的,因为它并不是一个js模块,因此我们需要用CSS-loader对css文件进行一个转换,然后webpack拿到转换后的文件进行打包(此时样式并没有生效我们还需要用到style-loader
安装 npm i css-loader -D

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

style-loader
作用:在我们当前界面生成一个style标签,然后把处理好的样式添加进来
安装 npm i style-loader -D

...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'] // 执行的顺序是从后往前
      }
    ]
  }
...

less-loader
安装 npm i less-loader -D

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

browserslistrc

  • 1 工程化:
  • 2 兼容性: CSS JS
  • 3 如何实现兼容:
  • 4 到底要兼容哪些平台
  • caniuse.com
    可以建一个.browserslistrc文件配置browserslistrc
> 0.01% 
last 2 version
not dead

postcss工作流程

postcss是JavaScript转换样式的工具,通过browserslistrc告诉它需要兼容的平台 安装 npm i postcss -D相当于解析器,到时候再安装些插件完成对样式的转换 可以去做一些兼容性的工作,比如给css添加不同的浏览器前缀
安装 npm i postcss-cli -D可以在命令行终端使用命令
npm i autoprefixer -D添加前缀
使用: npx postcss --use autoprefixer -o ret.css ./src/css/test.css

postcss-loader处理兼容

除了使用命令行使用autoprefixer添加前缀,我们可以使用postcss-loader
安装 npm i postcss-loader -D
postcss在css-loader之前进行工作

...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer')
                ]
              }
            }
          }
        ]
      }
    ]
  }

上面这种方式如果还要兼容别的就要再引用相应的插件,比较麻烦,因此我们可以使用下面的方式
postcss-preset-env 预设 -- 插件集合(集合了很多现代css转换时需要用到的插件)
安装 npm i postcss-preset-env -D

...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('postcss-preset-env')
                ]
              }
            }
          }
        ]
      },
      // 但是还有less的之后就得再配置一次,代码就会显得冗余
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('postcss-preset-env')
                ]
              }
            }
          },
          'less-loader'
        ]
      }
    ]
  }

我们可以单独使用一个配置文件postcss.config.js

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

写了这个之后webpack的配置文件就可以简写

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

importLoaders属性

若有使用@import引入css的文件,根据上面的配置打包会发现test.css中的代码没有被解析

@import './test.css';
.title {
  color: #12345678;
}

此时我们需要使用如下的配置

module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1
            }
          },
          'postcss-loader'
        ]
      }
    ]
  }

importLoaders: n表示遇到有使用@import方法引入文件时,我们需要往前使用n个loader来解析文件,这里的importLoaders: 1就表示往前使用一个也就是postcss-loader来处理文件

file-loader处理文件

安装 npm i file-loader -D

/**
 * 打包图片:
 *  - img src
 *    + 使用 require 导入图片,此时如果不配置 esModule: false ,则需.default 导出
 *    + 也可以在配置当中设置 esModule: false
 *    + 采用 import xxx from 图片资源,此时可以直接使用 xxxx
 *  - background url
 */
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: 'file-loader',
        //     options: {
        //       esModule: false // 不转为 esModule
        //     }
        //   }
        // ]
        use: ['file-loader']
      }
    ]
  }
  
  
***********img.css****************

.bgBox {
  width: 240px;
  height: 310px;
  border: 1px solid #000;
  background-image: url('../img/02.react.png'); // css-loader可以识别url的,并将此替换为require语法,而require语法更新后默认导出的是esModule,我们可以在css-loader的配置中
}

***********Image.js***************

import oImgSrc from '../img/01.wb.png'
import '../css/img.css' // 被依赖进来后将来会被我们配置的css的相关loader解析


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

  // 02 创建 img 标签,设置 src 属性
  const oImg = document.createElement('img')
  oImg.width = 600
  // file-loader更新后为了适配webpack5,此处返回的是一个对象的结构{ default: xxx }
  // oImg.src = require('../img/01.wb.png').default 
  // 通过file-loader的配置esModule: false(是否将我们导出的内容转化成esModule)可以不使用.default
  // oImg.src = require('../img/01.wb.png') 
  // 采用esModel的规范,此时oImgSrc就相当于defaule之后的结果
  oImg.src = oImgSrc
  oEle.appendChild(oImg)

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

  return oEle
}

document.body.appendChild(packImg())

设置图片名称与输出

...
  module: {
    rules: [
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              // name: '[name].[hash:6].[ext]', // 设置图片名称
              // outputPath: 'img' // 设置路径
              name: 'img/[name].[hash:6].[ext]', // 设置路径与此路径下的图片名称
            }
          }
        ]
      }
    ]
  }
/**
* [ext]: 扩展名
* [name]: 文件名
* [hash]: 文件内容 + MD4的算法 生成128位的hash值
* [contentHash]:
* [hash:<length>]:length是当前需要保存的长度
* [path]:
*/
...

url-loader处理图片

安装 npm i url-loader -D

...
  module: {
    rules: [
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: 'img/[name].[hash:6].[ext]',
              limit: 25 * 1024
            }
          }
        ]
      }
    ]
  }
/**
 * 01 url-loader base64 uri 文件当中,减少请求次数
 * 02 file-loader 将资源拷贝至指定的目录,分开请求
 * 03 url-loader 内部其实也可以调用 file-loader
 * 04 limit 如果图片的大小超过了limit的值,就会使用file-loader
 *
 */

asset处理图片(webpack5内置)

我们可以使用aasset替换file-loader url-loader raw-loader

  • asset module type
  • 01 asset/resource -->file-loader( 输出路径 )
  • 02 asset/inline --->url-loader(所有 data uri)base64的
  • 03 asset/source --->raw-loader
  • 04 asset (parser )
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    // assetModuleFilename: "img/[name].[hash:4][ext]"  // 相当于asset的全局的路径和名称的配置 不推荐
  },
  module: {
    rules: [
    
    *****asset/resource*****
    
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset/resource',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        }
      },
    *****asset/inline*****
    
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset/inline'
      }
      
    *****asset/resource*****
    
      {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
          filename: "img/[name].[hash:4][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024 // 如果图片的大小超过了maxSize的值,就会像是file-loader
          }
        }
      }
    ]
  }
}

asset处理图标字体

module: {
...
    rules: [
      {
        test: /\.(ttf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[hash:3][ext]'
        }
      }
    ]
  }
...

webpack插件使用

  • 插件与loader
    • loader: 对特定的模块类型进行转换(如以上几种),工作的时机:当我们要去识别或去读取这个文件内容的时候就进行工作
    • plugin: 可以做更多的事情,webpack存在生命周期,工作的时机:插件可以在webpack生命周期的任一时机被插进来,插件其实也是一个类
      举例常用插件clean-webpack-plugin
      安装:npm i clean-webpack-plugin -D
const path = require('path')
// 第三方插件需要自己安装并引入
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {...},
  plugins: [
    new CleanWebpackPlugin()
  ]
}

html-webpack-plugin插件使用

作用: 默认帮我们创建一个index.html,它会有一个自己内置的模板,也可以自己提供模板
安装:npm i html-webpack-plugin -D

const path = require('path')
// 第三方插件需要自己安装并引入
const path = require('path')
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'),
  },
  module: {...},
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'html-webpack-plugin',
      template: './public/index.html' // 自己提供的模板,模板里使用到ejs语法写占位的内容
    }),
    new DefinePlugin({
      BASE_URL: '"./"' // 定义下方常量BASE_URL的值
    })
  ]
}
***********./public/index.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插件使用

作用:复制一些我们不需要打包的资源

    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public',
          globOptions: {
            ignore: ['**/index.html'] // 需要忽略的 **/是从from定义的开始
          }
        }
      ]
    })

babel

作用:将一下新的语法如es6的语法等等转换为浏览器可以识别的语法
npm i babel-loader -D

babel也有预设,设置了许多要转换的语法

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env']
}
babel
module: {
    rules: [
    // 方式一 多个插件
      {
        test:/\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: {
              // 这里放对应插件
              'xxx',
              'xxx'
            }
          }
        }
      },
    // 方式二 多个预设
      {
        test:/\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
              // 这里放预设
            presets: ['xxx','xxx']
          }
        }
      }
    // 方式三
      {
        test: /\.js$/,
        use: ['babel-loader'] // 配置在babel.config.js
      }
},
// babel.config.js
module.exports = {
  presets: ['@babel/preset-env']
}

babel也是根据文件.browserslistrc里写的兼容范围去控制兼容性的

polyfill

webpack4默认有polyfill导致webpack4打包特别大,webpaack5基于优化打包的效率就移除了, ES6在Array对象上新增了Array.from方法,Promise等等更新的语法。Babel预设转换的语法有限,Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个填充。

// babel.config.js
module.exports = {
  // presets: ['@babel/preset-env']
  presets: [
    [
      '@babel/preset-env',
      {
        // false: 不对当前的JS处理做 polyfill 的填充
        // usage: 依据用户源代码当中所使用到的新语法进行填充
        // entry: 依据我们当前筛选出来的浏览器决定填充什么 也就是.browserslistrc此文件中筛选的
        useBuiltIns: 'entry',
        corejs: 3 // 使用的时候默认用的是版本2的,这里安装的是3版面,需要自己指定一下
      }
    ]
  ]
}
// webpack.config.js
  {
    test:/\.js$/,
    exclude: /node_modules/, // 使用这个的时候usage 可以加这行剔除node_modules,不用被我们的loader处理
    use: ['babel-loader']
  }

使用entry的时候需要在入口文件加import "core-js/stable"; image.png

webpack-dev-server

作用:开启一个热更新的本地服务,代码会运行到内存中
package.json中的scripts配置使用

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "serve": "webpack serve"
  },

HMR

作用:热模块替换

// indes.js
import './title.js'
if (module.hot) {
  module.hot.accept(['./title.js'], () => {
    console.log('title.js模块更新')
  })
}

// title.js
console.log('title.js');

// webpack.config.js
...
devServer: {
    hot: true
},
target: 'web', // 开发时可以配置这个,不用.browserslistrc
...

vue组件支持热更新

output中的path