likes
comments
collection
share

关于Vue项目中publicPath的二三事

作者站长头像
站长
· 阅读数 269

引言

Vue 项目中,vue.config.js 是项目中用来做启动和打包的配置文件,其中有一个重要参数是 publicPath,这个配置的作用是在开发阶段和生产阶段,为静态资源配置统一的资源标识符。但是,有的时候,相对路径、绝对路径、二级路径傻傻分不清。再不然就是开发阶段应用跑的好好的,而一但到了生产环境,不是白屏就是图片资源丢了,总之就是各种问题。今天就来好好的battle一下,到底在各种各样的场景下,这个参数应该怎么配。


绝对和相对之分

在讲配置前,首先来明确两个概念绝对路径 '/' 和 相对路径 './'。这两个概念的区别就是以哪个目录作为起点,可以理解为绝对路径就是绝对错不了的路径,始终以一个固定的目录作为起点。而相对路径容易错,可以是任意一个目录节点作为起点。而在网站的访问中,可以认为如果静态资源使用了绝对路径,那么他始终以当前这个网站来作为访问的根节点。

在 vue.config.js 中,publicPath 既可配置成绝对路径 ,也可以配置成相对路径。

  • 绝对路径:

    publicPath: '/',
    // 或带有二级路径的网站, 如访问地址为 http://my-app.com/sub/
    publicPath: '/sub/'
  • 相对路径:

    publicPath: './',
    // 或者
    publicPath: '.',
    // 或者
    publicPath: ''

    以上三种是等效的,都是相对路径。


关于publicPath

首先使用 @vue/cli 的脚手架,生成一个vue项目,来看一下 publicPath 的默认配置是什么?以下代码均来自 @vue/cli@5.0.8。

生成的 vue.config.js关于Vue项目中publicPath的二三事可以看到在生成的项目中,publicPath 的参数值并不存在,那么这个默认值到底是什么呢?打开 @vue/cli 的源码 在 packages/@vue/cli-service/lib/options.js 文件下找到了答案。关于Vue项目中publicPath的二三事可以看到脚手架默认赋予的 publicPath 的值为 '/',也就是说 @vue/cli 默认使用的是绝对路径。


关于 router 中的 base 参数

除了publicPath 这个参数外,还有一个与之有紧密关系的参数就是 router 当中的 base 。关于Vue项目中publicPath的二三事打开 vue-router 的官方文档可以看到对应的解释,base 是应用的基路径,即当前应用的所有的路由地址,都会被自动拼接上这个基路径,一般应用在部署有二级目录的网站上。

router/index.js 中 base 的 默认值为 process.env.BASE_URL,那么这个初始值是什么呢?继续翻找 @vue/cli 的源码,在以下文件中找到了答案

关于Vue项目中publicPath的二三事目录:packages/@vue/cli-service/lib/config/base.js此文件是 @vue/cli 对 webpack 的配置文件,可以看到脚手架中的环境变量来自于 util/resolveClientEnv 这个工具函数,然后调用 webpack.DefinePlugin 的插件,将这些环境变量生成最终可访问的常量。

关于Vue项目中publicPath的二三事目录:packages/@vue/cli-service/lib/util/resolveClientEnv.js此文件是 @vue/cli 用来生成环境变量的工具函数,可以看到 BASE_URL 的 值来自 getBaseUrl 这个函数,而参数 options 就是我们的 vue.config.js 所导出的 json 文件。此外还可以看到在脚手架中默认只有 VUE_APP_开头、NODE_ENV、BASE_URL 这三种环境变量才可以被 @vue/cli 所识别。

关于Vue项目中publicPath的二三事目录:packages/@vue/cli-service/lib/util/.js可以看到当 publicPath 被设置为 'auto' 后其值是 '',而其他情况下,这个值就是我们所配置的publicPath,那基本可以理解为,publicPath 和 router中的base 参数,在实际项目的开发中理应是同一个值。

而这里单独处理 'auto' ,是为了适配 webpack,因为 webpack 的 output.publicPath 可以设置为 'auto' 并有其自己的处理逻辑。但一旦使用了 'auto' 这个值,在启动 serve 后,auto 就会被当成二级路径比如 http://localhost:8080/auto/,所以 @vue/cli 要在这里强制将其置空。

这个问题的讨论地址:[https://github.com/vuejs/vue-...](https://github.com/vuejs/vue-...)webpack 关于 publicPath 的文档:https://webpack.docschina.org/configuration/output/#outputpublicpath

好了,看明白了默认值以后,终于可以开始进入正题了,publicPath 分别在绝对和相对路径下应该怎么用。


publicPath: '/',使用绝对路径,即默认配置

开发环境关于Vue项目中publicPath的二三事生产环境关于Vue项目中publicPath的二三事

结论:使用默认配置,无论是在开发阶段还是生产阶段,静态资源都会被挂上 '/',这样静态资源就会以当前网站服务作为根节点,然后加载相应的静态资源。

本地静态访问关于Vue项目中publicPath的二三事

而这样打出来的 dist/index.html 也是无法直接访问的,因为使用了绝对路径,当直接使用浏览器打开时,会直接去到根目录的盘符下作为访问的根路径,导致静态资源加载失败。当然生产包打出来一般都会使用服务器进行部署,因为大多数应用还有和后台接口的交互,必须要通过服务器启动后才能正常访问。那么有没有不用服务器就能启动进行访问的么?这时候你可能需要的就是相对路径。


publicPath: './',使用相对路径

开发环境关于Vue项目中publicPath的二三事

生产环境关于Vue项目中publicPath的二三事

结论:使用相对路径,可以看到只是比绝对路径在静态资源访问上少了 '/',这样的优点就是可以让当前应用被部署在任意目录,而不必担心静态资源有丢失的情况。比如本地访问。

关于Vue项目中publicPath的二三事可以看到使用相对路径进行打包后,直接本地访问 index.html,静态资源都是加载成功的,页面上显示了一部分东西,但是 home 页面的内容却丢失了。这其实是因为我们使用的路由模式是 history,虽然资源加载没有问题,但是因为地址栏路径与前端设置的访问路由不一致,导致前端路由不能正确加载,因此页面不能正确显示。

关于Vue项目中publicPath的二三事把路由修改为 hash 模式。

关于Vue项目中publicPath的二三事可以看到,当修改了路由为 hash 模式后,再次进行打包,index.html 即便在本地也可以顺利的进行访问了,进行路由切换也毫无问题。


publicPath: '/sub/',采用二级路径 (也是绝对路径)

开发环境关于Vue项目中publicPath的二三事

生产环境关于Vue项目中publicPath的二三事结论:可以看到,二级路径其实也是绝对路径,只不过在所有静态资源前统一拼接上了 '/sub/'。这种配置可以在同一个域下部署不同的项目,依靠二级路径来做到逻辑上的划分。


为什么当网站配置了二级访问路径,前端也要加上相应资源标识符呢?

当使用绝对路径时,所谓根路径,其实是以当前 域名+端口号后面的位置开始的。如以http://localhost:8080/test 这个网址为例,假如此时前端配置的 publicPath: '/',即默认值。那么打出来的前端静态资源在被加载时,就会变成 http://localhost:8080/js/xxxx...,因为端口后才是当前网站的根目录。如此一来,静态资源在物理层面已经无法被访问到了,导致资源加载失败,页面白屏等。所以当网站设置了二级目录时,前端这时候就需要注意了,可以有不同的解决方案防止白屏。

  1. 设置带有二级目录的绝对路径,publicPath: '/test/',这样打包出来的静态资源可以和当前网站的配置保持一致,可以避免资源加载失败。
  2. 设置相对路径,publicPath: './'。但是这样一来,如果此时使用的是 history 模式的路由配置,因为网站加了二级目录会导致前端的路由地址匹配失败,所以还需要将 router 的 base 设置为 '/test/' 需要与网站的二级目录保持一致。或者直接采用 hash 的路由模式,这样可以忽略掉 base 参数的配置,比较省心。

关于vue-cli 的官方文档对 publicPath 字段的解释

关于Vue项目中publicPath的二三事

这个值在开发环境下同样生效。如果你想把开发服务器架设在根路径,你可以使用一个条件式的值:

module.exports = {
  publicPath: process.env.NODE_ENV === 'production'
    ? '/production-sub-path/'
    : '/'
}
官方文档说的很清楚,总结以下几种配置模式方便记忆:
  1. 傻瓜模式:路由采用 hash 模式,publicPath 采用相对路径 './' 模式,此种组合配置操作简单,可以部署到任意路径上,并且能支持二级目录,比较省心。即便是直接访问打包后的 index.html 也能看到效果,应用的场景可能如下:

    • 一个纯前端应用,不需要和后台的接口进行交互,支持本地静态展示。
    • 只是用来部署到 github gh-page 分支下的纯前端展示应用。
    • 如使用 vue 开发的 h5 项目,然后使用 hbuilder 进行打包的 hybird app。
    • 部署在带有二级路径的网站上。
  2. 进阶模式:路由采用 history 模式,publicPath 采用绝对路径 '/' 模式,此这种模式需要服务器来对前端资源进行部署。同时,还需要在服务器上配置404重定向到 index.html。
  3. 升级模式:路由采用 history 模式,publicPath 采用了 '/sub/' 二级路径,相应的 router base 参数也应该设置为 '/sub/' (通常使用脚手架默认提供的 process.env.BASE_URL 即可)。此种模式即在多个项目同时部署到一个域名和端口下时,采用不同的二级路径来对项目进行逻辑上的划分。与进阶模式的服务部署方式相同。