vue.config.js 中 publicPath 和 router 中 createWebHistory()的区别(奈何没活,导师整活)

1,095 阅读12分钟

前言

本篇结合了企业中上线的项目 创作者大赛(国际服 国内访问资源可能加载的比较慢) 进行分析,阅读文章时,可以在网站的网络或源码中进行确认和辅助分析。

本无意写,但奈何没活,导师觉得我当时问的代理课题值得研究就让我写,然后没想到得是涉及到了挺多东西的

  • vue.config.js 中 publicPath 影响了什么 —— 正文
  • router 路由页面的访问路径由谁决定的 —— 正文
  • router 源码内置了重定向 —— 问题一
  • nginx 的 proxy_pass 结尾有无基本路径会影响什么 —— 问题二
  • 浏览器向本地服务器访问的前端资源有哪些(即拿到html文档后做了什么事) —— 问题三

前提

本文中,为避免 ip 地址 暴露问题,涉及到的网站和 ip 地址都是虚构的,以下为本文虚构的 ip 地址

本地环境(本地 ip 地址):http://10.10.13.187:8082/

普通测试环境:http://12.12.5.205:21301/

带有域名的 nginx 服务器: debug-test.example.com:59998/

场景

当时需求完成的差不多了,就把带有域名的测试环境发给了产品,由于国际服的用户登录等(即 iframe 登录套件)接口都需要用域名

image.png

image.png

所以需要用带有域名的 nginx 服务器代理到普通测试环境

debug-test.example.com:59998/1st-co-crea… 代理到 http://12.12.5.205:21301/1st-co-creation/home/tc/fan-art

然后我想要在本地调试下项目的代码和接口,但因为产品占用了 /1st-co-creation 这个 path 路径,为了不影响验收,就不能用该路径代理到本地。

location /1st-co-creation/ {
    client_max_body_size 20M;
    proxy_pass http://12.12.5.205:21301/1st-co-creation/;
}

咱就灵机一动,能不能再写一个新的 path 路径,即/2st-co-creation代理到本地,但发现无法成功代理,

location /2st-co-creation/ {
    client_max_body_size 20M;
    proxy_pass http://10.10.13.187:8082/1st-co-creation/;
}

即访问 debug-test.example.com:59998/2st-co-crea… 时,让其代理到本地 http://10.10.13.187:8082/1st-co-creation 去,而不是测试环境 http://12.12.5.205:21301/1st-co-creation

但当我访问 debug-test.example.com:59998/2st-co-crea… 时,会重定向到 debug-test.example.com:59998/1st-co-crea… ,那么页面也就展示不出来。

其中前端的 router 配置 和 vue.config.js 的配置为

// router.js
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

// vue.config.js
module.exports = {
  publicPath: "/1st-co-creation/",
}

正文

而这其中的关键就是关于 vue.config.js 中的 publicPath 的配置和 router 中 createWebHistory() 括号里填写的值,它们两个对项目地址的控制。

首先需要知道的是 process.env.BASE_URL 指向的是 vue.config.js 中的 publicPath,如果不配置即默认为 '/'

module.exports = {
    publicPath: "/1st-co-creation/",
    pluginOptions: {···
    },
    css: {···
    },
    chainWebpack: (config) => {···
    },
    devServer: {···
    },
};

同理,createWebHistory() 里如果不填写内容,则默认为 '/'

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

publicPath 和 createWebHistory() 区别或者说影响的范围

publicPath 影响的范围,以 publicPath: "/1st-co-creation/" 为例

  • 本地启动地址,会在 ip 地址后添加 publicPath,

    image.pngs

    表示该地址才是项目的静态资源地址或者说是该项目服务器的内存地址。

    它只是控制了该项目静态资源的路径,即 public 文件夹中的文件,包括 env.js,index.html 等,

    image.png

  • 而其中 index.html 中的 <%= BASE_URL %> 指的就是 publicPath: "/1st-co-creation/"

    image.png

createWebHistory() 控制的则是路由页面的路径,以下面配置为例,更能体现控制的是路由路径。

如果配置如下,这里直接本地启动,先不管代理

// router.js
const router = createRouter({
  history: createWebHistory(),
  routes,
});

// vue.config.js
module.exports = {
    publicPath: "/1st-co-creation/",
}

此时 createWebHistory() 里没填写值,默认为 '/',即路由是以 http://10.10.13.187:8082/ 为起始路由,随后根据 routes 的匹配规则来控制路由页面,而静态资源的路径为 http://10.10.13.187:8082/1st-co-creation 起始

当直接访问 http://10.10.13.187:8082/home/tc/fan-art可以访问的,但访问 http://10.10.13.187:8082/1st-co-creation/home/tc/fan-art无法访问,因为没有匹配上routes的配置。

