好家伙,

 

今天来手写我们的老伙计vue-router,

 

1.替换router

新开一个项目,并使用我们手写的router

 

2.大致结构

let Vue; // 保存vue的构造函数 class VueRouter { constructor(options) { } } VueRouter.install = (_Vue) => { Vue = _Vue; //备份Vue  Vue.mixin({ beforeCreate() { if (this.$options.router) { Vue.prototype.$router = this.$options.router; } } }) Vue.component("router-link", {}); //实现思路,找到对应的组件并将它渲染出来 Vue.component("router-view", {}); } export default VueRouter;

  2.1.这里使用Vue.mixin(),使任何组件都能调用到router

  2.2.Vue = _Vue,一会要用到Vue的方法,将某个变量变为响应式的

 

 

 

3.router-link实现

  3.1.组件的使用

 

  3.2.实现

Vue.component("router-link", { props: { to: { type: String, required: true, }, }, render(h) { return h("a", { attrs: {  href: `#${this.to}` } }, this.$slots.default); } });

  重点来了,为什么要用个#?

  在这段代码中,使用 # 的目的是为了在单页面应用(SPA)中实现基于 hash 的路由。在传统的单页面应用中,通过改变 URL 中的 hash   部分来切换页面内容,而不会导致整个页面重新加载。这种方式被称为 hash 模式路由。

  具体来说,当用户点击带有 # 的链接时,浏览器会更新 URL 中的 hash 部分,但不会触发整个页面的重新加载,而是根据新的 hash 值来  更新页面内容,从而实现页面的切换和路由导航。

  在 Vue 中,使用 # 可以帮助我们正确地处理 hash 模式路由。

 

4.实现router-view

Vue.component("router-view", { render(h) { let component = null; //获取当前路由所对应的组件并将它渲染出来 const current = this.$router.current; const route = this.$router.$options.routes.find((route) => route.path === current ) // const route = this.$router.$options.routes.find((route) => // {route.path === current} // ) //!!错误 //若使用箭头函数块{},必须要有返回值  console.log(route, current) if (route) { component = route.component } return h(component); } });

  总体上看,代码逻辑非常简单,在router中找到匹配的组件,然后返回相应的组件就好了,但问题来了,我怎么知道当前页面current是什么?

 

5.实现VueRouter

class VueRouter { constructor(options) { this.$options = options; this.current = "/"; let initial = window.location.hash.slice(1) || "/" Vue.util.defineReactive(this, "current", initial) window.addEventListener("hashchange", () => { this.current = window.location.hash.slice(1) || "/" console.log(this.current) }) } }

     第一步:开始我们默认this.current = “/”; 即首页,

  第二步:将current变为响应式数据,

  第三步:让current动态获取当前路由的值

问:为什么要将current变为响应式数据?

答:render的更新依赖于响应式数据curren,若current不为响应式数据,current变化,render不会重新渲染

 

搞定

 

6.源码

let Vue; // 保存vue的构造函数 class VueRouter { constructor(options) { this.$options = options; this.current = "/"; let initial = window.location.hash.slice(1) || "/" Vue.util.defineReactive(this, "current", initial) window.addEventListener("hashchange", () => { this.current = window.location.hash.slice(1) || "/" console.log(this.current) }) } } VueRouter.install = (_Vue) => { Vue = _Vue; //备份Vue  Vue.mixin({ beforeCreate() { if (this.$options.router) { Vue.prototype.$router = this.$options.router; } } }) Vue.component("router-link", { props: { to: { type: String, required: true, }, }, render(h) { return h("a", { attrs: { href: `#${this.to}` } }, this.$slots.default); } }); //实现思路,找到对应的组件并将它渲染出来 Vue.component("router-view", { render(h) { let component = null; //获取当前路由所对应的组件并将它渲染出来 const current = this.$router.current; // const route = this.$router.$options.routes.find((route) => // route.path === current // ) const route = this.$router.$options.routes.find((route) => {return route.path === current} ) //!!错误 //若使用箭头函数块{},必须要有返回值  console.log(route, current) if (route) { component = route.component } return h(component); } }); } export default VueRouter;

 

 

7.补充

一个小小bug

const route = this.$router.$options.routes.find((route) => route.path === current )

不能写成

const route = this.$router.$options.routes.find((route) => {route.path === current} )

第一段代码使用了简洁的箭头函数写法,直接返回了 route.path === current 的结果。这种写法适用于只有一行代码的情况,箭头函数会自动将这一行代码的结果作为返回值。因此,第一段代码会返回第一个满足条件 route.path === current 的 route 对象。

第二段代码使用了代码块 {} 包裹起来,但在代码块中没有显式返回值。这种情况下,箭头函数不会自动返回代码块中的结果,需要手动添加 return 关键字来返回值。因此,第二段代码中的箭头函数没有正确返回值,会导致代码出错。

所以,若要使用代码块 {}

const route = this.$router.$options.routes.find((route) => {return route.path === current} )