Vue-Router的使用
Vue Router
是Vue.js官方的路由管理器。它和 Vue.js
的核心深度集成。
Vue Router的核心使用步骤
步骤一: 使用vue-router插件 router.js
import Router from 'vue-router';
Vue.use(Router);
使用一个插件需要执行 Vue.use(Plugin)
,实际在 Vue 中的 use
调用 Plugin
中的 install
方法,并将 Vue
实例当做参数传入 install
方法中
步骤二: 创建Router实例 router.js
// routes为自定义的路由
export default new Router(routes);
Router
是一个构造函数,需要监听Url的变化,并进行响应,替换当前的显示组件
步骤三: 在根组件上添加盖实例: main.js
import router from './router';
new Vue({
router
}).$mount('#app');
步骤四: 添加路由视图 APP.vue
Vue-Router
需要实现两个全局组件router-link
和router-view
<router-view></router-view>
步骤五: 导航 xx.vue
<router-link to = "/">Home</router-link>
路由跳转
this.$router.push('/about');
获取路由参数
const { id } = this.$route.params;
Vue Router源码实现及解析
VueRouter需要实现的功能:
- 实现一个
VueRouter
的类并挂载一个install
方法,以便给Vue.use
使用,接收vue实例
当做参数 - 实现两个全局组件
router-link
和router-view
- 处理路由选项,监控Url变化并响应
- 需要将router实例挂载到Vue实例上 实现
Vue.prototype.$router
以及Vue.prototype.$route
index.js
1、实现一个VueRouter
的类,使用者通过进行new Router
将 routes 路由表作为参数传递给VueRouter
。并挂载一个 install
方法,给Vue运行 将当前插件挂载到全局。
let Vue;
class VueRouter {
constructor(options) {
this.$options.options;
}
}
// vue插件需要在外部使用Vue.use 进行挂载
// Vue.use会自动执行插件的 install 方法并将Vue实例当做参数传递进来
VueRouter.install = function(_vue) {
Vue = _vue;
// 使用mixin,beforeCreate获取顶层vue实例 拿到router
// 1、挂载$router
Vue.mixin({
beforeCreate() {
// 只有根组件才有router选项, 且在viewRouter组件中也需要用到
if (this.$options.router) {
// 方便外部 调用$router -> this.$router
Vue.prototype.$router = this.$options.router;
}
},
});
// 2、实现两个组件
Vue.component("router-link", RouterLink);
Vue.component("router-view", RouterView);
}
2、需要监听路由的变化,保存当前命中url对应的路由信息。扩展VueRouter
的功能
class VueRouter {
constructor(options) {
this.$options.options;
// 当前路径的地址
this.current = window.location.hash.slice(1) || "/";
// 匹配到的组件
Vue.util.defineReactive(this, 'matched', []);
window.addEventListener("hashchange", this.onHashChange.bind(this));
window.addEventListener("load", this.onHashChange.bind(this));
// 匹配路由
this.match();
}
match(routes) {
if (!routes) {
routes = this.$options.routes;
}
for (let route of routes) {
// 匹配首页
if (route.path === "/" && this.current === "/") {
this.matched.push(route);
return;
}
let pathArr = this.current.split("/");
// 匹配其他。 /about/foo -> 需要匹配两个 /about 以及 /about/foo
if (route.path !== "/" && pathArr.includes(route.path.slice(1))) {
this.matched.push(route);
if (route.children && route.children.length > 0) {
this.match(route.children);
}
}
}
}
onHashChange() {
this.current = window.location.hash.slice(1);
this.matched = [];
this.match();
}
}
link.js
router-link
比较简单,本质上其实就是一个简单的 a 标签。接收一个to
属性,跳转都当前的to
属性代表的新地址。
export default {
props: {
to: {
require: true,
type: String,
},
},
render(h) {
return h(
"a",
{
attrs: {
href: "#" + this.to,
},
},
[this.$slots.default]
);
},
};
view.js
路由的展示主要是子组件的展示,需要将每一个RouterView
进行标记。若当前组件中有多个RouterView
嵌套,则可以通过遍历与当前标记结合的方式判断当前RouterView
在嵌套组件中的层级,以便获取可匹配的路由信息
export default {
name: 'RouterView',
// 在当前组件中 可以通过this.$router获取router实例中的数据: install中已将router实例挂载到了Vue根组件上
render(h) {
// 标记当前组件为RouterView组件
this.$vnode.data.routerView = true;
// 为了获取当前组件的层级,matched数组中: 比如/about/foo 中存储第一位为父组件/about的路由信息,后面为/abou/foo的子路由信息 层级遍历
let depth = 0;
let parent = this.$parent;
// 判断当前组件的层级
while (parent) {
// 获取父级元素的data属性 判断 父组件是否为 routerView组件
// 若父级组件中存在routerView组件 则当前组件层级需要+1
const vnodeData = parent.$vnode ? parent.$vnode.data : {};
if (vnodeData.routerView) {
depth++;
}
parent = parent.$parent;
}
// 获取匹配到的组件
const matched = this.$router.matched;
let component = null;
// 获取当前层级匹配的路由信息
const route = matched[depth];
if(route) {
component = route.component;
}
return h(component);
}
}