webpack中极容易混淆的path、publicPath、contentBase配置

最近在写一个 Webpack + React 的 Demo,关于涉及路径参数配置很容易混淆,所以写这篇文章整理一下。

下面关于 webpack-dev-server 会简写成 dev-server 或者 devServer。

基本配置

写一个简单的项目,没有过多的东西,足以说明本文的几个配置项即可,目录结构如下:

1
2
3
4
5
6
7
my-project
  src
    index.css // 仅设置了一个背景图片
    index.html // 简单的一个 HTML 作为模板而已,随意写个 div 标签啥的就 OK 了
    index.js // 仅引用了 index.css
  package.json
  webpack.config.js

项目里面 index.js、index.css、index.html 里面其实没什么内容的,就如上面备注一样而已。

1
2
3
4
5
6
7
// package.json
{
  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// webpack.config.js
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    open: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'webpack demo',
      template: './src/index.html',
      filename: 'index.html',
      inject: true,
      hash: false
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: 'style.css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]'
        }
      }
    ]
  }
}

1. output.path

所有输出文件的目标路径。它是一个绝对路径,默认是项目根目录下的 dist 路径。

简单地说,就是运行 yarn run build 命令,webpack 将项目打包之后的文件(如 index.html、bundle.js、图片等)输出到该目录。这个还是比较好理解的。

2. output.publicPath

output.publicPath 常用于在生产环境。它会为所有的资源指定一个基础路径。设置后会为资源添加一个前缀。

我们修改一下 output.publicPath 配置,如下:

1
2
3
4
5
6
7
8
9
// webpack.config.js
const path = require('path')
module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/outputDir/'
  }
}

运行 yarn run dev 命令,可以看到命令行显示如下信息:

1
2
? ?wds?: Project is running at http://localhost:8081/
? ?wds?: webpack output is served from /outputDir/

然后,访问 http://localhost:8080/ 结果如下:

访问 http://localhost:8080/outputDir/ 结果如下:

根据上面两张图可以看出,设置 output.publicPath 后,如果 devServer.publicPath 没有设置,那么使用 webpack-dev-server 进行打包时生成的静态文件所在的位置以及 index.html 文件里面引用资源的前缀都是 output.publicPath 里面设置的值。

devServer.publicPath

接着,我们修改一下 devServer.publicPath 的配置,如下:

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js
module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/outputDir/'
  },
  devServer: {
    publicPath: '/assets/'
    open: true
  }
}

执行 yarn run dev 命令 ,命令行显示如下信息(我们看到跟此前不一样了):

1
2
? ?wds?: Project is running at http://localhost:8080/
? ?wds?: webpack output is served from /assets/

然后,访问 http://localhost:8080/ 结果如下:

访问 http://localhost:8080/assets/ 结果如下:

我们发现 JS 和 CSS 文件的引用路径还是没变,但是我们发现页面的背景图片没有了,因为它报错了,CSS 和 JS 都找不到,如下:

image.png

可以看出,devServer.publicPath 表示打包生成的静态文件所在的位置。并且它的优先级是最高的。而 output.publicPath 表示 index.html 文件里面引用资源的前缀。

devServer.contentBase

接着,添加 devServer.contentBase 配置,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// webpack.config.js
module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/outputDir/'
  },
  devServer: {
    contentBase: './aaa',
    publicPath: '/assets/',
    open: true
  }
}

执行 yarn run dev 命令,命令行显示信息如下:

1
2
3
? ?wds?: Project is running at http://localhost:8080/
? ?wds?: webpack output is served from /assets/
? ?wds?: Content not from webpack is served from ./aaa

可以发现有一条是:Content not from webpack is served from ./aaa,可以看出 devServer.contentBase 指的是,不由 webpack 打包生成的静态文件

访问 http://localhost:8080/ 结果如下:

因为 http://localhost:8080/ 下并没有 aaa 目录,所以根本找不到。而前面没有设置 devServer.contentBase 的时候,会使用 contentBase 的默认值(当前执行的目录,即项目根目录)。在访问 http://localhost:8080/ 时,由于在根目录下没有找到 index.html 文件,因此会显示根目录下的资源文件。

访问 http://localhost:8080/assets/,结果如下:

可见,devServer.contentBase 与打包生成的静态文件所在的位置和 index.html 里面引用资源的前缀是没有影响的。

接着,我们再修改一下 devServer.contentBase 的配置,将其设置为 src 目录,而该目录下是有我们编写的 index.html 模板文件的。

1
2
3
4
5
6
7
8
9
10
11
12
13
// webpack.config.js
module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/outputDir/'
  },
  devServer: {
    contentBase: './src',
    publicPath: '/assets/',
    open: true
  }
}

访问 http://localhost:8080/,结果如下:

可以看出,访问的是我们本地编写的 index.html 文件。请注意,这个不是 webpack 打包生成的 index.html 文件。


未完待续...

参考

  • webpack 中的 path、publicPath 和 contentBase
  • Webpack 中 publicPath 详解