youngjuning / issues

一寸欢喜 - 怕什么真理无穷,进一寸有一寸的欢喜
https://youngjuning.js.org
44 stars 4 forks source link

SPA F5刷新页面后404 #198

Closed youngjuning closed 3 years ago

youngjuning commented 5 years ago

参考链接

youngjuning commented 5 years ago

rap2-dolores docker部署引发的血案

在研究 阿里开源mock系统 rap2 指北 时,rap2-delorrap2-dolores 我都采用了docker部署。rap2-dolores 的 docker 部署参考的Rynxiao同学的 教你使用docker部署淘宝rap2服务

首先感谢 Rynxiao 同学贡献的 docker 配置方法。但是他的方案有一个严重的问题:F5刷新页面后404(也就是本文要解决的问题!!!)。

弯路

  1. 一开始我以为是 rap2-dolores 的问题,然后在 issues 中找到了 刷新404问题
  2. 根据1中指引找到了官方wiki中的 F5刷新页面后404
  3. 官方wiki表示这个锅不背,并指明是因为SPA应用的问题
  4. 根据3中的指引我来到了Nginx配置

wtf,根据 Rynxiao 的教程,rap2-dolores 是运行在 http-server 上的,而官方只告知了 Nginx 的配置。摆在我面前的选择有三个:

  1. 向官方低头,放弃docker和http-server使用 nginx 部署 rap2-dolores
  2. 向官方低头,放弃http-server,起一个nginx的docker,并部署rap2-dolores
  3. 自食其力,基于 Rynxiao 的已有成就,修复这个BUG。

前两种先不说认怂不好,就算实现了也不是很完美。。。

正确解决方法

参考:

1. 修改 Dockerfile:

+ RUN npm set registry https://registry.npm.taobao.org
- RUN npm install -g http-server
+ RUN npm install -g spa-http-server

2. 修改 docker-compose.yml:

- command: /bin/sh -c 'http-server ./build -s -p 8081'
+ command: /bin/sh -c 'http-server ./build -s -p 8081 --push-state'

为了祭奠我逝去的青春,谨在此研究一下 SPA到底有多厉害(坑)

youngjuning commented 5 years ago

什么是 SPA

只有一张Web页面的应用,是一种从Web服务器加载的富客户端,单页面跳转仅刷新局部资源 ,公共资源(js、css等)仅需加载一次,常用于PC端官网、购物等网站:

image

单页面与多页面的对比:前端:你要懂的单页面应用和多页面应用

youngjuning commented 5 years ago

Vue SPA F5刷新页面后404

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

后端配置例子

Apache

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

除了 mod_rewrite,你也可以使用 FallbackResource

nginx

location / {
  #加上了这个,就可以在url后面直接加路由上配置path了。
  try_files $uri $uri/ /index.html;
}

原生 Node.js

const http = require('http')
const fs = require('fs')
const httpPort = 80

http.createServer((req, res) => {
  fs.readFile('index.htm', 'utf-8', (err, content) => {
    if (err) {
      console.log('We cannot open "index.htm" file.')
    }

    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8'
    })

    res.end(content)
  })
}).listen(httpPort, () => {
  console.log('Server listening on: http://localhost:%s', httpPort)
})

基于 Node.js 的 Express

对于 Node.js/Express,请考虑使用 connect-history-api-fallback 中间件

Internet Information Services (IIS)

  1. 安装 IIS UrlRewrite
  2. 在你的网站根目录中创建一个 web.config 文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Handle History Mode and custom 404/500" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Caddy

rewrite {
    regexp .*
    to {path} /
}

Firebase 主机

{
  "hosting": {
    "public": "dist",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

Superstatic

{
  "rewrites": [
    {
      "source":"**",
      "destination":"/index.html"
    }
  ]
}

详细信息和警告请参考尤雨西源文档:http://t.cn/RgKwqxv

youngjuning commented 5 years ago

Nginx配置ReactJs项目,Url后面直接输入路由路径时老报404问题。

Nginx

location / {
  #加上了这个,就可以在url后面直接加路由上配置path了。
+ try_files $uri $uri/ /index.html;
}

或参考:

location /demo {
  root  E:/;
  #加上了这个,就可以在url后面直接加路由上配置path了。
  try_files $uri /demo/index.html;
  index  index.html index.htm;
}
youngjuning commented 4 years ago

http://www.dreamwu.com/post-103.html

youngjuning commented 4 years ago

https://pro.ant.design/docs/deploy-cn#%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1%E4%B8%8E%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%9A%84%E7%BB%93%E5%90%88