手撕router-view/router-link,vue-router底层到底是怎么实现的?(一)

前言

使用vue时总会使用到路由,路由的跳转..等等,对于对代码充满好奇的我们,自然不会放过每一个手撕源码的机会,因此今天我们一起来看看router-link、router-view的底层到底是怎么实现的

vue-router中的router-link的使用

首先我们来玩一下vue封装好的vue-router

  1. npm i vue-router 安装vue-router
  2. 在router文件夹下创建index.js

index.js

js 复制代码
import { createRouter, createWebHashHistory } from 'vue-router';

const routes = [
    {
        path: '/',
        name: 'home',
        component: () => import('../views/Home.vue')
    },
    {
        path: '/about',
        name: 'about',
        component: () => import('../views/About.vue')
    }
]

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

export default router;

这里我们就创建了一个router,抛出之后,在全局引入(main.js),并使用use。

注意: 这里我们使用的是带Hash的createWebHashHistory,它与不带Hash的路由创建模式存在一些区别:

URL 形式 :使用 createWebHashHistory 创建的路由模式,其 URL 中会带有 # 号,例如 /#/home/#/about 等;而 createWebHistory 创建的路由模式,其 URL 更加直观,不会在 URL 中添加 # 号,例如 /home/about 等。

服务器配置要求createWebHashHistory 不需要在服务器端进行特殊配置,因为 # 后面的部分不会被发送到服务器。而使用 createWebHistory 时,由于直接使用了 HTML5 中的 history.pushStatehistory.replaceState 方法来实现路由跳转,如果直接在浏览器中刷新或直接访问某个路由,服务器将无法识别该路由,并返回 404 错误。因此,需要在服务器端进行配置,将所有的路由请求都返回首页,再由前端代码进行路由的匹配和处理。

浏览器兼容性createWebHashHistory 支持所有浏览器,包括老版本的浏览器。而 createWebHistory 只支持 HTML5 标准浏览器。

SEO 优化createWebHashHistory 因为 URL 中带有 # 号,在搜索引擎的 SEO 优化方面存在一些问题。而 createWebHistory 的 URL 形式更有利于 SEO。

开发环境设置 :在开发环境下,使用 createWebHistory 需要将 webpack 的 historyApiFallback 属性设置为 true,以便在开发环境下正常使用路由。

main.js

js 复制代码
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'

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

引入之后我们就可以在App.js中加入路由入口 <router-view />,启动项目,这样一个基本的路由就建立起来了。

App.vue

js 复制代码
<template>
  <router-link to="/">首页</router-link>
  <router-link to="/about">about</router-link>
  <router-view />
</template>

<script setup>

</script>

<style lang="scss" scoped></style>
  • router-link点击可以跳转。
  • 此时启动项目,就能直接访问到/路径下的Home.vue了,更改路径为/about就能访问到About.vue。

走到这一步,我们就自然而然会思考:这个routerlink是如何进入到组件树,它的底层又是如何担起跳转页面的功能的?

组件都是开发出来的,这个routerlink也一样,它来自于我们安装的vue-router,底层源码到底做了什么事情?

手撕router-link

  1. routerlink能实现页面跳转的功能,那么必然他的底层一定存在a标签
  2. 标签中的to,具体到哪个路径需要做绑定,用到defineProps,并且这是一个必填required属性,就像href一样。
  3. 我们要使用自己写的routerlink,那必然在main.js中不要再use已经写好的router
  4. 我们学习html时标签中间的文字部分(显示不同的超链接,如:about超链接,home超链接),每一个超链接标签中间的文字都是不一样的,此时我们把自己写好的RouterLink组件引入到App.js中使用时,如何把组件中间写的文字搬运到a标签中?----><slot></slot>插槽占位符提升组件的定制性,可读性更好

RouterLink.vue

js 复制代码
<template>
    <a :href="'#' + props.to">
        <slot></slot>
    </a>
</template>

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

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

<style lang="scss" scoped></style>

App.vue

js 复制代码
<template>
  <RouterLink to="/">首页</RouterLink>
  <RouterLink to="/about">about</RouterLink>
