理解 Webpack3 路径配置项

2018/7/11

Webpack 中有很多涉及路径参数配置,若不知其所以然,很容易混淆出错。本文尽可能的汇集了 Webpack 中涉及路径的配置,力争深入浅出。

context

context 是 Webpack 编译时的基础目录,入口起点(entry)会相对此目录进行查找。

默认值为当前目录,Webpack 设置 context 默认值源码:

this.set("context", process.cwd());

process.cwd()即 Webpack 运行所在的目录(可以等同 package.json 所在目录)。

context 需配置为绝对路径,下面举例说明 context 配置对 entry 的影响。假设入口起点路径为src/web/main.js,那么 context 和 entry 可以做如下配置。

webpack-path
    |-- context
        |-- webpack.config.js
        |-- src
            |-- admin    
            |-- web
                |-- main.js

context 和 entry 的配置关系如下。

{
    context: path.resolve(__dirname, './src/'),
    entry: './web/main.js'
}
{
    context: path.resolve(__dirname, './src/web'),
    entry: './main.js'
}
{
    context: path.resolve(__dirname, './src/admin'),
    entry: '../web/main.js'
}

对于最后一个,虽然实际使用中一般不会这样配置,但可以非常充分的说明,一旦配置了 context,那么在配置 entry 的时候,就需要相对于 context 配置的路径去设置。

context 有什么实际作用?官方文档的解释是使得你的配置独立于工程目录 「This makes your configuration independent from CWD (current working directory)」。怎么理解?举个例子,在分离开发生产配置文件时候,一般把 webpack.config.js 放到 build 文件夹下,此时 entry 却不用考虑相对于 build 目录来配置,只需根据 context 的配置来设置。

entry 若配置为绝对路径,则和 context 没关系

output 的配置项和 context 没有关系,但是有些插件的配置项和 context 有关,后面会说明。

output

output.path

打包文件输出的目录,必须配置为绝对路径,默认值和 context 的默认值一样,都是process.cwd()

除了常规的配置方式,还可以在 path 中用使用 [hash] 模板,比如配置:

output: {
    path: path.resolve('./dist/[hash:8]/'),
    filename: '[name].js'
}

打包后的目录如下:

这里的 hash 值是编译过程的 hash,如果被打包进来的内容改变了,那么 hash 值也会发生改变。这个可以用于版本回滚。你也可以配置为path.resolve(./dist/${Date.now()}/)方便做持续集成等。

ouput.publicPath

记得最开始学习 Webpack 的时候,一直没把 publiPath 理解透彻,甚至还错误的认为它和 output.path 有关联。这个配置项在很多场景是非常重要的,如果不深入理解,就不能灵活运用。

怎么快速又准确的理解 publicPath,我觉得可以用下面的这个公式来表述:

静态资源最终访问路径 = output.publicPath + 资源 loader 或插件等配置路径

举例说明:

output.publicPath = '/static/'

// 图片 url-loader 配置
{
    name: 'img/[name].[ext]'
}
// 那么图片最终的访问路径为
output.publicPath + 'img/[name].[ext]' = '/static/img/[name].[ext]'

// JS output.filename 配置
{
    filename: 'js/[name].js'
}
// 那么JS最终访问路径为 
output.publicPath + 'js/[name].js' = '/static/js/[name].js'

// CSS 
new ExtractTextPlugin("css/style.css")
// 那么最终CSS的访问路径为
output.publicPath + 'css/style.css' = '/static/css/style.css'

publicPath 默认值为空字符串,接下来看其他各种常见的 publicPath 配置的实际意义。

publicPath 设为相对路径,举例说明。

{
    context: path.resolve(__dirname, './src'),
    entry: './main.js',
    output: {
        path: path.resolve(__dirname, `./static/`),
        filename: 'js/[name].js',
        publicPath: '../static/'
    }
}