由此可知,页面展示的路由完全由 createWebHistory() 控制,与 publicPath 配置无关,publicPath 只控制本地服务器静态资源的路径。


问题现象或者实际应用场景(加深 publicPath 和 createWebHistory() 的理解)

问题一:router 内置了重定向?

问题一及其衍生的原因:router 源码中内置了重定向

// router.js
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

// vue.config.js
module.exports = {
  publicPath: "/1st-co-creation",
}

本地服务器静态资源地址为 http://10.10.13.187:8082/1st-co-creation/

本地项目路由启动地址为 http://10.10.13.187:8082/1st-co-creation/home/tc/fan-art

问题描述

当访问 http://10.10.13.187:8082/home/tc/fan-art 时,会发生类似于重定向的事件,并跳转到 http://10.10.13.187:8082/1st-co-creation/home/tc/fan-art ,但路由中并没有配置重定向。

原因

因为在 createWebHistory() 中指定了路由的基本路径 "/1st-co-creation",因此如果不携带路由配置的基本路径 "/1st-co-creation",而直接访问 routes 中的配置(/home/tc/fan-art),会发生重定向。

问题一的衍生

const router = createRouter({
  history: createWebHistory("/2st-co-creation"),
  routes,
});

module.exports = {
  publicPath: "/1st-co-creation",
}

本地服务器静态资源地址为 http://10.10.13.187:8082/1st-co-creation/

本地项目路由启动地址为 http://10.10.13.187:8082/2st-co-creation/home/tc/fan-art

问题描述

当访问 http://10.10.13.187:8082/1st-co-creation/home/tc/fan-art 时,会发生类似于重定向的事件,但路由中并没有配置重定向,即重定向到 http://10.10.13.187:8082/2st-co-creation/1st-co-creation/home/tc/fan-art

原因

/1st-co-creation/home/tc/fan-art 被当成了 routes 中的路由配置,由于在 createWebHistory() 中指定了路由的基本路径 "/2st-co-creation" ,当监测到访问地址没有携带基本路径,会在本地服务器 ip 地址 http://10.10.13.187:8082 和 routes 配置 /1st-co-creation/home/tc/fan-art 之间自动添加路由的基本路径 "/2st-co-creation" ,并进行重定向跳转,然后由于 /1st-co-creation/home/tc/fan-art 没有在routes 中配置,也就没有页面展示。

问题二:proxy_pass ip 地址后不携带斜杠的影响?

// router.js
const router = createRouter({
  history: createWebHistory(),
  routes,
});

// vue.config.js
module.exports = {
  publicPath: "/2st-co-creation",
}

本地服务器静态资源地址为 http://10.10.13.187:8082/2st-co-creation/

本地项目路由启动地址为 http://10.10.13.187:8082/home/tc/fan-art

域名测试环境地址为 debug-test.example.com:59998/2st-co-crea…

问题描述

【1】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082;
     }

等同于

【2】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/2st-co-creation/;
     }

使用 nginx 【1】代理时,明明代理的是本地 http://10.10.13.187:8082/ , 但最终代理却是 http://10.10.13.187:8082/2st-co-creation/ ,也就是说访问 debug-test.example.com:59998/2st-co-crea… ,按理来说应该代理到 http://10.10.13.187:8082/home/tc/fan-art ,最终却代理到的是 http://10.10.13.187:8082/2st-co-creation/home/tc/fan-art

原因

方式 【1】 中,proxy_pass 后的 ip 地址没有明确指定基本路径,Nginx 会保留原始请求中匹配到的路径部分(/2st-co-creation/),最终的代理为 http://10.10.13.187:8082/2st-co-creation/home/tc/fan-art

方式 【2】 中,proxy_pass 后已经包含了基本路径(即/2st-co-creation),因此 nginx 不会保留, 最终的代理为 http://10.10.13.187:8082/2st-co-creation/home/tc/fan-art

问题三(即最初提到场景的实现):浏览器请求本地服务器的资源?

// router.js
const router = createRouter({
  history: createWebHistory("/1st-co-creation"),
  routes,
});

// vue.config.js
module.exports = {
  publicPath: "/1st-co-creation",
}

本地服务器静态资源地址为 http://10.10.13.187:8082/1st-co-creation/

本地项目路由启动地址为 http://10.10.13.187:8082/1st-co-creation/home/tc/fan-art

域名测试环境地址为 debug-test.example.com:59998/2st-co-crea…

问题描述

【1】 location /1st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://12.12.5.205:21301/1st-co-creation/;
      }

 【2】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/1st-co-creation/;
     }

当我访问

域名测试环境 debug-test.example.com:59998/2st-co-crea… 时,

重定向到了 debug-test.example.com:59998/1st-co-crea… (也就是路径中多了 /1st-co-creation),

