<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ![](https://img.kancloud.cn/c8/31/c83114b2e9f22e06483ff8cfbd89065b_1352x254.png)關于前端路由,網上有很多可以參考,但是感覺都是一些mini實現,對hashchange、pushState、replaceState這幾個api的使用。所以準備梳理下官方github倉庫的的源碼實現,同時給自己也作為閱讀筆記作為學習記錄,在代碼截圖中,為便于理解,會刪除大量干擾閱讀的支線/邊界情況代碼。 看完本篇文章你將理解: 1. Vue Router 的基本實現原理 2. 路徑是如何管理的,路徑和路由組件的渲染是如何映射的 3. 導航守衛是如何執行的 4. 給路由組件傳遞數據,有幾種方式,分別都怎么做的 先把[github源碼](https://github.com/vuejs/vue-router-next) clone 下來。 首先從入口index.js文件開始瀏覽: ``` export { createWebHistory } from './history/html5' export { createMemoryHistory } from './history/memory' export { createWebHashHistory } from './history/hash' export { createRouterMatcher, RouterMatcher } from './matcher' export { LocationQuery, parseQuery, stringifyQuery, LocationQueryRaw, LocationQueryValue, LocationQueryValueRaw, } from './query' export { RouterHistory, HistoryState } from './history/common' export { RouteRecord, RouteRecordNormalized } from './matcher/types' export { PathParserOptions, _PathParserOptions, } from './matcher/pathParserRanker' export { routeLocationKey, routerViewLocationKey, routerKey, matchedRouteKey, viewDepthKey, } from './injectionSymbols' export { // route location _RouteLocationBase, LocationAsPath, LocationAsRelativeRaw, RouteQueryAndHash, RouteLocationRaw, RouteLocation, RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteParams, RouteParamsRaw, RouteParamValue, RouteParamValueRaw, RouteLocationMatched, RouteLocationOptions, RouteRecordRedirectOption, // route records _RouteRecordBase, RouteMeta, START_LOCATION_NORMALIZED as START_LOCATION, RouteComponent, // RawRouteComponent, RouteRecordName, RouteRecordRaw, NavigationGuard, NavigationGuardNext, NavigationGuardWithThis, NavigationHookAfter, } from './types' export { createRouter, Router, RouterOptions, RouterScrollBehavior, } from './router' export { NavigationFailureType, NavigationFailure, isNavigationFailure, } from './errors' export { onBeforeRouteLeave, onBeforeRouteUpdate } from './navigationGuards' export { RouterLink, useLink, RouterLinkProps, UseLinkOptions, } from './RouterLink' export { RouterView, RouterViewProps } from './RouterView' export * from './useApi' export * from './globalExtensions' ``` 我們主要關注: * `history`模塊 * `matcher`模塊 * `router`模塊 * `RouterLink、RouterView`模塊 * `navigationGuards`模塊 ## createRouter 我們一般使用時這樣使用的: ![](https://img.kancloud.cn/e8/23/e823510dd507d7036072428038313f2d_940x398.png) ![](https://img.kancloud.cn/04/ee/04ee4275cd1675d26c3a87f398c85a3b_992x210.png) 在入口文件中找到`createRouter`方法,它是通過`router`模塊導出的,`router`模塊源碼路徑為`src/router.ts`,在該文件中找到`createRouter`方法源碼,傳入`RouterOptions`類型的對象,然后返回一個`Router`實例,同時了提供了很多方法。 ``` export function createRouter(options: RouterOptions): Router { const router: Router = { currentRoute, addRoute, removeRoute, hasRoute, getRoutes, resolve, options, push, replace, go, back: () => go(-1), forward: () => go(1), beforeEach: beforeGuards.add, beforeResolve: beforeResolveGuards.add, afterEach: afterGuards.add, onError: errorHandlers.add, isReady, install(app: App) { // ... }, } return router } ``` ## install方法 當我們在vue main.js中 app.use(router)其實就是調用了 router中install方法,接受一個vue app實例作為參數。 ``` const router = { install(app) { const router = this // 注冊路由組件 app.component('RouterLink', RouterLink) app.component('RouterView', RouterView) // 全局配置定義 $router 和 $route app.config.globalProperties.$router = router Object.defineProperty(app.config.globalProperties, '$route', { get: () = >unref(currentRoute), }) // 在瀏覽器端初始化導航 if (isBrowser && !started && currentRoute.value === START_LOCATION_NORMALIZED) { // see above started = true push(routerHistory.location). catch(err = >{ warn('Unexpected error when starting the router:', err) }) } // 路徑變成響應式 const reactiveRoute = {} for (let key in START_LOCATION_NORMALIZED) { reactiveRoute[key] = computed(() = >currentRoute.value[key]) } // 全局注入 router 和 reactiveRoute app.provide(routerKey, router) app.provide(routeLocationKey, reactive(reactiveRoute)) let unmountApp = app.unmount installedApps.add(app) // 應用卸載的時候,需要做一些路由清理工作 app.unmount = function() { installedApps.delete(app) if (installedApps.size < 1) { removeHistoryListener() currentRoute.value = START_LOCATION_NORMALIZED started = false ready = false } unmountApp.call(this, arguments) } } } ``` 簡單說install做了幾件事: 1. 引入vue, 注冊RouterLink、RouterView為全局組件 2. 在瀏覽器端初始化導航 3. 路徑變成響應式 4. 通過provide全局注入 router 和 reactiveRoute 5. 攔截vue實例的unmount方法,在unmount方法調用之前,先執行VueRouter相關的卸載工作 ## createRouter的參數(RouterOptions) ![](https://img.kancloud.cn/59/0a/590a8cb4df9185a886052da077953ba9_1380x674.png) 這里的類型文件寫的非常的清楚并且貼心的帶了example(源碼中直接復制過來的,有點又臭又長的的意思,但是實際很簡單): ``` export interface RouterOptions extends PathParserOptions { /** * History implementation used by the router. Most web applications should use * `createWebHistory` but it requires the server to be properly configured. * You can also use a _hash_ based history with `createWebHashHistory` that * does not require any configuration on the server but isn't handled at all * by search engines and does poorly on SEO. * * @example * ```js * createRouter({ * history: createWebHistory(), * // other options... * }) * ``` */ history: RouterHistory /** * Initial list of routes that should be added to the router. */ routes: RouteRecordRaw[] /** * Function to control scrolling when navigating between pages. Can return a * Promise to delay scrolling. Check {@link ScrollBehavior}. * * @example * ```js * function scrollBehavior(to, from, savedPosition) { * // `to` and `from` are both route locations * // `savedPosition` can be null if there isn't one * } * ``` */ scrollBehavior ? :RouterScrollBehavior /** * Custom implementation to parse a query. See its counterpart, * {@link RouterOptions.stringifyQuery}. * * @example * Let's say you want to use the package {@link https://github.com/ljharb/qs | qs} * to parse queries, you can provide both `parseQuery` and `stringifyQuery`: * ```js * import qs from 'qs' * * createRouter({ * // other options... * parse: qs.parse, * stringifyQuery: qs.stringify, * }) * ``` */ parseQuery ? :typeof originalParseQuery /** * Custom implementation to stringify a query object. Should not prepend a leading `?`. * {@link RouterOptions.parseQuery | parseQuery} counterpart to handle query parsing. */ stringifyQuery ? :typeof originalStringifyQuery /** * Default class applied to active {@link RouterLink}. If none is provided, * `router-link-active` will be applied. */ linkActiveClass ? :string /** * Default class applied to exact active {@link RouterLink}. If none is provided, * `router-link-exact-active` will be applied. */ linkExactActiveClass ? :string /** * Default class applied to non active {@link RouterLink}. If none is provided, * `router-link-inactive` will be applied. */ // linkInactiveClass?: string } ``` 上面的參數: * history:可以用createWebHistory創建也可以用createWebhashHistory * `routes`:應該添加到路由的初始路由列表 其他的可選參數: * `scrollBehavior`:在頁面之間導航時控制滾動的函數。可以返回一個 `Promise` 來延遲滾動 * `parseQuery`:用于解析查詢的自定義實現。必須解碼查詢鍵和值。參見對應的 `stringifyQuery` * `stringifyQuery`:對查詢對象進行字符串化的自定義實現。不應該在前面加上 ?。應該正確編碼查詢鍵和- 值。 `parseQuery` 對應于處理查詢解析。 * `linkActiveClass`:用于激活的 `RouterLink` 的默認類。如果什么都沒提供,則會使用 `router-link-active` * `linkExactActiveClass`:用于精準激活的 `RouterLink` 的默認類。如果什么都沒提供,則會使用 `router-link-exact-active` ### 其中options參數中的history的type是RouterHistory 定義如下 ``` interface RouterHistory { // 只讀屬性,基本路徑,會添加到每個url的前面 readonly base: string // 只讀屬性,當前路由 readonly location: HistoryLocation // 只讀屬性,當前狀態 readonly state: HistoryState // 路由跳轉方法 push(to: HistoryLocation, data?: HistoryState): void // 路由跳轉方法 replace(to: HistoryLocation, data?: HistoryState): void // 路由跳轉方法 go(delta: number, triggerListeners?: boolean): void // 添加一個路由事件監聽器 listen(callback: NavigationCallback): () => void // 生成在錨點標簽中使用的href的方法 createHref(location: HistoryLocation): string // 清除listeners destroy(): void } ``` `options.history`參數為createWebHistory、createWebHashHistory、createMemoryHistory三種的其中一種。 `hash`和`history`路由模式,除了`base`的處理邏輯不同,其他屬性或者方法使用的是共同的邏輯。 **1、createWebHashHistory** ![](https://img.kancloud.cn/07/66/07661391837582f8fb87fd7b17a9bec6_1382x644.png) createWebHashHistory這個模式很簡單,這是檢測下有沒有#號 沒有就加上,然后調用createWebHistory(base),base為基本路徑,其中重點分析還是createWebHistory **2、createWebHistory** ![](https://img.kancloud.cn/39/35/39352f26441a1e2fe71bffdcf48d8900_1298x332.png) ![](https://img.kancloud.cn/45/f3/45f3d34ffecf5054321e5aa3b735e07d_1280x1050.png)![](https://img.kancloud.cn/92/28/92289221ada413b1f1f9fab486b2c4dc_1128x564.png) ![](https://img.kancloud.cn/ca/c7/cac7bc2b4c4e8834922c59cb48db791a_1294x1028.png) `changeLocation`方法。`changeLocation`方法十分重要,它是`push`方法和`replace`等路由跳轉方法的實現基礎。該方法包含三個參數:目標`location`、目標`state`對象、是否為替換當前`location`,這里發生錯誤都時候用location做了一個降級。 在createWebHistory的時候,執行了這樣一句: ``` const historyListeners = useHistoryListeners( base, historyNavigation.state, historyNavigation.location, historyNavigation.replace) ``` 其中useHistoryListeners的代碼實現: 這里的listen方法由useHistoryListeners函數返回,在useHistoryListeners函數內部查看listen方法相關源碼 ~~~ function useHistoryListeners() { let listeners: NavigationCallback[] = [] let teardowns: Array<() => void> = [] function listen(callback: NavigationCallback) { listeners.push(callback) const teardown = () => { const index = listeners.indexOf(callback) if (index > -1) listeners.splice(index, 1) } teardowns.push(teardown) return teardown } return { listen, } } ~~~ listen方法的作用是將傳入的回調函數添加到listeners數組中,并返回移除函數,同時將移除函數添加到teardowns數組中,方便進行批量移除。 ![](https://img.kancloud.cn/33/18/3318a5ff42eac1f5e3d77822f23bde64_1148x148.png) 這里在useHistoryListeners中執行了這句,就是添加監聽,同時執行posStateHandler,在posStateHandler中: ![](https://img.kancloud.cn/01/8f/018fccfb7b278cdffec7914a61fb0648_958x990.png) 執行數組中的回調。 當用戶操作瀏覽器導航按鈕或者應用中調用了push/replace/go等方法時,都會觸發該事件,調用此函數。除了更新某些對象值之外,這個函數的關鍵在于遍歷listeners數組調用每一個注冊的回調函數。 ![](https://img.kancloud.cn/f8/00/f8005cd33b23aaa24350c477b05a9abd_1226x352.png) beforeunload事件當瀏覽器窗口關閉或者刷新時被觸發,此時會調用beforeUnloadListener函數,該函數源碼如下,主要作用是將當前滾動信息保存到當前歷史記錄實體中。 history和hash模式的差別在于base的處理,換句話可以說是瀏覽器url表現方式的差異,路由的跳轉、事件的監聽,都是基于history api。 # routes 回到createRouter方法中,可以看到該方法中只有一個地方用到了options.routes,它作為createRouterMatcher參數,執行后返回一個RouterMatcher類型的對象 ![](https://img.kancloud.cn/c8/31/c83114b2e9f22e06483ff8cfbd89065b_1352x254.png) createRouterMatcher函數的基本邏輯簡化后的代碼如下 ~~~ function createRouterMatcher( routes: RouteRecordRaw[], globalOptions: PathParserOptions ): RouterMatcher { const matchers: RouteRecordMatcher[] = [] const matcherMap = new Map<RouteRecordName, RouteRecordMatcher>() globalOptions = mergeOptions( { strict: false, end: true, sensitive: false } as PathParserOptions, globalOptions ) function getRecordMatcher(name: RouteRecordName) { // ... } function addRoute( record: RouteRecordRaw, parent?: RouteRecordMatcher, originalRecord?: RouteRecordMatcher ) { // ... } function removeRoute(matcherRef: RouteRecordName | RouteRecordMatcher) { // ... } function getRoutes() { // ... } function insertMatcher(matcher: RouteRecordMatcher) { // ... } function resolve( location: Readonly<MatcherLocationRaw>, currentLocation: Readonly<MatcherLocation> ): MatcherLocation { // ... } // add initial routes routes.forEach(route => addRoute(route)) return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher } } ~~~ 該函數接收兩個參數,第一個參數是路由配置數組,第二個參數是VueRouter初始化時傳進來的options。然后聲明兩個變量matchers和matcherMap,然后是聲明一系列方法,在return之前,遍歷routes,通過addRoute方法,將路由配置轉化為matcher。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看