在上面的配置,publickPath 配置为相对路径,按照公式,那么 main.js 访问的路径为:

静态资源最终访问路径 = output.publicPath + 资源 loader 或插件等配置路径

main.js 访问路径 = '../static/' + 'js/main.js' = '../static/js/main.js'

如何理解最终的路径,其实它这决定了引用资源页面的路径,比如要使得 index.html 能够正确引用 main.js,那么从访问 main.js 相对路径反推,index.html 需要放在 static 目录下或者和其平级的某个文件夹下下才能正常访问。

放在 static 目录
|-- static
    |-- index.html
    |-- js
        |-- main.js 

和 static 平级的某个文件夹下
|-- static
|   |-- js
|       |-- main.js 
|
|-- page
    |-- index.html

实际开发中一般都不会使用相对路径,相对路径的直接以文件方式访问(file://),比如一些混合 APP 就用的这种方式,此时就不适合使用绝对路径了。

publicPath 设为 http 地址(http://),比如publicPath:'http://wwww.qinshenxue.com/static/',适合资源托管到 CDN 的情况,比如公司的静态资源服务器等。

publicPath 应该以/结尾,同时其它 loader 或插件的配置不能以/开头。

webpack-dev-server

publicPath

上面讲过 webpack 的 publicPath,那么这里的 publicPath 和 上面的publicPath是不是一回事呢?答案是两者区别很大,首先这里的 publicPath 用于开发环境的,那这两者到底有啥区别呢?

我们知道 webpack-dev-server 打包的内容是放在内存中,通过 express 匹配请求路径,然后读取对应的资源输出。这些资源对外的根目录就是 publicPath,可以理解为和 outpu.path 的功能一样,将所有资源放在此目录下,在浏览器可以直接访问此目录下的资源。

但是这个路径仅仅只是为了提供浏览器访问打包资源的功能,Webpack 中的 loader 和插件仍然是以 ouput.publicPath 为准,这也是为什么官方推荐 publicPath 和 webpack 配置的保持一致(除了 http 地址),配置一致才能正常访问其它静态资源。

上面的解释可能还是比较生硬,还是举几个例子来说明:

本例将两处 publicPath 配置成不一样的,这样更容易对比理解。

// webpack.config.js
output: {
    path: path.resolve(`./dist/`),
    filename: 'js/[name].js',
    publicPath: '/static/'
}
// api 调用 webpack-dev-server
var webpack = require('webpack');
var webpackDevServer = require('webpack-dev-server');
var config = require("./webpack.config");
var compiler = webpack(config);
var server = new webpackDevServer(compiler, {
    hot: true,
    publicPath: '/web/'
});
server.listen(8282, "0.0.0.0")

如何查看 webpack-dev-server 所有启动后的资源访问路径呢?有个简单的方法,就是访问/webpack-dev-server,本例访问截图如下:

上面的资源可以点击查看,你会发现,资源的路径都是/web/,所以在index.html中引入JS的路径应为/web/js/main.js,同时也能看到,style.css中的图片路径仍然为/static/img/*.png,而不是/web/。

html-webpack-plugin

这个插件的几处配置受路径配置影响,因此需要专门说明下。

template

template的路径是相对于 output.context,源码如下:

this.options.template = this.getFullTemplatePath(this.options.template, compiler.context);

因此 template 对应的文件需要放在 ouput.context 配置的目录下才能被识别。

filename

filename的路径是相对于 output.path,源码如下:

this.options.filename = path.relative(compiler.options.output.path, filename);

在 webpack-dev-server 中,则相对于 webpack-dev-server 配置的 publicPath。

若 webpack-dev-server 中的 publicPath 和 ouput.publicPath 不一致,在这种配置下使用html-webpack-plugin是有如下问题:

这两个问题也反推出了最方便的配置为:

原创文章,持续完善中,转载请注明出处。本文地址: https://www.qinshenxue.com/article/20170315092242.html
Star支持