按理来说一开始实际代理【2】的地址为 http://10.10.13.187:8082/1st-co-creation/home/tc/fan-art ,应该能访问到的,为什么在 debug 域名中会发生重定向呢?

这里可能会误认为是由于 createWebHistory("/1st-co-creation") 的造成的,也就是问题一中提到的 router 基于基本路径内置了重定向,但实际上并不是

为了更好的展示问题,现在把路由的基本路径改为 /2st-co-creation

// router.js
const router = createRouter({
  history: createWebHistory("/2st-co-creation"),
  routes,
});

// vue.config.js
module.exports = {
  publicPath: "/1st-co-creation",
}

同样访问 debug-test.example.com:59998/2st-co-crea… 时,但仍重定向到了 debug-test.example.com:59998/1st-co-crea…

因此判断 nginx 代理时与路由的基本路径无关

原因

与路由的基本路径无关,但为什么会多出来 /1st-co-creation 呢?

【1】 location /1st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://12.12.5.205:21301/1st-co-creation/;
      }

 【2】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/1st-co-creation/;
     }

这是因为当访问 debug-test.example.com:59998/2st-co-crea… 时,地址代理到了 http://10.10.13.187:8082/1st-co-creation/ 本地服务器上了,然后本地服务器返回了 fan-art 的 html 文档,而之后浏览器获取到了这个资源后,基于自身网站的服务器 debug-test.example.com:59998 并拼接上 html 文档中的 srciprt 或者 link 中的 src 的路径,向自身网站的服务器发送请求

以下图 html 中的 <script src="/1st-co-creation/env.js"></script> 为例(注意:这里的静态资源路径 src 和 publicPath 有关,因为是由 <%= BASE_URL %> 转化来的)

自身网站服务器为 debug-test.example.com:59998 , 再拼接 scr 的 /1st-co-creation/env.jsdebug-test.example.com:59998/1st-co-crea… 进行发送请求,拿到相关的资源。

“非常重要!!!”:此时这里的请求都是通过 debug-test.example.com:59998/1st-co-crea… 请求的,它所走的 nginx 配置为 location /1st-co-creation/,也就是所有的资源都是通过没带域名的普通测试环境 http://12.12.5.205:21301/1st-co-creation/ 获取的,即下述测试环境的配置(测试环境的代码是git仓库中的,也就是正常能运行的代码)

// router.js
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
 routes,
});

// vue.config.js
module.exports = {
  publicPath: "/1st-co-creation/",
}
image.png

所有的资源都请求完后,就形成了一个完整的页面了(好家伙,从输入 url 到形成页面的一整套流程都基本理清了),其中,包括了打包之后的路由配置信息(下图所示),能看到路由配置的 createWebHistory("/1st-co-creation"),也就是普通测试环境中的代码。

image.png

由于路由配置的基本路径为 "/1st-co-creation",而访问的地址为 debug-test.example.com:59998/2st-co-crea… ,也就是说浏览器认为 routes 的配置为 /2st-co-creation/home/tc/fan-art 并且中间没有基本路径 "/1st-co-creation", 从而形成了路径重定向。

解决办法

【1】 location /1st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:80822/1st-co-creation/;
      }

 【2】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/1st-co-creation/;
     }

把原来 vue.config.js中 publicPath的值 /1st-co-creation/ ,直接修改为 /2st-co-creation/,由于修改了 publicPath 的值,导致 index.html 中的静态资源的路径发生了变化,即 /2st-co-creation/xxx.jsdebug-test.example.com:59998/2st-co-crea… 见下图) ,确确实实走得是 location /2st-co-creation/ 这层代理,那么它也就能代理到 http://10.10.13.187:8082/1st-co-creation/ 去了,路由的基本路径也是 /2st-co-creation/,不会出现重定向问题,中间也没啥幺蛾子出现,完美。

image.png

并且为了使后续避免出现配置相关的问题以及便于维护的因素, vue.config.js 中 publicPath 和 router 中createWebHistory() 路径保持一致,即设置 process.env.BASE_URL 为路由的基本路径。

// router.js
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

// vue.config.js
module.exports = {
    publicPath: "/2st-co-creation/",
};

那么相应的,nginx的代理到本地的配置也需要改了

location /2st-co-creation/ {
    client_max_body_size 20M;
    proxy_pass http://10.10.13.187:8082/2st-co-creation/;
}

其他

其他1

可能会遇到错误信息 Uncaught SyntaxError: Unexpected token '<',表示你 nginx 代理配置多了个 '/' 或则少了个 '/'

其他2 (本地服务器存放资源的结构?)

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

module.exports = {
   publicPath: "/2st-co-creation",
}

本地服务器静态资源地址为 http://10.10.13.187:8082/2st-co-creation/

