vue-router是什么
这里的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。再通俗的说,vue-router就是WebApp的链接路径管理系统。
vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。
至于我们为啥不能用a标签,这是因为用Vue做的都是单页应用(当你的项目准备打包时,运行 npm run build时,就会生成dist文件夹,这里面只有静态资源和一个index.html页面),所以你写的标签是不起作用的,你必须使用vue-router来进行管理。
vue-router实现原理
vue插件的注册原理
Vue.use原理
实际上该方法执行 就是调用了plugin的install方法 并传入arguments参数(参数默认添加了当前的this)
src/core/global-api\use.ts定了initUse方法,在 index.js中 调用initUse1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import type { GlobalAPI } from 'types/global-api'
import { toArray, isFunction } from '../util/index'
export function initUse(Vue: GlobalAPI) {
Vue.use = function (plugin: Function | any) {
const installedPlugins =
this._installedPlugins || (this._installedPlugins = [])
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (isFunction(plugin.install)) {
plugin.install.apply(plugin, args)
} else if (isFunction(plugin)) {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
}
Vue-router install干了啥 (路由注册实现流程)
代码详见: https://github.dev/vuejs/vue-router
- 一、通过Vue.mixin混入beforeCreate destroyed钩子
beforeCreate中 1. 跟路由赋值 2. 调用init方法 3. defineReactive调用 响应式 4. 调用parentNode registerRouteInstance
destroyed中 调用parentNode registerRouteInstance 重置 - 二、通过Object.defineProperty在Vue.prototype 1. 定义$router(传入new Vue的router实例) 2. 定义$route 是实例的history.current
- 三、注册RouterView RouterLink组件
VueRouter对象
属性和方法
src/index.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 属性
app: any
apps: Array<any>
ready: boolean
readyCbs: Array<Function>
options: RouterOptions
mode: string
history: HashHistory | HTML5History | AbstractHistory
matcher: Matcher
fallback: boolean
beforeHooks: Array<?NavigationGuard>
resolveHooks: Array<?NavigationGuard>
afterHooks: Array<?AfterNavigationHook>
// 方法
init match
初始化逻辑
路由初始化的时机是在组件的初始化阶段,执行到beforeCreate钩子函数的时候 会执行router.init方法。然后又执行history.transitionTo的方法做路由的过渡
matcher - createMatcher过程
createMatcher –> createRouteMap: 遍历routes执行addRouteRecord (传入 pathList pathMap nameMap route)
matcher - match函数的实现
1 | function match ( |
最终调用_createRoute方法 结合location和Record 返回新的route
SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是: 更新视图而不重新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪一种方式。
路径切换
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫(2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
导航守卫
resolveQueue
1、Hash模式
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说hash 出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。hash 模式的原理是 onhashchange 事件(监测hash值变化),可以在 window 对象上监听这个事件。
2、History模式
由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入”mode: ‘history’”,这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。
1 | const router = new VueRouter({ |
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。
所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
1 | location / { |
注意:
这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。后端只管返回index.html,js去判断跳哪个组件页面,以及是否404
1 | const router = new VueRouter({ |
3、使用路由模块来实现页面跳转的方式
方式1:直接修改地址栏
方式2:this.$router.push(‘路由地址’)
方式3: <router-linkto=”路由地址”>
vue-router使用
1、下载 npm i vue-router --save
2、在main.js中引入 import VueRouter from 'vue-router';
3、安装插件 Vue.use(VueRouter);
4、创建路由对象并配置路由规则
1 | let router = new VueRouter({ |
5、将其路由对象传递给Vue的实例,options中加入 router:router
6、在app.vue中留坑 <router-view></router-view>
使用:
1 | //main.js |
最后别忘记在app.vue中留坑。
vue-router参数传递
声明式的导航 <router-link:to="...">和编程式的导航 router.push(…)都可以传参,本文主要介绍前者的传参方法,同样的规则也适用于编程式的导航。
1、用name传递参数
在路由文件src/router/index.js里配置name属性
1 | const router = new VueRouter({ |
模板里(src/App.vue)用 $route.name来接收
比如: <p></p>
2、通过 <router-link> 标签中的to传参
这种传参方法的基本语法:
1 | <router-link :to="{name:xxx,params:{key:value}}">valueString</router-link> |
比如先在src/App.vue文件中
1 | <router-link :to="{name: 'hi1', params: {username: 'jspang', id: '555'}}">Hi页面1</router-link> |
然后把src/router/index.js文件里给hi1配置的路由起个name,就叫hi1.
1 | { |
最后在模板里(src/components/Hi1.vue)用 $route.params.username进行接收.
1 | {{$route.params.username}}-{{$route.params.id}} |
3、利用url传递参数—-在配置文件里以冒号的形式设置参数。
我们在/src/router/index.js文件里配置路由
1 | const router = new VueRouter({ |
User组件代码通过$route.params.id
1 | const User = { |
在App.vue文件里加入我们的 <router-view>标签。这时候我们可以直接利用url传值了
1 | <router-link to="/params/198/jspang website is very good">params</router-link> |
4、使用path来匹配路由,然后通过query来传递参数
1 | <router-link :to="{ name:'Query',query: { queryId: status }}">router-link跳转Query</router-link> |
对应路由配置:
1 | { |
于是我们可以获取参数:
1 | this.$route.query.queryId |
vue-router配置子路由(二级路由)
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL中各段动态路径也按某种结构对应嵌套的各层组件,例如:
1 | /user/foo/profile /user/foo/posts |
借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。
1 | const router = new VueRouter({ |
要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
单页面多路由区域操作
// app.vue
1 | <router-view></router-view> |
// index.js
1 | import Vue from 'vue' |
vue-router钩子函数
1、全局的钩子
1 | beforeEach(to,from,next) |
2、组件内的导航钩子
组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的
1 | methods: {}, |
beforeRouteEnter 不能获取组件实例 this,因为当守卫执行前,组件实例被没有被创建出来,剩下两个钩子则可以正常获取组件实例 this
beforeRouteEnter获取到this实例
1 | beforeRouteEnter (to, from, next) { |
最后是完整的导航解析流程:
最后是完整的导航解析流程:
1、导航被触发
2、在失活的组件里调用离开守卫
3、调用全局的 beforeEach 守卫
4、在重用的组件里调用 beforeRouteUpdate 守卫
5、在路由配置里调用 beforEnter
6、解析异步路由组件
7、在被激活的组件里调用 beforeRouteEnter
8、调用全局的 beforeResolve 守卫
9、导航被确认
10、调用全局的 afterEach 钩子
11、触发 DOM 更新
12、在创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数
1 | import Vue from "vue"; |
当router的mode为history时,去掉#
1 | router.beforeEach((route, redirect, next) => { |
这样就做好了登录拦截.我们只需在main.js中引入router就可以了.
参考:
https://router.vuejs.org/zh/
https://www.jianshu.com/p/96cfc1b9ff21
https://ustbhuangyi.github.io/vue-analysis/v2/prepare/