用過vue的小夥伴們都知道,路由使用起來非常簡單,只用引入,定義,配置,輸出還能需要的地方引入使用,實現單頁應用的頁面跳轉,今天我們就來說一說,我不import,也能實現
<router-link>、<router-view>
的路由效果。來到了我們最熟悉的手寫環節。
一、知識儲備
(1)、全域性元件
在vue中,路由是在很多地方都要使用的元件,所以在手寫的實現過程,我們將它定義成全域性元件,一旦被註冊,在任何需要使用的模板元件中,都不需要引入,直接可以使用。
使用app.component()方法註冊一個全域性元件,這裏的app實際上是Vue例項。接受兩個引數:name:元件名、options:元件定義物件,既可以是Vue元件,也可以是自定義元件。
(2)、provide和inject
用於祖先-後代關係通訊的特性,這就意味著允許所有的後代可以訪問到祖先元件注入的資料或者服務,不需要透過元件逐層的傳遞props。在跨多個元件層級傳遞資料或服務時多使用。
(3)、<slot>
元素,也叫插槽
它允許父元件向子元件的特定位置插入內容,這個機制叫做內容分發。它讓元件的設計更為靈活,允許元件的使用者決定某些部分的具體內容,不再僅僅是元件的設計者。
使用示例:
父元件
<template> <div> <child-component> <p>子元件中顯示的內容</p> </child-component> </div> </template>
子元件
<template> <div> <h1>我是子元件</h1> <slot></slot> <!-- 顯示的位置 --> </div> </template>
(4)<component>
元素,動態元件標籤
根據一個表示式來動態的渲染不同的元件,需要一個is特性,繫結到一個表示元件名稱的字串,或者是一個元件選項物件。這個值通常是響應式的資料屬性,決定了渲染哪個元件。
二、實現過程
1、router-link的實現
在使用vue的原生route-link時,會有一個to引數,值為跳轉的目標路由。這裏我們定義一個router-link元件,接收父元件,也就是使用router-link的元件傳來的引數,並使用slot顯示跳轉資訊。
<template> <div> <a :href="'#' + props.to"> <slot></slot> </a> </div> </template> <script setup> const props = defineProps({ to:{ type:String, required:true } }) </script> <style scoped> </style>
以雜湊路由為例,'#'加上父元件傳來的路徑,實現路由的切換。
這樣在使用了router-link的父元件中,你可以像Vue中的router一樣的方法使用它。
<router-link to="/">首頁</router-link> <router-link to="/about">About</router-link>
2、定義Router並單例輸出
import RouterLink from './RouterLink.vue' import RouterView from './RouterView.vue' import {ref,inject} from 'vue' // 單例的責任 export const createRouter = (options) => { return new Router(options) } export const createWebHashHistory = () =>{ //history 物件 function bindEvents (fn){ window.addEventListener('hashchange',fn) } return { url:window.location.hash.slice(1) || '/', bindEvents } } //標記一下 router const ROUTER_KEY = '__router__' 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) }) } // use方法會呼叫 外掛的install方法 install(app){ //全域性宣告有一個router 全域性使用的物件 app.provide(ROUTER_KEY,this) app.component('router-link',RouterLink) app.component('router-view',RouterView) } }
輸出單例路由示例,接收配置物件作為引數,例如Vue中router的routes和history
定義監聽hashchange事件的函式,將當前路徑和這個函式返回,接著Router類中跟蹤和響應URL hash的變化。
將ROUTER_KEY常量作為Vue中provide和inject API中的識別符號,用於在元件樹中注入和獲取路由例項,用於祖先-後代關係通訊的特性。
使用inject方法,從元件樹中獲取路由例項,讓任何元件都可以透過呼叫這個方法來訪問路由物件,從而實現對路由狀態的查詢和修改。
Router類,建構函式接收option物件(配置資訊routes和history),將當前路徑設定為響應式,儲存當前的路由狀態,初始值為createWebHashHistory中返回的url。並且在路徑發生改變時,自動響應更新對應的值,這是響應式在路由中的高階用法。
install(app),將當前Router注入到Vue應用(app)中,讓整個元件樹中的後代元件都能透過inject方法來訪問這個路由例項。對RouterLink、RouterView元件的全域性註冊。
3、router-view的實現
<template> <component :is="component"> </component> </template> <script setup> import { useRouter } from './index' import { computed } from 'vue'; const router = useRouter() console.log(router); //router-view 動態元件展示,依賴於 url 的變化 //響應式 router.current 設定為ref const component = computed(() => { const route = router.routes.find((route)=>route.path == router.current.value) return route ? route.component:null }) </script> <style scoped> </style>
使用計算屬性computed,在當前路由變化時,計算component的值,並顯示配置資訊中的對應元件,
route ? route.component:null
並且做了防止使用者輸入錯誤路由。
使用Router
import {createRouter,createWebHashHistory} from './grouter/index' import Home from '../pages/Home.vue' import About from '../pages/About.vue' const routes = [ { path:'/', name:'Home', component:Home }, { path:'/about', name:'About', component: About }, ] const router = createRouter({ history:createWebHashHistory(), routes }) export default router