随着 vue-cli 等脚手架工具对webpack的集成度越来越高,竟然项目都做完了还浑然不知自己对于打包进行了哪些配置和优化。基于这种现状自己动手尝试了一下对于打包的配置,这个过程中确实遇到了一些傻傻分不清楚的问题,个人觉得 webpack 官网的中文翻译有些地方比较晦涩,也可能是自己水平有限理解有限,这个暂且不说了唯有多学习来提高。
本文记录了在配置过程中通过反复尝试,对于 output.publicPath、output.path、devServer.publicPath、devServer.contentBase 的一些理解,因为这几个配置项如果使用错误通常会在请求资源时出现404的情况。
devServer
需要提到的一点是:webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 devServer 配置中的
publicPath
选项进行修改。(注:serve,将资源作为server的可访问文件)
devServer.publicPath
顾名思义,devServer 的各种配置只在development
模式下有效。根据官方文档 publicPath 默认值为/
,所以,假设服务器运行在http://localhost:8080
并且output.filename
被设置为bundle.js
,我们可以通过http://localhost:8080/bundle.js
来访问 bundle 文件。
也可以修改devServer.publicPath
,将 bundle 放在指定目录下:
module.exports = {
// ...
devServer: {
publicPath: '/assets/'
}
}
现在可以通过http://localhost:8080/assets/bundle.js
访问bundle。
确保
devServer.publicPath
总是以斜杠/
开头和结尾。
webpack-dev-server 可以看作是一个服务者,它的主要工作就是接收浏览器的请求,然后将资源返回。当服务启动时,会先让 webpack 进行模块打包并将资源准备好。当 webpack-dev-server 接收到浏览器的资源请求时,它会首先进行 URL 地址校验。如果该地址是资源服务地址(上面配置的publicPath),就会从 webpack 的打包结果中寻找该资源并返回给浏览器。反之,如果请求地址不属于资源服务地址,则直接读取硬盘中的源文件并将其返回。
对于上述我们做一个简单的验证:
配置文件如下:
项目目录结构如下:
在浏览器中访问http://localhost:8081/girl.jpeg
,因为该地址并非 publicPath 配置的资源服务地址,所以直接读取硬盘中的源文件进行返回。
综上所述:publicPath
字段的意义应该比较清楚了,即在浏览器中能够访问到的构建文件的路径。
devServer.contentBase
对于contentBase的解释官网描述为:告诉服务器从哪个目录中提供内容,只有在你想要提供静态文件时才需要。
其实就是 index.html 所在的目录,如果不配置默认为项目根目录。
下面来验证一下:
浏览器中访问:
http://localhost:8081
,展示如下:
我们将 contentBase 修改为contentBase: path.join(__dirname, 'public')
,继续访问http://localhost:8081
,展示如下(项目public目录中有favicon.ico和girl.jpeg两个资源):
output
output.publicPath
output 也有一个 publicPath 属性,那和 devServer.publicPath 有没有关联呢?答案是有的,如果 devServer.publicPath 没有设定值,那么开发服务器就会尝试去读取 output.publicPath 的值,如果 output.publicPath 显式设定了值则使用这个值,如果没有设定值就使用 devServer.publicPath 的默认值/
。
那我们现在来看一下只给output.publicPath
设定值,而不给devServer.publicPath
设定值会是什么情况,比如设定output.publicPath: /assets/
接下来,同时给二者设定不同的值,比如设定devServer.publicPath: /path/
但是,这两个值不同会带来其他的问题:
浏览器中访问的是http://localhost:8081/path/index.html
,但是,script 标签引用的资源文件前缀是assets
,也就是说以output.publicPath
的值为准。
其实,publicPath 是一个非常重要的配置项,并且容易与 path 相混淆。从功能上来说,path 用来指定资源的输出位置,而 publicPath 则用来指定资源的请求位置。
-
输出位置:打包完成后资源产生的目录,一般将其指定为工程中的dist目录。
-
请求位置:由 JS 或 CSS 所请求的间接资源路径。页面中的资源分为两种,一种是由 HTML 页面直接请求的,比如通过 script 标签加载的 JS ;另一种是由 JS 或 CSS 请求的,如异步加载的 JS、从 CSS 请求的图片字体等。publicPath 的作用就是指定这部分间接资源的请求位置。
publicPath 有三种形式,下面逐一介绍:
-
HTML相关
与 HTML 相关,也就是说我们可以将 publicPath 指定为 HTML 的相对路径。在请求这些资源时会以当前页面 HTML 所在路径加上相对路径,构成实际请求的URL。如:
// 假设当前HTML地址为 https://example.com/app/index.html // 异步加载的资源名为 0.chunk.js publicPath: '' // 实际路径 https://example.com/app/0.chunk.js publicPath: './js' // 实际路径为 https://example.com/app/js/0.chunk.js publicPath: '../assets/' // 实际路径为 https://example.com/assets/0.chunk.js
-
Host相关
若 publicPath 的值以
/
开始,则代表此时 publicPath 是以当前页面的 host name 为基础路径的。如:// 假设当前HTML地址为 https://example.com/app/index.html // 异步加载的资源名为 0.chunk.js publicPath: '/' // 实际路径 https://example.com/0.chunk.js publicPath: '/js/' // 实际路径为 https://example.com/js/0.chunk.js publicPath: '/dist/' // 实际路径为 https://example.com/dist/0.chunk.js
-
CDN相关
上面两种配置都是相对路径,我们可以使用绝对路径的形式配置 publicPath。这种情况一般发生于静态资源放在 CDN上 面时,由于其域名与当前页面域名不一致,需要以绝对路径的形式进行指定。当 publicPath 以协议头或相对协议的形式开始时,代表当前路径是 CDN 相关。如:
// 假设当前HTML地址为 https://example.com/app/index.html // 异步加载的资源名为 0.chunk.js publicPath: 'http://cdn.com/' // 实际路径 http://cdn.com/0.chunk.js publicPath: 'https://cdn.com/' // 实际路径为 https://cdn.com/0.chunk.js publicPath: '//cdn.com/assets/' // 实际路径为 //cdn.com/assets/0.chunk.js
output.path
path 可以指定资源输出的位置,要求值必须为绝对路径。如:
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
}
}
上述配置将资源输出位置为工程的 dist 目录。在 webpack v4 以前的版本中,打包资源默认会生成在工程根目录,因此我们需要上述配置;而在 webpack v4 之后,output.path 已经默认为 dist 目录,除非我们需要更改它,否则不必单独配置。
结合devServer.publicPath(指定webpack-dev-server的静态资源服务路径),我们看下面的例子:
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
devServer: {
publicPath: '/assets/',
port: 3000
}
}
从上面可以看到,webpack配置中 output.path 为 dist 目录,因此 bundle.js 应该生成在 dist 目录。但是当我们启动webpack-dev-server的服务后,访问 localhost:3000/dist/bundle.js
时却会得到404.这是因为devServer.publicPath
将资源位置指向了localhost:3000/assets/
,因此只有访问localhost:3000/assets/bundle.js
才能得到我们想要的结果。
为了避免开发环境和生产环境产生不一致而造成开发者的疑惑,我们可以将webpack-dev-server的publicPath与webpack中的output.path保持一致,这样在任何环境下资源输出的目录都是相同的。看下面的示例:
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
devServer: {
publicPath: '/dist/',
port: 3000
}
}
上面的配置可以保证访问localhost:3000/dist/bundle.js
时得到预期的结果。
本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情
参考:Webpack中文网