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(根目录)文件
<!DOCTYPE html>
<html lang="en">
<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">
  <title>Document</title>
</head>
<body>
  <script src="./dist/bundle.004799ddc36231aeaad8.js"></script>
</body>
</html>

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


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设置的自定义参数
<!DOCTYPE html>
<html lang="en">
<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">
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
  <!-- 这是注释 -->
</body>
</html>
  1. 执行打包命令npm run build,可以看到dist目录下生成了index.html
    a. 自动导入了打包后的文件
    b. 显示了自定义参数的title标题
    c. 默认开启了压缩,删除了注释和空格
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>webpack demo</title><script defer="defer" src="bundle.e97c675afbdcfa8f531d.js"></script></head><body></body></html>

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标签内的<style>标签中
module.exports = {
  entry: path.join(srcPath, 'index'),
   module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
    ]
  },
  },

识别.less文件(sass、stylus同理)

要在.js中使用import './src/style/xxx.less'文件:

  1. 安装npm i css-loader style-loader less-loader less -D
  2. 配置
    • less-loader:将.less文件转译成.css文件
module: {
    rules: [
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  },

自动添加hack前缀

  1. 安装npm i postcss-loader autoprefixer -D
  2. webpack.common.js中配置
module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader']
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader','postcss-loader',  'less-loader']
      }
    ]
  },
  plugins: [
    require('autoprefixer')
  ]

CSS文件抽离

在生产环境中,我们往往不会将样式文件给打包到html文件中,而是单独抽离css文件

  1. 安装:npm i mini-css-extract-plugin -D
  2. 在生产环境配置文件中webpack.prod.js配置,将前面的 style-loader换成mini-css-extract-pluginloader
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const prodConfig = {
  mode: 'production', // 生产环境
  output: {
    filename: 'bundle.[chunkhash].js',  // 输出文件名,一般要加上hash
    path: path.join(__dirname, '..', 'dist')  // 输出目录
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
      },
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name]-[contenthash].css'
    })
  ]
}
  1. 打包npm run build
    css抽离

2. 图片资源文件

想在.css中使用url导入图片

body {
  background-color: yellow;
  opacity: 0.7;
  transform: rotate(45deg);
  background: url('../imgs/img002.jpg');
}

或者在.js中使用import导入图片

import img from './imgs/img002.jpg'

需要借助file-loader或者url-loaderurl-loaderfile-loader差不多,只不过增加了一个设置文件大小阈值,当小于这个阈值时,使用base64导入文件

  • 开发环境
  1. 安装:npm i file-loader -D
  2. 在开发环境,因为不用考虑压缩问题,所以webpack.dev.js可以配置file-loader
// 处理css样式文件中的url图片
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        use: ['file-loader']
      }
  • 生产环境
  1. 安装: npm i url-loader -D
  2. 在生产环境,可以使用url-loader设置小图片使用base64方式写入
// 处理css样式文件中的url图片
{
        test: /\.(jpg|png|jpeg|gif)$/,
        use: {
          loader:'url-loader',
          options: {
            limit: 8192,    //限制 8kb 以下使用base64
            esMoudle: false,
            name: '[name]-[hash:8].[ext]',
            // 打包到/images目录下
            outputPath: 'images'
          }
        }
        
      }

解析ES6+代码

  • 安装: npm i babel-loader @babel/core @babel/preset-env -D
  • 配置:
// webpack.common.js
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader'], // 1. 开启缓存
        include: srcPath, // 2. 缩小适合范围(写include或exclude)
        exclude: /node_modules/
        }
    ]
  }

// 根目录创建,.babelrc
{
  "presets": ["@babel/preset-env"],  // 一般使用@babel/preset-env就够了
  "plugins": []
}

// 如果是高版本的babel,比如v7.8.0 或更高版本的babel等
// .babelrc 换成根目录创建 babel.config.json
{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.5"
      }
    ]
  ]
}

总的来说,webpack5对比webpack4来说,改动不算太多,还算是比较平稳过渡,最后,借用网上一张图,说明webpack4升级webpack5的改动:

webpack5与webpack4差异.png

参考:
https://blog.csdn.net/xiaolinlife/article/details/107032533
https://www.imooc.com/article/287156#comment

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容