关于vue.js:vuerouter源码四createRouter源码解析

44次阅读

共计 4294 个字符,预计需要花费 11 分钟才能阅读完成。

前言

【vue-router 源码】系列文章将带你从 0 开始理解 vue-router 的具体实现。该系列文章源码参考 vue-router v4.0.15
源码地址:https://github.com/vuejs/router
浏览该文章的前提是你最好理解 vue-router 的根本应用,如果你没有应用过的话,可通过 vue-router 官网学习下。

该篇文章将带你剖析 createRouter 的实现。

应用

const routerHistory = createWebHistory()
export const router = createRouter({
  history: routerHistory,
  strict: true,
  routes: [{ path: '/home', redirect: '/'},
    {
      path: '/',
      components: {default: Home, other: component},
      props: {default: to => ({ waited: to.meta.waitedFor}) },
    },
    {
      path: '/nested',
      alias: '/anidado',
      component: Nested,
      name: 'Nested',
      children: [
        {
          path: 'nested',
          alias: 'a',
          name: 'NestedNested',
          component: Nested,
          children: [
            {
              name: 'NestedNestedNested',
              path: 'nested',
              component: Nested,
            },
          ],
        },
        {
          path: 'other',
          alias: 'otherAlias',
          component: Nested,
          name: 'NestedOther',
        },
        {
          path: 'also-as-absolute',
          alias: '/absolute',
          name: 'absolute-child',
          component: Nested,
        },
      ],
    },
  ],
  async scrollBehavior(to, from, savedPosition) {await scrollWaiter.wait()
    if (savedPosition) {return savedPosition} else {if (to.matched.every((record, i) => from.matched[i] !== record))
        return {left: 0, top: 0}
    }
    return false
  },
})

createRouter

在剖析 createRouter 之前,先来看下它的参数类型:

export interface _PathParserOptions {
  // 应用正则时辨别大小写,默认 false
  sensitive?: boolean
  // 是否禁止尾随斜杠,默认 false
  strict?: boolean
  // 正则表达式前应该加 ^,默认 true
  start?: boolean
  // 正则表达式以 $ 结尾,默认为 true
  end?: boolean
}

export type PathParserOptions = Pick<
  _PathParserOptions,
  'end' | 'sensitive' | 'strict'
>

export interface RouterOptions extends PathParserOptions {
  history: RouterHistory
  // 路由表
  routes: RouteRecordRaw[]
  // 在页面之间导航时管制滚动行为。能够返回一个 Promise 来提早滚动。scrollBehavior?: RouterScrollBehavior
  // 用于自定义如何解析 query
  parseQuery?: typeof originalParseQuery
  // 用于自定义查问对象如何转为字符串
  stringifyQuery?: typeof originalStringifyQuery
  // 激活 RouterLink 的默认类
  linkActiveClass?: string
  // 精准激活 RouterLink 的默认类
  linkExactActiveClass?: string
}

咱们来看下 createRouter 具体做了什么。createRouter办法共 885(蕴含空行)行,乍一看可能会感觉办法很简单,仔细观察,其实很大一部分代码都是申明一些函数。咱们能够先临时抛开这些函数申明看其余部分。

首先会应用 createRouterMatcher 办法创立了一个路由匹配器 matcher,从options 中提取 parseQuerystringifyQueryhistory 属性,如果 options 中没有history,抛出谬误。

const matcher = createRouterMatcher(options.routes, options)
const parseQuery = options.parseQuery || originalParseQuery
const stringifyQuery = options.stringifyQuery || originalStringifyQuery
const routerHistory = options.history
if (__DEV__ && !routerHistory)
    throw new Error('Provide the"history"option when calling"createRouter()":' +
        'https://next.router.vuejs.org/api/#history.'
    )

紧接着申明了一些全局守卫相干的变量,和一些对于 params 的解决办法,其中无关全局守卫的变量都是通过 useCallbacks 创立的,params相干办法通过 applyToParams 创立。

// 全局前置守卫相干办法
const beforeGuards = useCallbacks<NavigationGuardWithThis<undefined>>()
// 全局解析守卫相干办法
const beforeResolveGuards = useCallbacks<NavigationGuardWithThis<undefined>>()
// 全局后置钩子办法
const afterGuards = useCallbacks<NavigationHookAfter>()

// 以后路由,浅层响应式对象
const currentRoute = shallowRef<RouteLocationNormalizedLoaded>(START_LOCATION_NORMALIZED)
let pendingLocation: RouteLocation = START_LOCATION_NORMALIZED

// 如果浏览器环境下设置了 scrollBehavior,那么须要避免页面主动复原页面地位
// https://developer.mozilla.org/zh-CN/docs/Web/API/History/scrollRestoration
if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {history.scrollRestoration = 'manual'}

// 标准化 params,转字符串
const normalizeParams = applyToParams.bind(
  null,
  paramValue => '' + paramValue
)
// 编码 param
const encodeParams = applyToParams.bind(null, encodeParam)
// 解码 params
const decodeParams: (params: RouteParams | undefined) => RouteParams =
  applyToParams.bind(null, decode)

对于 useCallbacks 的实现:在 useCallbacks 中申明一个 handlers 数组用来保留所有增加的办法,useCallbacks的返回值中包含三个办法:add(增加一个 handler,并返回一个删除handler 的函数)、list(返回所有handler)、reset(清空所有handler

export function useCallbacks<T>() {let handlers: T[] = []

  function add(handler: T): () => void {handlers.push(handler)
    return () => {const i = handlers.indexOf(handler)
      if (i > -1) handlers.splice(i, 1)
    }
  }

  function reset() {handlers = []
  }

  return {
    add,
    list: () => handlers,
    reset,
  }
}

applyToParams的实现:接管一个处理函数和 params 对象,遍历 params 对象,并对每一个属性值执行 fn 并将后果赋给一个新的对象。

export function applyToParams(fn: (v: string | number | null | undefined) => string,
  params: RouteParamsRaw | undefined
): RouteParams {const newParams: RouteParams = {}

  for (const key in params) {const value = params[key]
    newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value)
  }

  return newParams
}

而后申明了大量的函数,包含 addRouteremoveRoutegetRoutes 等,这些函数也就是咱们日常应用的 addRouteremoveRoute 等。

createRouter 的最初创立了一个 router 对象,并将其返回,该对象简直蕴含了申明的所有函数。

总结

createRouter函数中申明了一些全局钩子所需的变量和很多函数,这些函数就是咱们日常应用的一些办法,如 addRouteremoveRoute 等,在函数的最初,申明了一个 router 对象,后面所申明的函数少数都会被蕴含在这个对象里,最终会将 router 返回。在 router 中有个重要的 install 办法,对于 install 的过程能够看之前的文章,这里就不再次介绍了。
对于 router 中的各个函数,会在后续文章中持续介绍。

正文完
 0