webpack5 基本配置

这篇文件主要介绍以下基本配置:

  1. 拆分不同环境的配置文件:webpack-merge
  2. 自动生成模板文件: html-webpack-plugin
  3. 自动删除以前的打包文件:clean-webpack-plugin
  4. 定义全局变量:
    • 使用cross-env设置命令行参数,并在配置文件中获取process.env.xxx,这个只能在脚本配置文件中获取
    • 使用webpack.DefinePlugin设置模块中可以使用的全局变量,注意要使用JSON.stringify封装变量
  5. 开发环境跨域设置:devServer.proxy中设置代理
  6. 样式资源导入处理
    • css:css-loader, style-loader
    • less:less-loader
    • 增加浏览器兼容前缀:postcss-loaderautoprefixer插件结合使用
    • 抽离css文件:mini-css-extract-pluginloader和插件
  7. 图片资源导入处理
    • file-loader:将资源文件抽离出来,打包到指定位置
    • url-loader:同file-loader,但url-loader增加文件大小阈值,小于阈值使用base64导入
  8. 将es6转换成es5:babel-loader

创建demo

  1. 创建项目
mkdir webpack-demo
cd webpack-demo
npm init -y
npm i webpack webpack-cli -D

可以看到package.json中webpack 和 webpack-cli的版本,现在最新的已经是webpack5

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.40.0",
    "webpack-cli": "^4.7.2"
  }
}
  1. 创建基本目录
  • src:资源目录,随便创建一个文件index.js,作为文件入口
// index.js
function hello() {
  console.log('hello')
}
hello()  // 注意要调用这个函数,不然会被默认tree-shaking掉
  • dist:创建打包输出目录
  • webpack.config.js:webpack默认的基本配置文件
const path = require('path')

module.exports = {
  entry: path.join(__dirname, 'src', 'index.js'), // 单文件入口
  output: {
    filename: 'bundle.js',  // 输出文件名
    path: path.join(__dirname, 'dist')  // 输出目录
  }
}
  • 改下package.json脚本命令
"scripts": {
    "build": "webpack"
  },
  1. 启动打包:npm run build
    会在dist/bundle.js打包出以下结果,可以看出来webpack5的tree-shaking已经非常强大,无用的代码不再被保留
console.log("hello");

区分 开发环境dev 和 生产环境prod

在大项目中,我们往往需要根据不同的环境来进行不同的配置,所以需要拆分webpack配置文件

  1. 创建一个config目录,用来区分不同环境的配置文件:
    • webpack.common.js:用来配置公共的webpack配置
    • webpack.dev.js:用来配置开发环境专有的webpack配置
    • webpack.prod.js:用来配置生产环境专有的webpack配置
  2. 在不同环境的配置文件中,使用webpack-merge中的merge来合并公共的webpack配置文件(webpack4中是通过smart来合并的)
    npm i webpack-merge -D
// webpack.common.js 公共的配置
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')

module.exports = {
  entry: path.join(srcPath, 'index')
}


// webpack.dev.js
const path = require('path')
const commonConfig = require('./webpack.common')
const {merge} = require('webpack-merge')
const devConfig = {
  mode: 'development', // 开发环境
  output: {
    filename: 'bundle.js',  // 输出文件名
    path: path.join(__dirname, '..', 'dist')  // 输出目录
  }
}
module.exports = merge(commonConfig, devConfig)


// webpack.prod.js

const path = require('path')
const commonConfig = require('./webpack.common.js')
const {merge} = require('webpack-merge')

const prodConfig = {
  mode: 'production', // 生产环境
  output: {
    filename: 'bundle.[chunkhash].js',  // 输出文件名,一般要加上hash
    path: path.join(__dirname, '..', 'dist')  // 输出目录
  }
}

module.exports = merge(commonConfig, prodConfig)
  1. package.json脚本中区分不同环境的启动命令
    先要安装:npm install webpack-dev-server -D
    PS:在webpack4中开发环境使用的命令是webpack-dev-server --config config/webpack.dev.js,在webpack5后,改成了webpack server
