Skip to main content

path 路径

概述

webpack的打包配置中,一般会使用到pathpublicPathcontentBase来配置资源路径,那么三者分别代表什么含义呢?三者的含义简要概述如下:

  • path在生产环境(production)中代表打包后资源在项目中的存放目录,在开发环境(development)中可以省略
  • publicPath在生产环境一般用来CDN资源添加前缀,在开启 webpackDevServer 的开发环境中代表打包后资源在内存中存放的相对路径,默认为项目根目录
  • contentBase是 devServer 对象中的一个字段,决定了 webpackDevServer 启动时服务器资源的根目录,默认是项目的根目录

下面我们将结合一个demo来分别阐述三者的具体含义。在该 demo 中可以执行npm run build进入生产环境,执行npm run dev后进入开发环境。demo 中的package.json配置如下所示:

./package.json
{
//...
"scripts": {
"build": "webpack --config webpack.prod.js",
"dev": "webpack-dev-server --config webpack.dev.js --open"
}
//...
}

path

path一个绝对路径,代表打包在本地磁盘上的物理位置

webpack.prod.js
module.exports = {
//...
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
}
//...
};

path字段的配置在生产环境下是必须的,因为我们需要为webpack指明打包路径,但是在开发环境下不是必须的,因为当使用 webpackDevServer时打包出来的文件都在内存中而没有打包到磁盘,即使指定了输出目录,输出目录也是空的。

  • 在生产环境中配置./dist为输出目录, 执行npm run build后文件都被打包到了指定的./dist目录,打包过程如下所示:
  • 在开发环境中配置./dist为输出目录, 执行npm run dev启动webpackDevServer后发现./dist目录一直为空,打包过程如下所示:

publicPath

output.publicPath

output中的publicPath可以分为两种情况:

  1. 绝对路径:通常用在生产环境中
    1. 将 webpack 的打包好的静态资源进行 CDN 托管,通过设置publicPath添加 CDN 前缀
    2. 也可以不用 CDN,将其直接指向服务器上的静态资源目录,如 /static/, /public/, /build/, /dist/ 等。
  2. 相对路径:通常用在开发环境中,通过设置publicPath添加相对路径前缀,在开启webpackDevServer时,指明打包后的资源路径,默认路径为项目的根目录,如 ./build/, ./dist/,一般保持默认即可。
caution

需要注意的是,publicPath 都是以‘/’结尾。

  • 生产环境

在生产环境下,如果我们不设置publicPath,资源的引用会采用相对路径引用。但有时候我们可能会利用 CDN 来托管这些资源,此时我们就需要为引用的资源添加 CDN 的 url 前缀。例如在生产环境中我们可以配置如下:

webpack.prod.js
module.exports = {
//...
module: 'development',
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
publicPath: "http://localhost:8080/", // 假设其为CDN前缀
}
//...
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
})
}
//...

在 demo 中的index.less中我们引入了./images/logo.png,当我们不设置output.publicPath时,样式文件的打包结果如下:

tip

如果 index.less 里引用的资源路径是 / 开头的绝对路径,打包将不受 webpack 配置的影响,始终指向的是服务器静态资源的地址,file-loader, url-loader 也不会起作用,其只对相对路径有效。

设置output.publicPathhttp://localhost:8080/时,打包结果如下:

当不设置output.publicPath时,包后的index.html文件如下:

设置output.publicPathhttp://localhost:8080/时,打包后的index.html文件如下:

通过打包结果可以看出,在生产环境中,我们可以利用publicPath来为系统引用资源设置公共的 CDNurl前缀。

  • 开发环境

下面我们结合 demo,着重讲解在开发环境中output.publicPath的表现。

我们不为publicPath赋值,让publicPath取默认值,因此我们期望打包后,所有文件都可以被打包到根目录,以/为前缀。

webpack.dev.js
module.exports = {
//...
module: 'development',
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
// publicPath: "/dist/",
}
//...
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
})

