likes
comments
collection
share

Vue 实战(一)手写hash-router

作者站长头像
站长
· 阅读数 21

Vue中封装了很多好用的库,其中router路由源码相对简单,拿出来学习,手写一下,练练手

实现Router的基本设计

import RouterLink from "./RouterLink.vue"
import RouterView from "./RouterView.vue"
import { inject, ref } from "vue"

let singletonInstance;

export const createRouter = (options) => {
    if (!singletonInstance) {
        singletonInstance = new Router(options);
    }
    return singletonInstance;
};
//单例模式

export const createWebHashHistory = () => {
    function bindEvents(fn) {
        window.addEventListener('hashchange', fn)
    }
    // history 对象
    return {
        url: window.location.hash.slice(1) || '/',
        bindEvents
    }
}
//标记一下  router 向全世界暴露
const ROUTER_KEY = '__router__'
// use 开头的是一派hooks 函数式编程
export const useRouter = () => {
    return inject(ROUTER_KEY)
}

class Router{

    constructor(options) {
        this.history = options.history
        this.routes = options.routes
        this.current = ref(this.history.url)
        this.history.bindEvents(() => {
            this.current.value = window.location.hash.slice(1)
        })
    }
    install(app){
        //全局声明有一个router 全局使用的对象
        app.provide(ROUTER_KEY,this)
        app.component('router-link',RouterLink)
        app.component('router-view',RouterView)
    }
}

思考:考虑设计一个vue-router就是去不刷新整个页面的情况下更新 URL,实现视图的切换和状态的管理。这就是SPA (single page application)。Hash 模式:使用 URL 的 hash(#)部分来模拟一个完整的 URL,当 URL 改变时,不会发送请求到服务器。

  • 使用单例模式创建路由器实例,保证全局唯一。

  • 利用 hash 模式管理历史,通过监听 hash 变化更新当前路由状态。

  • 通过依赖注入机制,在 Vue 应用中全局注册路由器,并提供访问路由器实例的方法。

  • 实现核心的路由管理类,包含路由状态管理和组件注册功能。

  • 提供 router-linkrouter-view 组件,用于路由导航和视图渲染。

第一步:

使用单例模式创建路由器实例,保证全局唯一。

let singletonInstance;

export const createRouter = (options) => {
    if (!singletonInstance) {
        singletonInstance = new Router(options);
    }
    return singletonInstance;
};
//单例模式

向外导出一个createRouter的函数,根据singletonInstance可以确保只会创建一个实例对象,保证了Router实例的一致性和统一。

第二步

利用 hash 模式管理历史,通过监听 hash 变化更新当前路由状态。

export const createWebHashHistory = () => {
    function bindEvents(fn) {
        window.addEventListener('hashchange', fn)
    }
    // history 对象
    return {
        url: window.location.hash.slice(1) || '/',
        bindEvents
    }
}

向外导入一个createWebHashHistory函数返回一个url window.location.hash.slice(1)删除# 得到 #后面的内容,如果不存在路径就返回根路径,为hashchange添加监听器

第三步

通过依赖注入机制,在 Vue 应用中全局注册路由器,并提供访问路由器实例的方法。

const ROUTER_KEY = '__router__'  
export const useRouter = () => { return inject(ROUTER_KEY) }

创建const ROUTER_KEY = '__router__'这样的常量,主要是为了在代码中提供一个统一的、明确的标识符,用于存储和访问路由相关的数据或对象

第四步

实现核心的路由管理类,包含路由状态管理和组件注册功能。

class Router{

    constructor(options) {
        this.history = options.history
        this.routes = options.routes
        this.current = ref(this.history.url)
        this.history.bindEvents(() => {
            this.current.value = window.location.hash.slice(1)
        })
    }
    install(app){
        //全局声明有一个router 全局使用的对象
        app.provide(ROUTER_KEY,this)
        app.component('router-link',RouterLink)
        app.component('router-view',RouterView)
    }
}
  1. 构造函数constructor接收一个options对象作为参数,其中包括historyroutes属性。history用于管理浏览器的历史记录,routes用于定义路由规则。
  2. 在构造函数中,将historyroutes赋值给this对象的相应属性。同时,通过ref函数将this.history.url赋值给this.current属性,用于实时获取当前的URL。
  3. 调用this.history.bindEvents方法,绑定浏览器历史记录的事件监听器。当历史记录发生变化时,更新this.current.value为当前的哈希值(window.location.hash.slice(1))。
  4. install方法用于将Router对象安装到一个Vue应用中。首先,通过app.provide方法提供一个全局的router对象,供应用中的组件使用。然后,注册两个组件:router-linkrouter-view

两个组件:router-linkrouter-view

router-link点击时去跳转到相应的页面

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

<script setup>
import { defineProps } from 'vue';

const props = defineProps({
    to:{
        type:String,
        requried:true
    }
})
</script>

<style lang="css" scoped>

</style>

之前传的参数props除去了# 因为需要#去保证不会发送请求到服务器,所以添加一个#,<slot>这里用了一个插槽去占了一个位置,允许父组件中插入任意的内容,比如文本或HTML结构。

router-view是展示与当前路由匹配的视图组件

<template>
    <!-- 动态组件 -->
    <component :is="component"></component>
</template>

<script setup>
import { useRouter } from './index.js';
import { computed } from 'vue';

const router = useRouter();

// 不能式私有的响应式 useRouter 是大家共有的
const component = computed(() => {
   const route = router.routes.find(
    (route) => route.path == router.current.value
   )
   return route? route.component:null
})
</script>

<style lang="css" scoped>

</style>

动态组件当url发生改变时去加载对应组件,用计算属性去定义组件,不能用ref或者是reactive等响应式,ref或者是reactive都是私有属性不能让其他组件访问到,所以得用computed计算属性。(route) => route.path == router.current.value箭头函数去路由routes数组中匹配当前的路由路径值,如果匹配到了就去返回route 没有匹配到就返回null空。

尾记

手写底层源码,让我更加熟悉vue的用法,更加理解了如何去设计好一个组件,希望能从码农变为架构师

转载自:https://juejin.cn/post/7394786709646770176
评论
请登录