一、路由更新的原理图:
前三步是vue-router实现的,后四步是vue实现的
graph TD
url改变 --> 触发监听事件 --> 改变vue-router里面的current变量 --> 改变vue中_route变量 --> vue监听_route的变化 --> 获取到最新的组件 --> render新组件
二、main.js 入口文件
初始化,把router实例挂载到vue的根实例上, main.js 中是根实例,App.vue 子实例, 其余的是孙子实例
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
name: 'main',
router,
render: h => h(App)
}).$mount('#app')
三、router / index.js
作用:(1)vue.use 注册插件 (2)router实例化过程
import Vue from 'vue'
import VueRouter from '../vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
console.log('router.js');
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About,
children: [
{
path: 'a',
name: 'AboutA',
component: {
render() {
return <h1>about a</h1>
}
}
},
{
path: 'b',
name: 'AboutB',
component: {
render() {
return <h1>about b</h1>
}
}
}
]
}
]
const router = new VueRouter({
routes
})
export default router
1、vue.use 注册插件,install.js
use的实现过程:
(1) 执行里面的方法
(2)如果有install这个属性 ,并且这个属性是个方法,不会执行方法本身,会执行install方法
(3)install这个方法的第一个参数是vue
install作用:先通过vue.mixin方法在生命周期钩子中混入注册属性、组件的方法,等vue实例化时,直接调用,注册全局属性和组件
(1)注册全局属性:router
(2)注册全局组件:router-view router-link
(3)初始化init: 根据当前路由,初始化页面,渲染对应组件,创建监听,后续路由改变时,页面改变
2、router实例化过程
const router = new VueRouter({
routes
})
export default router
四、vue-router内部实现
router实例上的属性和方法:matcher、mode、history、init()、push()、replace()等等
1、matcher的作用
//嵌套路由数据
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About,
children: [
{
path: 'a',
name: 'AboutA',
component: {
render() {
return <h1>about a</h1>
}
}
},
{
path: 'b',
name: 'AboutB',
component: {
render() {
return <h1>about b</h1>
}
}
}
]
}
]
// 数据扁平化后
const pathList = ['/', '/about', '/about/a', '/about/b'] 存储所有路径的数组
const pathMap = {
'/': '记录1',
'/about': '记录2',
'/about/a': '记录3',
'/about/b': '记录4'
}
record = {
path: '/about/a',
component: A组件,
parent: {
.....记录
}
}
// 路径的映射关系表
2、 init方法:根据当前路径显示到对应的页面
(1)渲染对应的组件
(2)创建监听,后续路由变化会重新跳转页面
(3)路由变化后,更改vue中对应的_route的值,触发响应式,从而页面更新 Vue.util.defineReactive(this, '_route', this._router.history.current)
3、create-matcher 内部实现:addRoutes方法、match方法
该方法是创建匹配器,根据不同的路由,匹配对应的组件,先把用户传入的路由信息扁平化,创建路由映射表,例如: /about/a
const pathList = ['/', '/about', '/about/a', '/about/b'] 存储所有路径的数组
const pathMap = { // 路径的映射关系表
'/': '记录1',
'/about': '记录2',
'/about/a': '记录3',
'/about/b': '记录4'
}
其中pathList、pathMap是createRouteMap方法格式化的
(1)addRoutes方法:做权限管理时,需要动态添加路由
用途:接口返回来的动态路由信息,重新整合到映射表中(pathList、pathMap)
import createRouteMap from "./create-route-map"
export default function createMatcher(routes) {
let {pathList, pathMap } = createRouteMap(routes)
function addRoutes(routes) {
// routes是新的路由信息
createRouteMap(routes, pathList, pathMap)
}
}
(2)match方法
找到对应的记录,并且要根据记录产生一个记录匹配数组,此处借助createRoute方法
例如:/about/a路径时,需要先渲染/about父组件,再在父组件的router-view中渲染子组件a
// pathMap = { '/about/a': {path: '/about/a', component: a组件, parent: xx } }
// record = {
// path: '/about/a',
// component: A,
// parent: {
// path: '/about',
// component: About,
// ...
// }
// }
function match(location) {
let record = pathMap[location]
let local = {
path: location
}
if (record) { // 如果找到了当前的记录,会根据当前的记录产生一个route
return createRoute(record, local)
} else {
return createRoute(null, local)
}
}
function createRoute(record, location) {
// location ===> { path: "/about/a" }
let res = []
if (record) {
while(record) {
res.unshift(record)
record = record.parent // 一直找父级,添到最前边
}
}
return {
...location,
matched: res
}
}
// 最终生成的数据格式为:
{
path: '/about/a',
matched: [record1, record2] // record1对应about的信息, record2对应aboutA
}
所以match的最终目的是生成下图所示结构的数据
4、history内部方法
history路由系统内部分三个类:公共类、hash类、h5类
hash类:getCurrentLocation()、setupHashLister()
base类:current当前路径、transitionTo()、updateRoute()等
// hash.js
getCurrentLocation() {
return getHash()
}
setupHashLister() { // 创建监听,路由变化后的需要做的
window.addEventListener('hashchange', () => {
this.transitionTo(getHash()) // 当路由变化后,需要跳转到当前hash对应的页面
})
}
// 在router初始化时,有个属性history,根据模式,生成相应的路由系统
if (this.mode === 'hash') {
this.history = new HashHistory(this)
}
// init初始化时,调用了history的transitionTo方法
history.transitionTo(路径,设置监听的回调)
(1)transitionTo方法逻辑
- 判断路径,跟当前的是否一致,不一致,更新路由
- 回调函数,创建监听
transtionTo(location, onComplete) {
let route = this.router.match(location) // 根据当前路径找到要渲染的组件
if (this.current.path === location && route.matched.length === this.current.matched.length) {
return // 如果是相同路径,就不进行跳转了
} else {
// 更新路径
this.updateRoute(route) // 只有_route更新了,才能触发响应式,视图才可以更新
}
onComplete && onComplete()
}
updateRoute(route) {
this.current = route // this指的的history类,但是vue中的_route还没改变
this.cb && this.cb(route) // 路径变化会将最新路径传递给listen
}
listen(cb) {
this.cb = cb // 先存储方法
}
之前在init中调的listen,存储的回调方法
// 发布订阅模式
// history.listen((route) => {
// app._route = route // 主要目的是更改_route,视图才可以更新
// })
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。
在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。
本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。
除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。
在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!