React项目在 GitHub Page 上使用History route 部署的方法
前言
前段时间用React
+TS
+antd
写了一个后台模板, 传送门
准备部署在GitHub
page上当做demo方便查看, 在部署的过程中就遇到了这个一个问题, 折腾了一段时间, 最后通过曲线救国的方式解决了
分析
ghpage
是不支持配置rewrite
规则的, 因此访问一个不存在的目录时, 并不会给我们做重定向的功能, 所以此时就会看到404
页面, 如果是hashRoute
自然不会有这个问题.
这样看来, 这个问题岂不是无解了, 也不尽然, 虽然不会重定向, 但是他会优先从我们的ghpage
根目录寻找404.html
这个文件, 如果找到了,就是用这个文件当做404
的模板页面
所以, 我们可以通过创建一个404.html
文件的方式, 在这个页面中写js来做一些跳转
比如, 当访问https://xstnet.github.io/react-admin-template/dashboard 这个url的时候,由于我们的项目中不存在 dashboard
这个文件, 也不存在dashboard
这个目录, 因此Git就会返回一个404
页面出来
404.html
创建一个404.html
文件, 这个文件必须要位于项目的根目录, 也就是说假如我们的项目打包后生成了一个dist
文件夹, 把dist
这个文件夹当做gh-page
的根目录的话, 那404.html
的路径就是dist/404.html
, 它和index.html
是同级的
下面来看看应该怎么去写一个跳转逻辑
控制页面跳转
还是以https://xstnet.github.io/react-admin-template/dashboard 这个链接举例
已知我们能成功跳转的页面地址只有ghpage
项目的首页, 也就是https://xstnet.github.io/react-admin-template,
但是光往这个地址跳没有意义啊, 又回到首页了, 因此我们要加东西, 加什么呢, 自然是加路由参数了, 不过要怎么加呢?
我采用的是吧页面路由部分当做参数跳回到首页, 也就是下面的规则:
原url: https://用户名.github.io/仓库名[/route]
跳转url: https://用户名.github.io/仓库名?ghpage=route
不管现在访问的是几级路由, 甚至可能后面还有参数, 不过不用管, 统一放到 ghpage
参数中
不过这里还有个坑, 那就是在含有参数时, url中会含有
?
这个字符, 放到跳转地址中就会出现多个?
, 不是一个标准的url了, 因此我们对ghpage=route
部分再做一层处理, 如下伪代码ghpage=encodeURIComponent(route);
使用 encodeURIComponent 转义一次就没有这个问题了
现在就可以往这个地址跳转了
代码实现
如下是我的404.html
的代码
我采用的是动态获取仓库名的方式, 也就是取 location.pathname
中第一个/
字符前的所有字符串当做仓库名, 而第一个/
之后的所有字符都当做参数传给ghpage
<!DOCTYPE html>
<html>
<head>
<title>Redirecting...</title>
<script>
const path = location.pathname;
const pos = path.indexOf('\/', 1);
const repo = path.substring(0, pos);
const ghpage = path.substring(pos);
const redirectUrl = location.origin+repo+'/?ghpage='+encodeURIComponent(ghpage+location.search);
window.location.replace(redirectUrl);
</script>
</head>
<body>
Redirecting...
</body>
</html>
例子
源url:https://xstnet.github.io/react-admin-template/dashboard?a=1&b=2
经过代码处理后的跳转地址:https://xstnet.github.io/react-admin-template?ghpage=dashboard%3Fa%3D1%26b%3D2
组件内路由处理
上面说完了跳转, 虽然最后带着参数跳转过来了, 但是组件内不处理的话, 带不带参数有什么区别吗?
下面开始处理, 只需要在我们的布局组件内监听一下即可
import { useNavigate, useSearchParams } from 'react-router-dom';
const [searchParams] = useSearchParams();
useEffect(() => {
// 借助 gh-page 404.html的功能跳转回来, 解析路由并加载相应的页面
if (searchParams.has('ghpage')) {
const ghpage = decodeURIComponent(searchParams.get('ghpage')!);
// 这里要注意如果跳转失败了要看看是否需要加上'/'前缀
navigate(ghpage);
return;
}
}, [])
上面的代码使用useEffect
时不需要依赖项, 因为从别的页面跳转过来肯定是全部页面都要重新加载的
这个时候再查看一下我的页面, 会发现中间有个短暂的重定向中转, 就是通过上面的方法实现的传送门
缺点
- 刷新页面时会有一个重定向, 但是在页面内由
navigage
方法控制的跳转是不会有问题的 - 组件内增加了对
ghpage
这个路由参数的监听
进阶
很多时候我们并不想在除了gh-pages
分支上去创建一个404.html
文件, 也没有必要, 但要使用上面的方法跳转必须要有一个404.html文件
, 那还有别的方法吗?
答案无疑是确定的, 那就是使用Github Action
提供的workflow
配置文件来生成
当我们使用Github Action
来发布GitHub Page
时, 就可以使用这种方法, 那就是把创建404.html
文件这一步骤放到配置文件中
以下是我的.github/workflows/gh-pages.yml
文件内容, 供参考
name: Publish To Github Page
on:
push:
branches:
- master
workflow_dispatch:
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
with:
persist-credentials: false
- name: Install and Build # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
run: |
npm i -g pnpm
pnpm install --no-frozen-lockfile
pnpm run gh-pages
- name: Generate 404 Page # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
run: |
echo "<!DOCTYPE html>" > dist/404.html
echo "<html>" >> dist/404.html
echo " <head>" >> dist/404.html
echo " <title>Redirecting...</title>" >> dist/404.html
echo " <script>" >> dist/404.html
echo " const path = location.pathname;" >> dist/404.html
echo " const pos = path.indexOf('\/', 1);" >> dist/404.html
echo " const repo = path.substring(0, pos);" >> dist/404.html
echo " const ghpage = path.substring(pos);" >> dist/404.html
echo " const redirectUrl = location.origin+repo+'/?ghpage='+encodeURIComponent(ghpage+location.search);" >> dist/404.html
echo " window.location.replace(redirectUrl);" >> dist/404.html
echo " </script>" >> dist/404.html
echo " </head>" >> dist/404.html
echo " <body>" >> dist/404.html
echo " Redirecting..." >> dist/404.html
echo " </body>" >> dist/404.html
echo "</html>" >> dist/404.html
- name: Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to.
FOLDER: dist # The folder the action should deploy.
结束
至此, 折腾终于结束了, 也算是圆满解决了HistoryRoute
在Github Page
上发布的问题了
感谢阅读, 再见