mini-vue-route(1)

准备工作

使用 pnpm创建一个vue项目

pnpm create vite
.pnpm-store/v3/tmp/dlx-3305              |   +1 +
.pnpm-store/v3/tmp/dlx-3305              | Progress: resolved 1, reused 0, downloaded 1, added 1, done
✔ Project name: … mini-vue-router
✔ Select a framework: › Vue
✔ Select a variant: › TypeScript

Scaffolding project in /Users/x/Desktop/workspace/mini-vue-router...

Done. Now run:

  cd mini-vue-router
  pnpm install
  pnpm run dev

新建两个vue文件作为我们的页面组件

<template>
  <div>
    this is about 
  </div>
</template>

<script setup lang="ts">
</script>
<template>
  <div>
    this is home
  </div>
</template>

<script setup lang="ts">
</script>

image.png

实现

新建src/router/index.ts文件,先定义出几个常用的api导出



const useRouter = () => {

}

const useRoute = () => {

}

const createRouter = () => {

}

const createWebHashHistory = () => {

}


export { useRouter, useRoute, createRouter, createWebHashHistory }

首先,我们使用vue-router时会先使用createRouter创建一个router,然后使用Vue.use(router),先实现这部分

class Router {

  constructor() {}

  install(app) {

    // 注册router
    
    // 注册router-view与router-link组件
  }
}

注册完成后,vue-router可以在任何地方使用,所以可以使用provide注入router,每次获取时使用inject获取

import { inject } from 'vue'

if (!inject) {
  console.error('inject is not defined, please check vue version')
}

const ROUTER_KEY = Symbol('router')

const useRouter = () => {
  return inject(ROUTER_KEY)
}


class Router {

  constructor(options) {
    
  }

  install(app) {

    // 注册router
    
    // 注册router-view与router-link组件


    // 注入router
    app.provide(ROUTER_KEY, this)
  }
}

然后是 createRouter, createRouter 接收options,并返回router实例
image.png

const createRouter = (options) => {
  return new Router(options)
}

继续完善,options中我们常用的配置有routeshistory

routes 是路由描述的数组,history 是路由模式: 通过使用createWebHashHistory(hash模式)createWebHistory(history模式)

这里先接收这两个参数
image.png

然后完善createWebHashHistory函数,这是创建hash模式的API,我们先返回一个hashchange
的监听:

const createWebHashHistory = () => {
  function historyListeners(cb) {
    window.addEventListener('hashchange', cb)
  }

  return {
    historyListeners
  }
}

这样在Router类的constructor中就可以实时获取到更新后的url, 这个url使用ref存起来

class Router {

  constructor(options) {
    this.history = options.history
    this.routes = options.routes

    // 当前的url
    const currentUrl = window.location.hash.slice(1) || '/'
    this.currentUrl = ref(currentUrl)

    // 监听后更新url
    this.history.historyListeners(() => {
      this.currentUrl.value = window.location.hash.slice(1);
    })
  }

  history: any
  routes: any[]
  currentUrl: Ref<string>

  install(app) {

    // 注册router
    
    // 注册router-view与router-link组件


    // 注入router
    app.provide(ROUTER_KEY, this)
  }
}

/router目录中新建router.ts,引入我们写的api使用:

import { createRouter, createWebHashHistory } from './index'

import Home from "../components/Home.vue";
import About from "../components/About.vue";

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/about",
    name: "About",
    component: About,
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});
export default router;

main.ts中注册:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/router'

createApp(App).use(router).mount('#app')

image.png

image.png

然后我们来实现router-linkrouter-view

router-link:

<template>
  <a :href="'#' + props.to">
    <slot></slot>
  </a>
</template>

<script setup lang="ts">
import { defineProps } from "vue";

// 定义组件的 props
const props = defineProps({
  to: {
    type: String, 
    required: true
  },
});
</script>

router-view:

<template>
  <component v-if="component" :is="component"></component>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useRouter } from "./index";

let router = useRouter();

const component = computed(() => {
  const route = router.routes.find(
    (route) => route.path === router.currentUrl.value
  );
  return route?.component;
});
</script>

这里先简单实现,不考虑各种嵌套路由子路由的情况。
image.png

image.png

router/index中全局注册这两个组件

import { App, inject, ref, Ref } from 'vue'
import RouterView from './RouterView.vue' ~!这里 ++
import RouterLink from './RouterLink.vue' ~!这里 ++

if (!inject) {
  console.error('inject is not defined, please check vue version')
}

const ROUTER_KEY = Symbol('router')

const useRouter = () => {
  return inject<Router>(ROUTER_KEY)!
}

const useRoute = () => {

}

const createRouter = (options: any) => {
  return new Router(options)
}

const createWebHashHistory = () => {
  function historyListeners(cb: any) {
    window.addEventListener('hashchange', cb)
  }

  return {
    historyListeners
  }
}

type RouterOptions = {
  history: any,
  routes: any[]
}

class Router {

  constructor(options: RouterOptions) {
    this.history = options.history
    this.routes = options.routes

    // 当前的url
    const currentUrl = window.location.hash.slice(1) || '/'
    this.currentUrl = ref(currentUrl)

    // 监听后更新url
    this.history.historyListeners(() => {
      this.currentUrl.value = window.location.hash.slice(1);
    })
  }

  history: any
  routes: any[]
  currentUrl: Ref<string>

  install(app: App) {

    // 注册router
    
    // 注册router-view与router-link组件


    // 注入router
    app.provide(ROUTER_KEY, this)

    app.component('router-view', RouterView)       ~!这里 ++
    app.component('router-link', RouterLink)       ~!这里 ++
  }
}


export { useRouter, useRoute, createRouter, createWebHashHistory }

注册后在helloword组件中使用:

image.png

image.png
image.png

可以正常使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值