"scripts": {
    "build": "webpack --config config/webpack.prod.js",
    "dev": "webpack server --config config/webpack.dev.js"
  },
  1. 运行npm run build,打包
// dist/bundle.004799ddc36231aeaad8.js
console.log("hello");

html-webpack-plugin 自动创建index.html文件

打包的js文件,需要我们有一个载体来查看结果,也就是我们一般说的模板文件index.html

  • 手动创建一个index.html(根目录)文件



  
  
  
  Document


  


使用浏览器打开,可以看到正常输出的结果


dev

这个载体文件,不单单是开发环境需要,生产环境也是需要的,但是生产环境我们往往会生成不同的hash值来防止资源缓存,所以每次生成的文件名都是不同的,手动引入入口文件往往不太现实,所以需要借助html-webpack-plugin插件来自动生成项目中的html页面,并实现自动导入对应脚本

  • html-webpack-plugin 自动创建index.html文件
  1. 安装: npm i html-webpack-plugin -D
  2. plugins中使用,因为是公用的,所以写在webpack.common.js配置文件中:
// webpack.common.js 公共的配置
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const htmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: path.join(srcPath, 'index'),
  plugins: [
    new htmlWebpackPlugin({
      // 以根目录中的index.html文件作为模板来自动生成dist/index.html文件
      // 注意,这里是相对根目录而言的,因为脚本的上下文是在根目录下
      template: 'index.html'  
      // inject: 'head', // 脚本注入的位置,可以是head, body,或者为 false默认
      // 在这里还可以自定义参数,在模板中,使用ejs方式 <%= htmlWebpackPlugin.options %>获取自定义属性
      title: 'webpack demo',
      // webpack5默认开启压缩属性,webpack4要手动设置
      // minify: {
      //   removeComments: true, // 删除注释
      //   collapseWhitespace: true  // 删除空格
      // }
    })
  ]
}
  1. 删除根目录下index.html模板中手动导入的语句,并接收自在htmlWebpackPlugin设置的自定义参数



  
  
  
  <%= htmlWebpackPlugin.options.title %>


  


  1. 执行打包命令npm run build,可以看到dist目录下生成了index.html
    a. 自动导入了打包后的文件
    b. 显示了自定义参数的title标题
    c. 默认开启了压缩,删除了注释和空格
webpack demo

clean-html-plugin 删除之前的打包文件

随便修改下入口文件,发现打包新的文件时,之前生成的打包文件每次都会存在,所以我们往往配合clean-html-plugin,在每次生成新的打包文件之前,会先删除之前的打包文件

  1. 安装 npm i clean-webpack-plugin -D
  2. webpack.common.js中配置
// webpack.common.js 公共的配置
const path = require('path')
const srcPath = path.join(__dirname, '..', 'src')
const htmlWebpackPlugin = require('html-webpack-plugin')
// webpack5写法
const {CleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = {
  entry: path.join(srcPath, 'index'),
  plugins: [
    new CleanWebpackPlugin(), // 清除之前的打包文件
    new htmlWebpackPlugin({
      // 以根目录中的index.html文件作为模板来自动生成dist/index.html文件
      // 注意,这里是相对根目录而言的,因为脚本的上下文是在根目录下
      template: 'index.html'  
    })
  ]
}

PS:webpack4中导入插件的写法为const CleanWebpackPlugin = require('clean-webpack-plugin')

  1. 再次打包时,就发现之前的包被清除了

定义全局变量

我们通常会有个配置文件,需要根据环境的不同,配置不同的接口地址,这时一般就会用到全局变量,webpack可以使用DefinePlugin插件来设置全局变量

我们一般在package.json文件运行脚本中,会自定义一个环境变量NODE_ENV(,这个变量其实是自定义的,但前端约定俗成用这个变量名):

// mac电脑
"scripts": {
    "build": "NODE_ENV=prod webpack --config config/webpack.prod.js",
    "dev": "NODE_ENV=dev webpack server --config config/webpack.dev.js NODE_ENV=development"
  },
// windows电脑
"scripts": {
    "build": "set NODE_ENV=prod && webpack --config config/webpack.prod.js",
    "dev": "set NODE_ENV=dev && webpack server --config config/webpack.dev.js NODE_ENV=development"
  },

然后在其它脚本中,我们就可以通过process.env.NODE_ENV来获取

为了写一个脚本配置,适配所有电脑,所以要安装cross-env解决跨平台问题:
npm i cross-env -D,这样,就可以只使用一个脚本:

"scripts": {
  "build": "cross-env NODE_ENV=prod webpack --config config/webpack.prod.js",
  "dev": "cross-env NODE_ENV=dev webpack server --config config/webpack.dev.js"
},

然后在webpack.common.js中,打印process.env.NODE_ENV可以获取到脚本中的设置值,注意,这个process.env只有在配置文件中才能获取到,在其它index.js中是获取不到的,除非设置了全局变量

// webpack.common.js
 plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
    })
  ]

