后端路由与前端路由
后端路由:根据不同的URL地址分发不同的资源
前端路由:根据不同的用户事件,显示不同的页面内容(负责事件监听,触发事件后,通过事件函数渲染不同内容)
多页面应用模式MPA(Multi Page Application) | 单页面应用模式SPA(Single Page Application) | |
---|---|---|
应用构成 | 由多个完整页面构成 | 一个外壳页面和多个页面片段构成 |
跳转方式 | 页面之间的跳转是从一个页面到另一个页面 | 一个页面片段删除或隐藏,加载另一个页面片段并显示。片段间的模拟跳转,没有开壳页面 |
跳转后公共资源是否重新加载 | 是 | 否 |
URL模式 | http://xxx/page1.html 和http://xxx/page2.html |
http://xxx/shell.html#page1 和http://xxx/shell.html#page2 |
用户体验 | 页面间切换加载慢,不流畅,用户体验差,尤其在移动端 | 页面片段间切换快,用户体验好,包括移动设备 |
能否实现转场动画 | 否 | 容易实现(手机APP动效) |
页面间传递数据 | 依赖URL 、cookie 或者localstorage ,实现麻烦 |
页面传递数据容易(Vue 中的父子组件通讯props 对象或Vuex ) |
搜索引擎优化(SEO) | 可以直接做 | 需要单独方案(SSR) |
特别适用的范围 | 需要对搜索引擎友好的网站 | 对体验要求高,特别是移动应用 |
开发难度 | 较低,框架选择容易 | 较高,需要专门的框架来降低这种模式的开发难度 |
前端路由与SPA
传统的后端路由,根据客户端请求的不同网址,返回不同的网页内容。这样会造成服务器压力增加以及每次都重新请求,响应慢,用户体验下降。于是SPA应运而生,在url地址改变的过程中,通过js来实现不同的UI之间的切换,而不再向服务器重新请求页面,只通过ajax向服务器请求数据,对用户来说这种无刷新的、即时响应有更好的体验。其中根据url地址的变化而展示不同的UI,就是通过前端路由来实现的
前端路由的实现方式
基于location.hash实现(location.hash+hashchange事件)
location.hash的值是url中#
后面的内容,如http://www.163.com#netease
,location.hash为'#netease'
hash满足一下几个特性,才使得其可以实现前端路由:
-
url中hash值的变化并不会重新加载页面,因为hash是用来指导浏览器行为的,对服务端是无用的,所以不会包括在http请求中
-
hash值的改变,都会在浏览器的访问历史中增加一个记录,能通过浏览器的回退、前进控制hash的切换
-
我们可以通过onhashchange事件,监听到hash值的变化,从而响应不用路径的逻辑处理。我们就可以在onhashchange事件,根据hash转换来更新对应的视图,但不会去重新请求页面
window.addEventListener('hashchange', function() { }, false)
触发hash值的变化有2种方法:
一种是通过a标签,设置href属性,当a标签点击之后,地址栏会改变同时触发onhashchange事件
<a href="#kaola">to kaola</a>
另一种是通过js直接赋值给location.hash,也会改变url同时触发onhashchange事件
location.hash="#kaola"
具体实现方式:
<div id="app">
<a href="#/zhuye">主页</a>
<a href="#/keji">科技</a>
<a href="#/caijing">财经</a>
<a href="#/yule">娱乐</a>
<!-- component标签当做是组件的占位符 -->
<component :is="comName"></component>
</div>
<script>
const zhuye = {
template: "<h1>主页信息</h1>"
}
const keji = {
template: "<h1>科技信息</h1>"
}
const caijing = {
template: "<h1>财经信息</h1>"
}
const yule = {
template: "<h1>娱乐信息</h1>"
}
const vm = new Vue({
el: "#app",
data: {
comName: zhuye
},
// 注册私有组件
components: {
zhuye,
keji,
caijing,
yule
}
})
window.onhashchange = function () {
switch (location.hash.slice(1)) {
case "/zhuye":
vm.comName = zhuye
break
case "/keji":
vm.comName = keji
break
case "/caijing":
vm.comName = caijing
break
case "/yule":
vm.comName = yule
break
}
}
</script>
基于history新API实现(history.pushState()+popState事件)
history.pushState():会增加一条新的历史记录
传入的新url不一定是绝对地址。如果是相对地址,它将以当前url为基准。传入的新url与当前url应该是同源否则pushState()会抛出异常
const state = {
'page_id': 1, 'user_id': 5 } //一个与指定网址相关的状态对象
const title = null //新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null
const url = 'hello-world.html' //新的网址,必须与当前url同源
window.history.pushState(state, title, url)
history.replaceState():会替换当前的历史记录
两个API相同之处:都可以修改url地址,操作浏览器的历史记录同时state会改变,但不会引起页面的刷新
通过onpopstate监听history.state
的变化,来指引js做加载以及渲染等任务
window.addEventListener('popstate',function(){
//获取到最新的state值
var state = history.state
})
hash模式 | history模式 | |
---|---|---|
url显示 | 有#,很Low | 无#,好看 |
回车刷新 | 可以加载到hash值对应页面 | 一般就是404掉了 |
支持版本 | 支持低版本浏览器和IE浏览器 | HTML5新推出的API |
history模式存在的问题:
export default new Router({
mode: 'history'
}
在路由跳转时,路径改变了,其实并没有加载页面,当我们刷新的时候就会报404错误,因为Vue Router设置的路径不是真实存在的路径
解决history模式下刷新报404的弊端,这就需要服务器端做点手脚,配置一下 apache 或 nginx 的url重定向,重定向到你的首页路由上就ok了(将不存在的路径请求重定向到入口文件index.html)
配置nginx的方式:
location / {
try_files $uri $uri/ /index.html
}
Vue Router插件
Vue Router是Vue.js官方的路由管理器,可以非常方便的用于SPA应用程序的开发,支持hash模式和HTML5 history模式
添加路由链接
router-link
标签默认被渲染为a标签
to
属性默认被渲染为href属性
to
属性值默认被渲染为#开头的hash地址
<router-link to="/user">User</router-link>
添加路由填充位(路由占位符)
将来通过路由规则匹配到的组件,将会被渲染到router-view
所在位置
<router-view></router-view>
router.js
每一个路由规则都是一个配置对象,其中至少包含 path 和 component 两个属性:
- path:表示当前路由规则匹配的hash地址
- component:表示当前路由规则对应要展示的组件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from './components/Login.vue'
import Admin from './components/Admin.vue'
import Customer from './components/Customer.vue'
import AdminHome from './components/Admin/AdminHome.vue'
import CustomerHome from './components/Customer/CustomerHome.vue'
import UserList from './components/Admin/UserList.vue'
import MyOrder from './components/Customer/MyOrder.vue'
import HomeCooking from './components/Customer/HomeCooking.vue'
Vue.use(VueRouter)
// 创建路由实例对象router
const router = new VueRouter({
// routes是路由规则数组
routes: