什么是Vue-Router?
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue.js 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
如果对路由概念还是不懂的可以看我的另一篇文章 小飞棍来咯
接下来我们在创建vue的项目,剔除掉用vuerouter模仿这自己实现一个简单的vuerouter,我们还会实现router-link和router-view,
创建vue工程
vue create 【demo】
当然index.js文件内容还是不变的
import Vue from "vue";
import VueRouter from "./zvue-router";
import Home from "../views/Home.vue";
// 1.应用插件
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
];
// 2.创建实例
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
export default router;
接下来创建另一个文件夹zvue-router.js
import Link from "./zrouter-link";
import View from "./zrouter-view";
let Vue;
// 1.实现一个插件:挂载$router
class KVueRouter {
constructor(options) {
this.$options = options;
console.log("[Log] this.$options-->", this.$options);
}
}
KVueRouter.install = function(_Vue) {
// 保存构造函数,在KVueRouter里面使用
Vue = _Vue;
};
export default KVueRouter;
可以看出文件的结构目前非常的简单,在router进行挂载的时候会调用文件里的install方法并且会把Vue的实例传进去。那么问题来了我们如何挂载$router和怎么获取根实例中的router选项,其实我们可以使用Vue.mixin全局混入。
Vue.mixin({
beforeCreate() {
Vue.prototype.$router = this.$options.router;
},
});
但是如果这样写会导致一个问题,就是 Vue.prototype.$router = this.$options.router;会在挂载的时候多次调用,其实我们在根组件挂载的时候就可以直接赋值不需要进行多次赋值所以我们需要去判断当是根组件的时候进行挂载即可。
Vue.mixin({
beforeCreate() {
// 确保根实例的时候才执行
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
目前暂时install方法写完,接下来我们看constructor构造体里面的,我们需要一个current记录下当前的路径,并且在一开始去监听hashchange和load刷新,我们在创建一个路由表这样是为了让后面的router-view进行优化罢了
constructor(options) {
this.$options = options;
console.log("[Log] this.$options-->", this.$options);
this.current = "/";
// 监控url变化
window.addEventListener("hashchange", null);
window.addEventListener("load", null);
// 创建一个路由映射表
this.routeMap = {};
options.routes.forEach((route) => {
this.routeMap[route.path] = route;
});
}
接下来我们实现router-link和router-view,这时候需要回到install函数里,在这个函数里我们需要创建这个两个组件,创建一个zrouter-link.js,在router-link里我们需要接收一个to的参数,其实router-link本质上还是利用了a标签,对于render不熟悉可以去回顾回顾
export default {
props: {
to: {
type: String,
required: true
},
},
render(h) {
// <a href="#/about">abc</a>
// <router-link to="/about">xxx</router-link>
// h(tag, data, children)
console.log(this.$slots);
return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)
// return <a href={'#' + this.to}>{this.$slots.default}</a>
}
}
这里解释下为什么用render而不是template。
vue有两种形式的代码 compiler(模板)模式和runtime模式(运行时),我们使用的是运行时的版本所以不能用不了template
如果你坚持使用template会报错报错如下:
如果你想用template你可以自己更换版本
同理我们创建
zrouter-view.js文件
export default {
render(h) {
//获取path对应的component
const { routeMap, current } = this.$router;
console.log("[Log] this.$router-->", this.$router, routeMap[current]);
const component = routeMap[current].component || null;
return h(component);
},
};
我们在构造体注册了路由表和current所以我们这个时候对于代码简化是非常的友好,目前全都写好了,但是运行时发现,点击只切换了一次,这是因为current是不响应式的所以我们需要改写
// 需要创建响应式的current属性
// 利用Vue提供的defineReactive做响应化
// 这样将来current变化的时候,依赖的组件会重新render
Vue.util.defineReactive(this, "current", "/");
//zvue-router.js完整代码
import Link from "./zrouter-link";
import View from "./zrouter-view";
let Vue;
// 1.实现一个插件:挂载$router
class ZVueRouter {
constructor(options) {
this.$options = options;
console.log("[Log] this.$options-->", this.$options);
// 需要创建响应式的current属性
// 利用Vue提供的defineReactive做响应化
// 这样将来current变化的时候,依赖的组件会重新render
Vue.util.defineReactive(this, "current", "/");
// 下面那个不是响应式
// this.current = "/";
// this.app = new Vue({
// data() {
// return {
// current: '/'
// }
// }
// })
// 监控url变化
window.addEventListener("hashchange", this.onHashChange.bind(this));
window.addEventListener("load", this.onHashChange.bind(this));
// 创建一个路由映射表
this.routeMap = {};
options.routes.forEach((route) => {
this.routeMap[route.path] = route;
});
}
onHashChange() {
console.log(window.location.hash);
this.current = window.location.hash.slice(1);
}
}
ZVueRouter.install = function(_Vue) {
// 保存构造函数,在ZVueRouter里面使用
Vue = _Vue;
// 挂载$router
// 怎么获取根实例中的router选项
Vue.mixin({
beforeCreate() {
// 确保根实例的时候才执行
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
// 任务2:实现两个全局组件router-link和router-view
Vue.component("router-link", Link);
Vue.component("router-view", View);
};
export default ZVueRouter;
源码位置:github.com/deedhei/vue… 希望帅哥美女们给个star