webpack4之后,上面这些操作,webpack都帮我们内置了,所以可以在任何js文件中都能获取到process.env.NODE_ENV的值,这个值对应的是webpack配置文件中的mode值,即developmentproduction

这里主要是说明怎么接收命令行的参数,并在DefinePlugin设置全局变量的方法,方便我们在模块中使用,但注意的是,设置的对象需要使用JSON.stringify转换,包括字符串(否则字符串会被识别成变量而报错)

开发环境启动和配置

之前配置了开发环境脚本,但一直没用,现在有了index.html模板文件和打包后的js文件,就可以使用npm run dev启动命令,根据提示打开浏览器,访问http://localhost:8080/就可以查看结果了

dev启动

跨域配置

在开发环境最常用的配置是处理跨域问题,在webpack.dev.js中设置devServer,这点和webpack4是一样的

// webpack.dev.js

const path = require('path')
const commonConfig = require('./webpack.common')
const {merge} = require('webpack-merge')
const devConfig = {
  mode: 'development', // 开发环境
  output: {
    filename: 'bundle.js',  // 输出文件名
    path: path.join(__dirname, '..', 'dist')  // 输出目录
  },
  devServer: {
    // 设置代理
    proxy: {
      // 将本地 /api/xxx 代理到 localhost:6666/api/xxx
      // '/api': 'http://localhost:3000',

      // 将本地 /api2/xxx 代理到 localhost:6666/xxx,通常用这个,/api2仅作为代理转发的标识
      '/api2': {
          target: 'http://localhost:3000',
          changeOrigin: true,
          pathRewrite: {
              '/api2': ''
          }
      }
    }
  }
}

module.exports = merge(commonConfig, devConfig)

修改index.js文件,请求跨域的接口

function hello() {
  console.log('hello11')
}
fetch('/api2/data').then(res => {
  console.log(1111)
})
// 显示跨域
fetch('http://localhost:3000/data').then(res => {
  console.log(2222)
})
hello()

写个简单的express服务器来测试:

const express = require('express')
const app = express()

app.listen(3000, () => {
  console.log('server is running in port: 3000');
})
app.get('/data', (req, res) => {
  console.log('11111')
  res.send('hello')
})

然后启动开发环境:npm run dev

image.png

image.png

文件资源处理

.js文件中导入import './style/index.css'样式文件,运行,发现会报错,这是因为webpack默认只认识js文件,其它类型的文件都不识别。
要使webpack识别它们,必须引入对应的loader去处理,将其转换成js认识的文件

1. 处理样式文件

识别.css文件

  1. 安装npm i css-loader style-loader -D
  2. webpack.common.js中配置module.rules规则:
    • css-loade:为了在js中使用import方式导入css文件
    • style-loader:将样式写在html文件中head标签内的