谈到路由,一般分为前端路由和后端路由两种,后端路由指的当用户通过浏览器切换不同URL时,都会向服务器发起资源请求,服务器通过后端路由匹配之后根据不同URL返回不同页面,而前端路由则将浏览器与服务器交互(页面跳转的URL规则匹配)的任务交给前端来做
1.前端路由模式
目前单页应用(SPA)成为目前前端应用的主流,而大型单页应用的一个大特征是,由前端路由来控制页面的跳转,通过url的切换,在不请求服务器的前提,更新页面视图,这里以vue-router为例分析,前端路由模式主要包括两种:hash模式和history模式
1.1 hash 模式
hash模式即是通过 hash 值(类似锚点)的变化,浏览器不用向服务器发起请求,也就无需刷新页面。而浏览器是怎么监听变化的呢?就是通过url中hash 值的变化,此时还好触发 hashchange 事件,通过此事件的触发我们就可以清晰知道hash发生了什么变化
假设你浏览器访问的url地址是 http://127.0.0.1/#/test 那么通过 location.hash 获取的hash值为 #/test
导致路由的变化无非是三种情况:刷新页面、浏览器返回操作、新链接跳转,下面是具体流程图👇
那hash模式下监听路由变化实现的原理是怎么样的呢?
window.addEventListener('hashchange', this.onHashChange.bind(this)) window.addEventListener('load', this.onHashChange.bind(this)) function onHashChange(){ // todo 匹配 hash 做 dom 更新操作 }
有兴趣的童鞋可以研究vue-router关于hash类的具体源码实现 点我到达火箭
1.2 history 模式
自HTML5新标准出台,pushState和replaceState是HTML5的新接口,通过这两个 API 可以改变 url 地址且不会发送请求,前端路由从此了多了另外一种模式History,而且通过这种模式不再需要在URL添加#符号,也能让URL显得更加优美
我们先看看window.history对象里面有什么
History.pushState
在不刷新浏览器的情况下,创建新的浏览记录并插入浏览记录队列中,当刷新页面,页面内容不变但地址发生了变化,该API可传入三个参数,分别是
- 状态对象(stateObject): 可以通过pushState方法可以将stateObject(对象)内容传递到新页面中
- 标题(title):可不传
- 地址(url):新的历史记录条目的地址(url不支持跨域);
window.history.pushState({data: "test"}, "", 'http://127.0.0.1/test');
History.replaceState:
的使用与 history.pushState() 类似,区别在于pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录,把当前的历史记录改成目标地址
window.history.replaceState({data: "test"}, "", 'http://127.0.0.1/test');
popstate
当用户发起返回操作或者执行history.go()或history.forward()等操作时,才会触发popstate
window.addEventListener('popstate', e => { //todo });
讲完history模式涉及到接口,树酱聊聊配置history模式需要注意的事项,这种模式相比hash模式还需要配置后端,如果后台没有正确的配置,当用户在浏览器直接刷新 http://127.0.0.1/#/test 就会返回 404,那如何解决这个问题呢 🤔️
你需要在Nginx配置文件添加重定向 附上 history vue-router官方文档
location / { try_files $uri $uri/ /index.html; }
Vue-router History模式下的流程图如下所示
有兴趣Vue-router关于history类的具体源码实现 点我到达火箭
2.路由应用
聊聊vue-router的一些应用场景
2.1 路由拦截
路由拦截可以用来作为前端鉴权入口,比如判断是否是已登录状态
mport Vue from 'vue' import Router from 'vue-router' Vue.use(Router) const router = new Router({ routes: [ { path: '/home','' component: () => import('@/components/Home') } },{ path: '/login', name: 'login', component: (resolve) => { component: () => import('@/components/Login') } }}] }) // 判断是否需要登录权限 以及是否登录 router.beforeEach((to, from, next) => { if (to.matched.some(res => res.meta.requireAuth)) { // 判断路由是否需要登录权限 if (localStorage.getItem('item')) { //判断是否有token next() } else {// 没登录则跳转到登录界面 next({ path: '/login', query: {redirect: to.fullPath} }) } } else { next() } })
2.2 路由懒加载
懒加载,顾名思义就是等需要再加载,在SPA应用中,如果不通过懒加载加载组件的方式,会导致webpack打包出来的文件体制过大,进而影响用户体验
export default new Router({ routes: [ { path: '/home', name: 'home', component: () => import('@/components/Home') # 懒加载引入组件 } ] });
2.3 路由模块化管理
你是否还在烦恼如何按不同模块不同功能管理不同路由,这里要推荐使用 require.context()
不同功能模块区分,再通过require.context 导出所有路由
require.context() 它允许传入一个目录进行搜索,一个标志表示是否也应该搜索子目录,以及一个正则表达式来匹配文件,当你构建项目时,webpack会处理require.context的内容
require.context()可传入三个参数分别是:
- directory :读取文件的路径
- useSubdirectories :是否遍历文件的子目录
- regExp: 匹配文件的正则
实际应用如下👇
routes导出来结果是这样
当然原本的定义路由的方式也变了
/* * 个人中心路由模块(user) * @Author: tree * @Date: 2019-11-06 20:20:51 */ export default [ { path: '/user/info', name: 'personalInfo', component: () => import('@/views/user/info.vue'), meta: { title: '账号信息', keepAlive: false, showHeader: true }, }, { path: '/user/security', name: 'security', component: () => import('@/views/user/security.vue'), meta: { title: '安全设置', keepAlive: false, showHeader: true }, }, ];
2.4 其他常见路由 API
- window.history.back() : 加载 history 列表中的前一个 URL
- window.history.forward() : 加载 history 列表中的下一个 URL
- window.history.back(n) : 加载 history 列表中的某个页面
- window.location.href: 返回完整的URL
- window.location.hash: 返回URL的锚部分
- window.location.pathname: 返回URL路径名