</template>

<script setup>
import RouterLink from './router/grouter/RouterLink.vue'
</script>

<style lang="scss" scoped></style>

但是问题来了:我们使用vue-router中的routerlink组件的时候,想让其生效,其实就是在main.js中用app给use了一下我们在路由中创建的router,它压根就不需要像上方我们的做法一样(引入这个组件import RouterLink ......),那么这个use方法到底做了什么事情呢?

  • vue-router中的routerlink组件不需要声明就能使用,这是:全局组件,想让一个组件成为全局组件:app.component('组件名',类在哪里),如routerlink组件声明:app.component('router-link',RouterLink)
  • main.js中我们原来是在router文件夹中的index.js中引入vue-router 然后去createrouter..createWebHashHistory,然后定义一个routes,通过createrouter方法创造一个router最终抛出这个router,现在我们手撕一下这个index.js

index.js

js 复制代码
import { createRouter } from './grouter/index'

const router = createRouter()


export default router

我们还封装了一层index.js在grouter文件夹下:

index.js

js 复制代码
// VueRouter封装在这里

import RouterLink from "./RouterLink.vue"
const createRouter = () => {
    return new Router()
}

class Router {
    constructor() {

    }
    install(app) {
        // console.log(app);
        // console.log('vue-router install');
        // 完成全局组件的声明
        app.component('RouterLink', RouterLink);

    }

}

export {
    createRouter
}

那么至此,在App.vue中我们就再也不需要引入RouterLink组件才能去使用RouterLink了

App.vue

js 复制代码
<template>
  <RouterLink to="/">首页</RouterLink>
  <RouterLink to="/about">about</RouterLink>
</template>

<script setup>
// import RouterLink from './router/grouter/RouterLink.vue'

</script>

<style lang="scss" scoped></style>

效果:

到这里,可以看到,vue分为vue的本体以及vue的生态,不得不说尤雨溪是真的厉害。在做vue框架时做好边界(mvvm,组件,VDOM...)的同时还有自己的生态(install)app只需要use一下拿到这个实例,把app传过去就能调用install方法,

手撕router-view

既然已经写到这里了,不如把router-view也给写了

  • 还是一样,在刚才封装的index.js中再将RouterView也给声明成全局组件,虽然这个组件还没写
  • 动态组件 <component :is="component"> </component>这个component是响应式的我们需要拿到url才知道需要去哪里,这里就需要对index.js中写的路由routes数组里的path与当前url做对比,比对成功就跳转

RouterView.vue

js 复制代码
<template>
    <component :is="component"> </component>
</template>

<script setup>
import { computed, ref } from 'vue'

let router = useRouter()

const component = computed(() => {
    const route = router.routes.find(
        (route) => route.path === router.url
    )
    return route ? route.component : null
})
</script>

<style lang="scss" scoped></style>

grouter文件夹下的index.js

js 复制代码
// VueRouter封装在这里
import { ref } from "vue";
import RouterLink from "./RouterLink.vue"
import RouterView from "./RouterView.vue"
const createRouter = (options) => {
    return new Router(options)
}

const createWebHashHistory = () => {
    return {
        url: window.location.hash.slice(1) || '/'
    }
}

class Router {
    constructor(options) {
        console.log(options, '/////////');
        this.history = options.history
        this.routes = options.routes;
        // 当前的url 状态 它是router-view computed计算属性的依赖
        this.current = ref(this.history.url)
    }
    install(app) {
        // console.log(app);
        // console.log('vue-router install');
        // 完成全局组件的声明
        app.component('RouterLink', RouterLink);
        app.component('RouterView', RouterView);

    }

}

export {
    createRouter,
    createWebHashHistory
}
  • url: window.location.hash.slice(1) || '/' 由于是带Hash的,需要将井号去除
  • 接下来一起来看看option打印出来的效果吧,createRouter接收两个值,一个history,一个routes,现在我们已经把history实现了,看看他是否真的接收到了我们的url。

至此,routerlink介绍完毕,routerview未完待续...

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax