我之前在上线自己的博客遇到过下面这些问题
- 为啥我的博客在开发阶段都没问题,部署到服务器之后访问不了除了
/
的页面 - 路由用hash模式就没问题,改成history就会有问题
- 公众号:前端南玖
- 不定时有送书活动,记得关注~
- 每日推送前端技术文章~
什么是路由
在Web开发过程中,经常会遇到『路由』的概念。那么,到底什么是路由?简单来说,路由就是URL到函数的映射。
并且路由这个概念最早是出现在后端的,因为早期的网页都是服务端渲染的,比如:JSP,PHP,ASP等语言,都是直接返回渲染好的html给客户端显示。
后端路由
在web开发早期的年代里,前端的功能远不如现在这么强大,一直是后端路由占据主导地位。无论是jsp,还是php、asp,用户能通过URL访问到的页面,大多是通过后端路由匹配之后再返回给浏览器的。浏览器在地址栏中切换不同的URL时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件返回给前端,并且每次切换页面时,浏览器都会刷新页面。
在后端,路由映射表中就是不同的URL地址与不同的html + css + 后端数据 之间的映射
下面这张图或许能够让你更加清楚的了解后端路由:
前端路由
前端路由是后来发展到SPA(单页应用)时才出现的概念。
前端路由主要是有两种模式:
- hash: 带有hash的前端路由,优点是兼容性高。缺点是URL带有
#
号不好看 - **history: **不带hash的前端路由,优点是URL不带
#
号,好看。缺点是既需要浏览器支持也需要后端服务器支持
前端路由应用最广泛的例子就是当今的SPA的web项目。不管是Vue、React还是Angular的页面工程,都离不开相应配套的router工具。
整个页面就只有一整套的HTMLCSS+JS,这一套HTML+CSS+JS中包含了许多个网页,当我们请求不同的URL时,客户端会从这一整套HTML+CSS+JS中找到对应的HTML+CSS+JS,将它们解析执行,渲染在页面上。
前端路由带来的最明显的好处就是,地址栏URL的跳转不会白屏了——这也得益于客户端渲染带来的好处。
我们同样来看一张图:
Hash模式
早起的前端路由实现就是基于
location.hash
来实现的,location.hash
就是路由#
后面的内容,其原理就是通过hashchange
监听#
后面的内容的变化来进行页面更新。hash
模式是利用浏览器不会对#
后面的路径对服务端发起请求。
使用hash模式有两个特点:
- 改变hash值,浏览器不会重新加载页面
- 当刷新页面时,hash不会传给服务器
也就是说http://localhost/#a
与http://localhost/
这两个路由其实都是去请求http://localhost
这个页面的内容,至于为什么它们可以渲染出不同的页面,这个是前端自己来判断的,不需要后端配置。
优点:
- 可以兼容低版本浏览器,支持IE8
- 只有
#
之前的内容才会作为URL发送给服务器,就算服务端没有对路由进行全覆盖也不会返回404 hash
改变都会在浏览器访问的历史记录中增加一个记录,所以可以通过浏览器进行前进后退,如果想在hash模式下不保存记录,可以使用location.replace
History模式
history
是基于HTML5新增的pushState
和replaceState
两个API以及浏览器的popState
事件监听历史栈的改变,只要历史栈有信息发生改变,就会触发该事件。这种模式同样也是不会向后端发起请求的。
优点:
- 该模式的路由不带
#
,看起来更美观 pushState
设置的URL
可以是任意的与当前URL同源的URL,而hash
只能改变#后面的内容
缺点:
- IE9及其以下版本浏览器不支持,IE10开始支持
vue-router会检测浏览器版本,当无法启用history
模式时会自动降级为hash
模式
文章开头说的问题是什么原因导致的?
为啥我的博客在开发阶段都没问题,部署到服务器之后访问不了除了/
的页面??
那是因为vue-cli
在开发模式下帮你启动的那个express
开发服务器帮你做了这方面的配置。理论上我们在开发模式下本来也是需要配置服务端的,只不过vue-cli
都帮你配置好了,所以你就不用手动配置了。
那么该如何配置呢?
其实在生产模式下配置也很简单,参考vue-router给出的配置例子。一个原则就是,在所有后端路由规则的最后,配置一个规则,如果前面其他路由规则都不匹配的情况下,就执行这个规则——把构建好的那个index.html
返回给前端。这样就解决了后端路由抛出的404的问题了,因为只要你输入了http://localhost/user/1
这地址,那么由于后端其他路由都不匹配,那么就会返回给浏览器index.html
。
浏览器拿到这个html之后,router库就开始工作,开始获取地址栏的URL信息,然后再告诉前端库(比如Vue)渲染对应的页面。到这一步就跟hash模式是类似的了。
当然,由于后端无法抛出404的页面错误,404的URL规则自然是交给前端路由来决定了。你可以自己在前端路由里决定什么URL都不匹配的404页面应该显示什么。
页面渲染流程
- 浏览器通过DNS服务器得到域名的IP地址,向这个IP地址请求得到HTML文本
- 浏览器渲染进程解析HTML文本,构建DOM树
- 解析HTML的同时,如果遇到内联样式或者样式文件,则下载并构建样式规则,如果遇到JavaScript脚本,则会下载执行脚本
- DOM树和CSSOM构建完成之后,渲染进程将两者合并成渲染树(render tree)
- 渲染进程开始对渲染树进行布局,生成布局树(layout tree)
- 渲染树对布局树进行绘制,生成绘制记录
服务端渲染(SSR)
顾名思义,服务端渲染就是在浏览器请求页面URL时,服务端直接将我们需要的HTML文本组装好,并返回给浏览器,这个HTML文本被浏览器解析之后,不需要经过JavaScript脚本的执行,可直接构建出完整的DOM树并展示页面中。这个服务端组装的过程,叫做服务端渲染。
客户端渲染(CSR)
页面的渲染其实就是浏览器将HTML文本转化为页面帧的过程。而如今我们大部分WEB应用都是使用 JavaScript 框架(Vue、React、Angular)进行页面渲染的,也就是说,在执行 JavaScript 脚本的时候,HTML页面已经开始解析并且构建DOM树了,JavaScript 脚本只是动态的改变 DOM 树的结构,使得页面成为希望成为的样子,这种渲染方式叫动态渲染
,也可以叫客户端渲染(client side rende)
。
前端渲染把渲染的任务交给了浏览器,通过客户端的算力来解决页面的构建,这个很大程度上缓解了服务端的压力。而且配合前端路由,无缝的页面切换体验自然是对用户友好的。不过带来的坏处就是对SEO不友好。
需要明确的是,只要在浏览器地址栏输入URL再回车,是一定会去后端服务器请求一次的。而如果是在页面里通过点击按钮等操作,利用router库的api来进行的URL更新是不会去后端服务器请求的。
前端路由与服务端渲染
虽然前端渲染有诸多好处,不过SEO的问题,还是比较突出的。所以react、vue等框架在后来也在服务端渲染上做着自己的努力。基于前端库的服务端渲染跟以前基于后端语言的服务端渲染又有所不同。前端框架的服务端渲染大多依然采用的是前端路由,并且由于引入了状态统一、vnode等等概念,它们的服务端渲染对服务器的性能要求比php等语言基于的字符串填充的模板引擎渲染对于服务器的性能要求高得多。所以在这方面不仅是框架本身在不断改进算法、优化,服务端的性能也必须要有所提升。
当然在二者之间,也出现了预渲染的概念。也即先在服务端构建出一部分静态的html文件,用于直出浏览器。然后剩下的页面再通过常用的前端渲染来实现。通常我们可以把首页采用预渲染的方式。这个的好处是明显的,兼顾了SEO和服务器的性能要求。不过它无法做到全站SEO,生产构建阶段耗时也会有所提高,这也是遗憾所在。
关于预渲染,可以考虑使用prerender-spa-plugin这个webapck的插件,它的3.x版本开始使用puppeteer来构建html文件了。