VueRouter
本章我们要书写initComponents
这个方法,这个方法我们要实现两个组件router-link
、router-view
router-link 组件实现
/**
*
* @param {*} vue 的实例
*/
initComponents(vue) {
vue.component('router-link', {
props: {
to: String
},
template: '<a :href="to"><slot></slot></a>'
})
}
这么一看代码好像很简单哦!
没有太对的代码去做实现,我们就添加了一个a标签和一个插槽,没有其他的标记,但是我们该什么进行注册呢?又该怎么做呢?
我们要进行注册的时候,就需要执行initComponents
、createRouteMap
这两个方法,我们可以在 install的时候调用这两个方法
_vue.mixin({
beforeCreate() {
if (this.$options.router) {
_vue.prototype.$router = this.$options.router;
this.$options.router.init();
}
},
});
然后我们可以运行npm run serve
进行测试啦!
但是。。。
不仅没有渲染出来,还有一堆报错,无奈改改吧!
该怎么解决呢?? 下面我使用以下两个方法进行维护
vue.config.js
我们进入vue-cli官网中查看runtimeCompiler(运行时编译器)的配置
使用完整版的vue进行维护
// vue.config.js
/**
* @type {import('@vue/cli-service').ProjectOptions}
*/
module.exports = {
// 选项...
runtimeCompiler: true,
};
ok这样就显示出来了
render版本进行维护
initComponents(vue) {
vue.component("router-link", {
props: {
to: String,
},
// template: '<a :href="to"><slot></slot></a>',
render(h) {
return h(
"a",
{
attrs: {
href: this.to,
},
},
[this.$slots.default]
);
},
});
}
重新运行一边
router-view
这个组件相等于一个占位符,我们需要工具路由地址,并将它渲染到对应的位置来
vue.component("router-view", {
render(h) {
// 我们需要先找到当前的路由地址 self.data.current
// 找到对应路由所对应的组件
const component = self.routeMap[self.data.current];
// h函数可以将我们的路由转换成虚拟dom传递给页面
return h(component);
},
});
这样页面是可以正常渲染了,但是还需要一些的优化
优化
我们利用popstate
的方法进行页面回退的监听,然后对data
的current
进行修改
initEvent() {
window.addEventListener("popstate", () => {
this.data.current = window.location.pathname;
});
}
完整代码
let _vue = null;
export default class Router {
$options = null;
routeMap = null;
data = null;
// Vue.use 方法会调用对应组件的 install 方法
static install(Vue) {
// 1. 判断插件是否被安装
if (Router.install.installed) {
return;
}
Router.install.installed = true;
// 2. 将`install`方法中的参数`vue`插入到全局中
_vue = Vue;
// 3. 将创建`vue`实例时候传入的`Router`对象注入到`vue`实例上
// 使用混入
_vue.mixin({
beforeCreate() {
if (this.$options.router) {
_vue.prototype.$router = this.$options.router;
this.$options.router.init();
}
},
});
}
constructor(options) {
// 记录构造函数中传入的选项
this.options = options;
// 将来将options的规则解析到 routeMap中来,键值对的形式
this.routeMap = {};
// data是响应式的对象,使用vue的提供的方法,方便之后路由更新我们可以及时更新页面
this.data = _vue.observable({
current: "/",
});
}
/**
* @description 注册初始化方法
*/
init() {
this.createRouteMap();
this.initComponents(_vue);
this.initEvent();
}
/**
* @description 遍历路由规则,把路由规则解析成键值对的形式,储存到routeMap中
*/
createRouteMap() {
this.options.routes.forEach((route) => {
this.routeMap[route.path] = route.component;
});
}
/**
*
* @param {*} vue 的实例
*/
initComponents(vue) {
const self = this;
vue.component("router-link", {
props: {
to: String,
},
// template: '<a :href="to"><slot></slot></a>',
render(h) {
return h(
"a",
{
attrs: {
href: this.to,
},
on: {
click: this.clickHander,
},
},
[this.$slots.default]
);
},
methods: {
clickHander(e) {
history.pushState({}, "", this.to);
this.$router.data.current = this.to;
e.preventDefault();
},
},
});
vue.component("router-view", {
render(h) {
// 我们需要先找到当前的路由地址 self.data.current
// 找到对应路由所对应的组件
const component = self.routeMap[self.data.current];
// h函数可以将我们的路由转换成虚拟dom传递给页面
return h(component);
},
});
}
initEvent() {
window.addEventListener("popstate", () => {
this.data.current = window.location.pathname;
});
}
}
到这里我们就结束了模拟路由的history模式,其核心就是理由vue的插件的对应回调还有render的方法进行的一些操作,不过我这也是站在前人的肩膀上做的一些小总结。