histroy router的问题
在history router模式下,我们可能会进行一些刷新路由操作
场景复现如下:
我们的路由位于/route之下,此时进行刷新。
不巧,我们出现404了。
这个问题很简单,因为没有对应的api或者静态资源,所以很正常的返回了404页面。
此时你不想添加一个后端api返回全局index.html,所以通过了webpack-dev-server的historyFallback进行了重定向兜底。
此时再次刷新:
不巧,我们的刷新没有成功,报错了:
Uncaught SyntaxError: Unexpected token '<'
点开错误详情,发现第一行error
<!DOCTYPE html>
再看一下请求,不是应该返回script吗,怎么返回了一个html了?
问题剖析
原来是我们通过webpack-dev-server里的historyfallback进行了一次兜底,把所有的页面通通返回到/。
这本来是一件好事,它可以让任何请求都返回/页面(也就是我们的index.html),然后走我们react的router逻辑,再分发到对应的页面。
但是问题就出在这里,我们复现一下浏览器和webpack处理的过程:
- 刷新
/route - 浏览器访问
/route webpack-dev-server监听到后返回到/。- 此时返回
/对应的index.html页面 - 开始请求
index.html里的静态资源 - 不妙的事情来了,
index.html里的静态资源bundle是相对路径bundle_xxx,那这时候请求到了/route/bundle_xxx - 浏览器正常处理
/route/bundle_xxx,继续请求我们的服务器dev server webpack_dev_server把/route/bundle_xxx,又把这个路径定向给了/- 此时返回
/对应的index.html - 此时
/route/bundle_xxx返回了index.html - 很明显,一个bundle包肯定是js文件,此时返回了html文件,自然就报错了。
问题解决
知道了问题的所在,我们解决问题就太轻松了。
问题在于第六步,我们的bundle文件打包出来是基于相对路径的,如果我们把它改为绝对路径就完全ok了呀。
解决办法
- dev: 修改
webpack的publicPath为/即可 - prod: 修改
publicPath为对应的部署前缀即可
此时我们用纯前端的方式解决了Uncaught SyntaxError: Unexpected token '<'问题,再也不用去后端配置一个返回index.html的api了。
router
另外,如果你想要了解spa应用路由的原理,或者不想使用react-router这样庞大体量的路由库,可以参考一下
bobo-router
这是一个纯hooks,支持路由守卫的高性能匹配库。