以下文件夹,是我们实现路由原理所需的
boinnie-router文件夹里,是我们要自己实现的vue-router的源码
router文件夹里,是关于路由的配置
views文件夹,是我们存放单文件vue组件的地方
app.js 是我们的根组件
main.js 是我们将组件实现挂载到页面的地方
将文件夹和文件创建好之后,就开始实现vue-router的原理
内容分别为
Home.vue
This is an home page
About.vue
This is an about page
router/index.js
import Vue from "vue";
import BonnieRouter from "../bonnie-router";
import Home from "../views/Home.vue";
import About from "../views/About.vue";
Vue.use(BonnieRouter);
const router = new BonnieRouter({
routes: [
{
path: "/",
component: Home
},
{
path: "/about",
component: About
}
]
});
export default router;
代码解析:
vue
和vue-router底层实现的bonnie-router
导入Home、About
Vue.use(BonnieRouter)
是关键,后面将会进行讲解App.vue
Home |
About
将根组件挂载到页面,将路由挂载到vue实例
上
main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
Vue.config.productionTip = false;
new Vue({
router,
render: h => h(App)
}).$mount("#app");
源码实现
bonnie-router/index.js
let Vue;
class BonnieRouter {
//接收Vue.use()传递过来的vue
static install(_Vue) {
Vue = _Vue;
}
constructor(options) {
// 接收传递过来的配置项
this.$options = options;
// 定义解析routes对象的变量
this.routeMap = {};
// 定义路由的地址
//这样写的目的是为数据驱动视图,数据改变时调用此处变量的地方也会做出相应变化
this.app = new Vue({
data: {
curr: "/"
}
});
//调用初始化方法
this.init();
}
}
export default BonnieRouter;
前面说过 Vue.use(BonnieRouter) 是关键,在源码实现的这个文件里,使用了 一个静态方法 static install
,使用的意义是为了接收前文Vue.use传递过来的Vue,因为在router/index.js里实例化BonnieRouter的时候传递了一个参数,所以在此文件里我们需要给构造函数添加一个参数options,然后我们需要调用一些初始化的方法,具体代码如下
init() {
// 1.创建路由事件
this.bindEvent();
// 2.创建 路由对象
this.createRouteMap();
// 3.创建组件
this.createComponent();
}
显而易见,我们还需要实现其他的一些方法,首先实现bindEvent()方法。在这个方法中,我们给window绑定了两个事件,在页面加载load
的时候和在路由hash发生变化
的时候都去调用另一个方法handleChange
bindEvent(){
// hash模式路由
//页面初始化时也调用handleChange方法
window.addEventListener("load", this.handleChange.bind(this));
//当路由发生改变时,执行handleChange方法
window.addEventListener("hashchange", this.handleChange.bind(this));
}
然后实现handleChange方法,代码如下。这个方法的目的就是根据从url栏获取到的hash变化修改上文里 默认路由地址this.app.curr
,若没有获取到hash变化,则赋值 ‘ / ’
handleChange() {
// 更新视图
// 1.先获取url栏里的hash
// 2.将#去掉 替换 上文里 this.app.curr的值
// 3.如果获取不到hash 默认赋值 /
let hash = this.getHash() || "/";
this.app.curr = hash;
}
//若果获取到url的hash,就会执行此方法
getHash() {
return location.hash.slice(1);
}
然后我们就要实现createRouteMap()方法,创建路由对象的方法。在构造函数里接收的参数在此处进行使用,这里的optios其实就是router/index.js里的 路由配置项 ,我们将其遍历放进一个对象里。key就是path,vaule就是路由的每个配置项
createRouteMap() {
this.$options.routes.forEach(item => {
this.routeMap[item.path] = item;
});
}
最后我们要实现createComponent方法。此时,我们已经配置好的路由,然后就要创建两个标签router-link router-view
,然后根据router-link的to属性
来改变hash值,根据hash的变化来动态改变router-view要显示的组件
createComponent() {
// 再此处创建 router-link router-view两个标签
Vue.component("router-view", {
render: h => {
//h 是创建虚拟dom 格式要以 ↓ 这种方式呈现\编写
//return h("div", { attr: { "data-id": "0" } }, this.$solts.default);
//但我们其实已经创建好了单文件组件,没必要在以上种方式去写 直接取出对应的组件就ok
var component = this.routeMap[this.app.curr].component; // 根据curr的值动态获取component组件
return h(component);
}
});
Vue.component("router-link", {
props: {
//将router-link 标签上 to属性的值取出
to: {
type: String,
required: true
}
},
render: function(h) {
return h(
"a",
{
attrs: {
href: `#${this.to}`
}
},
this.$slots.default
);
}
});
}
router-link的实现讲解:
就是创建一个vue组件,组件名叫’ router-link ‘,这个组件是一个a标签,给他添加一个href属性
,属性值就是调用的时候写的那个to属性
,to属性是必填的
router-view的实现讲解:
还是创建一个vue组件,组件名叫’ router-view ',我们要根据router/index.js里路由的配置,即根据path来显示相应的component
路由的底层实现就是如此。
总结:先是根据url地址栏的变化来触发hashchange事件,此时会修改this.app.curr的值。然后会顺序执行createRouteMap方法,来根据router/index.js里路由的配置项来生成一个路由对象的集合。再执行createComponent方法来创建两个组件 router-link 、 router-view。
router-view是根据this.app.curr的值参照routerMap对象集合来显示相应的组件。
router-link的作用就是用来修改url地址栏,然后开始下个轮回