//先注释掉devServer.contentBase
devServer: {
// publicPath: "/dist/"
}
}

当我们执行npm run dev后,将启动 webpackDevServer,运行过程如下图所示:

演示1:output.publicPath默认值

由演示的结果可以看出,打包后的静态资源search.htmlindex.js和图片本质上都是以./为前缀,还是相对路径,这证明了在默认的情况下,output.publicPath代表着项目根目录

为了进一步的证明,我们还可以将output.publicPath改为/dist/(假设 /dist/ 为服务器的静态资源目录,需要服务器配置),我们期望所有的静态资源都会添加/dist/前缀。运行过程如下图:

演示2:output.publicPath为特定值

通过图中可以看出,所有的静态资源都是以/dist/为前缀。

devServer.publicPath

在开启 webpackDevServer 时浏览器中可通过该路径访问 bundled 文件,静态文件前会加上这个路径前缀devServer.publicPathoutput.publicPath的功能相似,两者之间的联系如下:

  1. 若两者都不设置的话,那所有的静态文件都以“/”为前缀。
  2. devServer.publicPath没有设置,则默认为output.publicPath的值。
  3. output.publicPath的值没有设置,则所有静态文件以devServer.publicPath值为前缀。
  4. 若两者都有设置且不相同,则使用loader打包出的静态文件以output.publicPath的值为前缀,html文件以devServer.publicPath为前缀。
tip

从上面两者的联系可以看出,我们最好只设置output.publicPath的值,或者两者设置为相同的值,不然两者关系太复杂。

上述结论的12在前面的案例中已经涉及,分别对应演示 1演示 2,此处不再细述,下面重点说下34

  • output.publicPath的值没有设置,则所有静态文件以devServer.publicPath值为前缀,也就是说所有的静态资源会打包到devServer.publicPath的路径下。如下例所示:
webpack.dev.js
module.exports = {
//...
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
// publicPath: "/test/",
}
//...
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
})

//先注释掉devServer.contentBase
devServer: {
publicPath: "/dist/",
// contentBase: './src',
}
}

此时打包过程如下图所示:

演示3:devServer.publicPath为特定值

从打包结果可以看出,所有的文件都是以"/dist/"为前缀。

  • 为了验证结论第 4 条,我们将上面配置中output.publicPath的注释取消,打包结果如下:

演示4:devServer.publicPath与`output.publicPath`的值不相同

通过打包过程可以看出,search.htmldevServer.publicPath值为前缀,而其对应的index.js却以output.publicPath值为前缀。除此之外打包的页面也没能正常显示,因此在设置这两个值的时候,最好两者保持一致,或者只设置output.publicPath即可

contentBase

devServer.contentBase决定了 webpackDevServer 启动时服务器资源的根目录,默认是项目的根目录contentBase 不会影响 pathpublicPath它唯一的作用就是指定服务器的根目录来引用静态文件

前面的例子中已经展示了,在不设置contentBase时,项目中所有的一级目录都会默认被列举在http://localhost:8080/页面中,如下图所示:

为了验证contentBase可以设置服务器资源目录,我们可以将devServer.contentBase设置如下

webpack.dev.js
module.exports = {
//...
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
publicPath: "/dist/",
}
//...
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
})

//先注释掉devServer.contentBase
devServer: {
contentBase: './dist',
}
}

因为在开发环境执行npm run dev./dist为空目录,为了证明首页目录真的是./dist,我们可以在./dist新建一个文件i-am-dist.html,打包结果如下图所示:

由上图可知服务器资源的目录为./dist,当我们在地址栏输入dist/search.html时页面可以正常显示,这也说明contentBasepublicPath其实没啥关系,互不影响。

参考资料

  1. Webpack 中 path/publicPath/contentBase 的关系,by fi3ework
  2. webpack 配置文件中 publicPath 和 contentBase 傻傻分不清, by 小飞猫_
  3. webpack 配置 publicPath 的理解, by SamWeb
  4. webpack 中的热更新及原理分析,by 程柳锋