近期被问到一个问题,在你们项目中使用的是Vue的SPA(单页面)还是Vue的多页面设计?
这篇文章主要围绕Vue的SPA单页面设计展开。 关于如何展开Vue多页面设计请点击查看。
vue-router是什么?
首先我们需要知道vue-router是什么,它是干什么的?
这里指的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。 换句话说,vue-router就是WebApp的链接路径管理系统。
vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。
那与传统的页面跳转有什么区别呢?
1.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。
2.传统的页面应用,是用一些超链接来实现页面切换和跳转的。
在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。
至于为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,所以你写的标签是不起作用的,必须使用vue-router来进行管理。
vue-router实现原理
SPA(single page application):单一页面应用程序,有且只有一个完整的页面;当它在加载页面的时候,不会加载整个页面的内容,而只更新某个指定的容器中内容。
单页面应用(SPA)的核心之一是:
1.更新视图而不重新请求页面;
2.vue-router在实现单页面前端路由时,提供了三种方式:Hash模式、History模式、abstract模式,根据mode参数来决定采用哪一种方式。
路由模式
vue-router 提供了三种运行模式:
● hash: 使用 URL hash 值来作路由。默认模式。
● history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。
● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。
Hash模式
vue-router 默认模式是 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会去重新加载。
hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分(/#/..),浏览器只会加载相应位置的内容,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置; 所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据 。
History模式
HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;
由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: 'history'",这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
//main.js文件中 const router = new VueRouter({ mode: 'history', routes: [...] })
当使用 history 模式时,URL 就像正常的 url,例如 yoursite.com/user/id,比较好… 不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问
所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
export const routes = [ {path: "/", name: "homeLink", component:Home} {path: "/register", name: "registerLink", component: Register}, {path: "/login", name: "loginLink", component: Login}, {path: "*", redirect: "/"}]
此处就设置如果URL输入错误或者是URL 匹配不到任何静态资源,就自动跳到到Home页面。
abstract模式
abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。
根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (当然,你也可以明确指定在所有情况下都使用 abstract 模式)
vue-router使用方式
1:下载npm i vue-router -S
**2:在main.js中引入 ** import VueRouter from 'vue-router';
3:安装插件Vue.use(VueRouter);
4:创建路由对象并配置路由规则
let router = new VueRouter({routes:[{path:'/home',component:Home}]});
5:将其路由对象传递给Vue的实例,options中加入 router:router
6:在app.vue中留坑
<router-view></router-view>
具体实现请看如下代码:
//main.js文件中引入 import Vue from 'vue'; import VueRouter from 'vue-router'; //主体 import App from './components/app.vue'; import index from './components/index.vue' //安装插件 Vue.use(VueRouter); //挂载属性 //创建路由对象并配置路由规则 let router = new VueRouter({ routes: [ //一个个对象 { path: '/index', component: index } ] }); //new Vue 启动 new Vue({ el: '#app', //让vue知道我们的路由规则 router: router, //可以简写router render: c => c(App), })
最后记得在在app.vue中“留坑”
//app.vue中 <template> <div> <!-- 留坑,非常重要 --> <router-view></router-view> </div> </template> <script> export default { data(){ return {} } } </script>
vue-router源码分析
我们先来看看vue的实现路径。
在入口文件中需要实例化一个 VueRouter 的实例对象 ,然后将其传入 Vue 实例的 options 中。
export default class VueRouter { static install: () => void; static version: string; app: any; apps: Array<any>; ready: boolean; readyCbs: Array<Function>; options: RouterOptions; mode: string; history: HashHistory | HTML5History | AbstractHistory; matcher: Matcher; fallback: boolean; beforeHooks: Array<"color: #ff0000">HashHistory"htmlcode">
window.addEventListener("hashchange",funcRef,false)"htmlcode">
export class HashHistory extends History { constructor (router: Router, base: "htmlcode">transitionTo (location: RawLocation, onComplete"htmlcode">supportsPushState export const supportsPushState = inBrowser && (function () { const ua = window.navigator.userAgent if ( (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) && ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('Chrome') === -1 && ua.indexOf('Windows Phone') === -1 ) { return false } return window.history && 'pushState' in window.history })()其实所谓响应式属性,即当_route值改变时,会自动调用Vue实例的render()方法,更新视图。
$router.push()-->HashHistory.push()-->History.transitionTo()-->History.updateRoute()-->{app._route=route}-->vm.render()
监听地址栏
在浏览器中,用户可以直接在浏览器地址栏中输入改变路由,因此还需要监听浏览器地址栏中路由的变化 ,并具有与通过代码调用相同的响应行为,在HashHistory中这一功能通过setupListeners监听hashchange实现:
setupListeners () { window.addEventListener('hashchange', () => { if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { replaceHash(route.fullPath) }) }) }HTML5History
History interface是浏览器历史记录栈提供的接口,通过back(),forward(),go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
export class HTML5History extends History { constructor (router: Router, base: "_blank" href="https://link.juejin.im/">oursite.com/#/user/id //如请求,只会发送http://oursite.com/所以hash模式下遇到根据url请求页面不会有问题
而history模式则将url修改的就和正常请求后端的url一样(history不带#)
oursite.com/user/id
如果这种向后端发送请求的话,后端没有配置对应/user/id的get路由处理,会返回404错误。
官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。同时这么做以后,服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。或者,如果是用 Node.js 作后台,可以使用服务端的路由来匹配 URL,当没有匹配到路由的时候返回 404,从而实现 fallback。
两种模式比较
一般的需求场景中,hash模式与history模式是差不多的,根据MDN的介绍,调用history.pushState()相比于直接修改hash主要有以下优势:
"htmlcode">
//abstract.js实现,这里通过栈的数据结构来模拟路由路径 export class AbstractHistory extends History { index: number; stack: Array<Route>; constructor (router: Router, base: "_blank" href="https://github.com/vuejs/vue-router/tree/dev/src">点击查 看要是喜欢可以给我一个star,github
总结
以上所述是小编给大家介绍的vue-router原理浅析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!