本地项目路由启动地址为 http://10.10.13.187:8082/2st-co-creation/home/tc/fan-art

域名测试环境地址为 debug-test.example.com:59998/2st-co-crea…

问题描述

【1】 location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/1st-co-creation/;
      }

【2】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/2st-co-creation/;
     }

当 nginx 为 【1】时, 会代理到 http://10.10.13.187:8082/1st-co-creation/ ,public 下除了index.html 是正确的,其他比如 env.js,app.js 等都和 index.html 相同,并且图片等都没有进行请求,导致页面无法展示,即空白页。

image.png image.png

当 nginx 为 【2】时, 会代理到 http://10.10.13.187:8082/2st-co-creation/ ,即正确的静态资源路径,图片,所有的页面和请求都正常。

image.png

原因

(1)先解释下 env.js,app.js 等都和 index.html 相同的问题

由于 webpack-dev-server 中用到了 connect-history-api-fallbac 中间件,查阅了下官网大致说明

image.png

也就是说如果请求的基本路径和静态资源的路径不相等, connect-history-api-fallbac 这个中间件就会返回服务器默认的 index.html(相当于 public 下的 index.html)。

而如果请求的基本路径和静态资源的路径相等,则会返回静态资源路径下的 index.html

【1】 location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/1st-co-creation/;
      }

【2】location /2st-co-creation/ {
        client_max_body_size 20M;
        proxy_pass http://10.10.13.187:8082/2st-co-creation/;
     }

当 nginx 为 【1】时,debug代理之后浏览器向 http://10.10.13.187:8082/1st-co-creation 发送请求,请求的基本路径为 /1st-co-creation,而静态资源的路径为 /2st-co-creation,不相等,则返回http://10.10.13.187:8082http://10.10.13.187:8082 服务器下的默认 index.html

image.png

拿到html后,则会根据 html 中的script 或者 link 的 src 路径依次请求,即 debug-test.example.com:59998/2st-co-crea…debug-test.example.com:59998/2st-co-crea… 等,然后又走debug代理访问了 http://10.10.13.187:8082/1st-co-creation。

而此时请求的基本路径和静态资源的路径不相等,给env.js 和 app.js 请求都返回了 index.html,所以它们所有的请求才会相同。

当 nginx 为 【2】时,debug代理之后浏览器向 http://10.10.13.187:8082/2st-co-creation 发送请求,请求的基本路径为 /2st-co-creation,而静态资源的路径为 /2st-co-creation,相等,返回 http://10.10.13.187:8082 服务器下的 静态资源路径/2st-co-creation 目录中的 index.html。

拿到html后,跟上述分析一样,又走了 debug 代理访问了 http://10.10.13.187:8082/1st-co-creation ,请求路径和静态资源路径相等,返回静态资源路径下的 env.js 和 app.js

总得来说,可以把服务器想象成一个文件夹,根路径中存放了 env.js,favicon.ico,index.html,即 public 目录下的文件(此时是未打包的即webpack编译的,当请求后才会编译) image.png

然后又根据项目中 publicPath 的值,新建了一个文件夹 2st-co-creation(以 /2st-co-creation 为例),里面存放的是经过 build 之后的代码(即把 dist 命名为了 2st-co-creation

image.png

最后形成了一个类似这样的文件(自己建的,便于理解

image.png

(2)图片等资源没有进行请求,导致页面无法展示,即空白页的问题

图片和其他页面的资源是放到哪的?当然是项目的 src 目录下的,而 src 最后会被 webpack 打包成 app.js。

当 nginx 为 【1】时, app.js 在上一个问题中,因为代理后请求的基本路径和静态资源路径不相等,返回给 app.js 的是 index.html 文件,根本就没拿到真正的 app.js 文件自然图片和页面的资源根本就没请求。

当 nginx 为 【2】时, 因为代理后请求的基本路径和静态资源路径相等,所有文件都能正常访问,那么就能形成页面了。

其他3

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

module.exports = {
   publicPath: "/2st-co-creation",
}

本地服务器静态资源地址为 http://10.10.13.187:8082/2st-co-creation/

本地项目路由启动地址为 http://10.10.13.187:8082/2st-co-creation/home/tc/fan-art

问题描述

当访问 http://10.10.13.187:8082/2st-co-creation/env.jshttp://10.10.13.187:8082/env.js 时,都能访问到相同的 env.js 文件。

原因

有了上个问题的分析,即一个服务器的资源存放位置,就很能够好得理解这个问题了。

image.png

访问 http://10.10.13.187:8082/2st-co-creation/env.js/2st-co-creation 目录下的 env.js, 而访问 http://10.10.13.187:8082/env.js 是 服务器根目录下的 env.js

因此都能访问到 env.js,且这两个 env.js 是相同的资源。