欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue路由History模式分析

 更新時(shí)間:2023年06月02日 11:28:08   作者:WindrunnerMax  
Vue-router是Vue的核心組件,主要是作為Vue的路由管理器,Vue-router默認(rèn)hash模式,通過引入Vue-router對(duì)象模塊時(shí)配置mode屬性可以啟用history模式,本文將通過代碼示例給大家詳細(xì)分析Vue路由History模式

Vue路由History模式分析

描述

Vue-routerhash模式使用URLHash來模擬一個(gè)完整的URL,當(dāng)URL改變時(shí)頁面不會(huì)重新加載,而Vue-routerhistory模式是充分利用history.pushStateAPI來完成URL跳轉(zhuǎn),同樣在頁面跳轉(zhuǎn)時(shí)無須重新加載頁面,當(dāng)然也不會(huì)對(duì)于服務(wù)端進(jìn)行請(qǐng)求,當(dāng)然對(duì)于history模式仍然是需要后端的配置支持,由于應(yīng)用是個(gè)單頁客戶端應(yīng)用,如果后臺(tái)沒有正確的配置,當(dāng)用戶在瀏覽器直接訪問URL時(shí)就會(huì)返回404,所以需要在服務(wù)端增加一個(gè)覆蓋所有情況的候選資源,如果URL匹配不到任何靜態(tài)資源時(shí),則應(yīng)該返回同一個(gè)index.html應(yīng)用依賴頁面,例如在Nginx下的配置。

location / {
  try_files $uri $uri/ /index.html;
}

分析

Vue-router源碼的實(shí)現(xiàn)比較復(fù)雜,會(huì)處理各種兼容問題與異常以及各種條件分支,文章分析比較核心的代碼部分,精簡(jiǎn)過后的版本,重要部分做出注釋,commit id560d11d。

首先是在定義Router時(shí)調(diào)用Vue.use(VueRouter),這是Vue.js插件的經(jīng)典寫法,給插件對(duì)象增加install方法用來安裝插件具體邏輯,此時(shí)會(huì)調(diào)用VueRouter類上的靜態(tài)方法,即VueRouter.install = installinstall模塊主要是保證Vue-router只被use一次,以及通過mixinVue的生命周期beforeCreate內(nèi)注冊(cè)實(shí)例,在destroyed內(nèi)銷毀實(shí)例,還有定義$router$route屬性為只讀屬性以及<router-view><router-link>全局組件的注冊(cè)。

// dev/src/install.js line 6
export function install (Vue) {
  if (install.installed && _Vue === Vue) return
  install.installed = true // 保證 Vue-router 只被 use 一次
  _Vue = Vue
  const isDef = v => v !== undefined
  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
  }
  Vue.mixin({
    beforeCreate () { // 注冊(cè)實(shí)例
      if (isDef(this.$options.router)) { // this.$options.router 來自于 VueRouter 的實(shí)例化 // 判斷實(shí)例是否已經(jīng)掛載
        this._routerRoot = this
        this._router = this.$options.router
        this._router.init(this) // // 調(diào)用 VueRouter 的 init 方法 // 后文會(huì)說明 init 方法的作用
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this // 將組件的 _routerRoot 都指向根 Vue 實(shí)例
      }
      registerInstance(this, this)
    },
    destroyed () { // 銷毀實(shí)例 即掛載undefined
      registerInstance(this)
    }
  })
  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })
  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })
  Vue.component('RouterView', View) // 注冊(cè)全局組件 <router-view>
  Vue.component('RouterLink', Link) // 注冊(cè)全局組件 <router-link>
  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}

之后是VueRouter對(duì)象的構(gòu)造函數(shù),主要是先獲取mode的值,如果mode的值為history但是瀏覽器不支持history模式,那么就強(qiáng)制設(shè)置mode值為hash,接下來根據(jù)mode的值,來選擇vue-router使用哪種模式。

// dev/src/index.js line 40
constructor (options: RouterOptions = {}) {
  this.app = null
  this.apps = []
  this.options = options
  this.beforeHooks = []
  this.resolveHooks = []
  this.afterHooks = []
  this.matcher = createMatcher(options.routes || [], this) // 創(chuàng)建路由匹配對(duì)象
  let mode = options.mode || 'hash'
  this.fallback =
    mode === 'history' && !supportsPushState && options.fallback !== false // 檢車兼容
  if (this.fallback) {
    mode = 'hash'
  }
  if (!inBrowser) {
    mode = 'abstract'
  }
  this.mode = mode
  switch (mode) {
    case 'history':
      this.history = new HTML5History(this, options.base) // 實(shí)例化history模式
      break
    case 'hash':
      this.history = new HashHistory(this, options.base, this.fallback) // 實(shí)例化Hash模式
      break
    case 'abstract':
      this.history = new AbstractHistory(this, options.base)
      break
    default:
      if (process.env.NODE_ENV !== 'production') {
        assert(false, `invalid mode: ${mode}`)
      }
  }
}

在構(gòu)造函數(shù)中調(diào)用了創(chuàng)建路由匹配對(duì)象的方法createMatcher,而在createMatcher中又調(diào)用了實(shí)際用以創(chuàng)建路由映射表的方法createRouteMap,可以說createMatcher函數(shù)的作用就是創(chuàng)建路由映射表,然后通過閉包的方式讓addRoutesmatch函數(shù)能夠使用路由映射表的幾個(gè)對(duì)象,最后返回一個(gè)Matcher對(duì)象。

// dev/src/create-matcher.js line 16
export function createMatcher (
  routes: Array<RouteConfig>,
  router: VueRouter
): Matcher {
  const { pathList, pathMap, nameMap } = createRouteMap(routes) // 創(chuàng)建路由映射表
  function addRoutes (routes) {
    createRouteMap(routes, pathList, pathMap, nameMap)
  }
  function match ( // 路由匹配
    raw: RawLocation,
    currentRoute?: Route,
    redirectedFrom?: Location
  ): Route {
    const location = normalizeLocation(raw, currentRoute, false, router) // location 是一個(gè)對(duì)象,類似于 {"_normalized":true,"path":"/","query":{},"hash":""}
    const { name } = location
    if (name) { // 如果有路由名稱 就進(jìn)行nameMap映射
      const record = nameMap[name]  // nameMap[name] = 路由記錄
      if (process.env.NODE_ENV !== 'production') {
        warn(record, `Route with name '${name}' does not exist`)
      }
      if (!record) return _createRoute(null, location)
      const paramNames = record.regex.keys
        .filter(key => !key.optional)
        .map(key => key.name)
      if (typeof location.params !== 'object') {
        location.params = {}
      }
      if (currentRoute && typeof currentRoute.params === 'object') {
        for (const key in currentRoute.params) {
          if (!(key in location.params) && paramNames.indexOf(key) > -1) {
            location.params[key] = currentRoute.params[key]
          }
        }
      }
      location.path = fillParams(record.path, location.params, `named route "${name}"`)
      return _createRoute(record, location, redirectedFrom)
    } else if (location.path) { // 如果路由配置了path,到pathList和PathMap里匹配到路由記錄 
      location.params = {}
      for (let i = 0; i < pathList.length; i++) {
        const path = pathList[i]
        const record = pathMap[path]
        if (matchRoute(record.regex, location.path, location.params)) {
          return _createRoute(record, location, redirectedFrom)
        }
      }
    }
    // no match
    return _createRoute(null, location)
  }
  function redirect ( // 處理重定向
    record: RouteRecord,
    location: Location
  ): Route {
    const originalRedirect = record.redirect
    let redirect = typeof originalRedirect === 'function'
      ? originalRedirect(createRoute(record, location, null, router))
      : originalRedirect
    if (typeof redirect === 'string') {
      redirect = { path: redirect }
    }
    if (!redirect || typeof redirect !== 'object') {
      if (process.env.NODE_ENV !== 'production') {
        warn(
          false, `invalid redirect option: ${JSON.stringify(redirect)}`
        )
      }
      return _createRoute(null, location)
    }
    const re: Object = redirect
    const { name, path } = re
    let { query, hash, params } = location
    query = re.hasOwnProperty('query') ? re.query : query
    hash = re.hasOwnProperty('hash') ? re.hash : hash
    params = re.hasOwnProperty('params') ? re.params : params
    if (name) {
      // resolved named direct
      const targetRecord = nameMap[name]
      if (process.env.NODE_ENV !== 'production') {
        assert(targetRecord, `redirect failed: named route "${name}" not found.`)
      }
      return match({
        _normalized: true,
        name,
        query,
        hash,
        params
      }, undefined, location)
    } else if (path) {
      // 1. resolve relative redirect
      const rawPath = resolveRecordPath(path, record)
      // 2. resolve params
      const resolvedPath = fillParams(rawPath, params, `redirect route with path "${rawPath}"`)
      // 3. rematch with existing query and hash
      return match({
        _normalized: true,
        path: resolvedPath,
        query,
        hash
      }, undefined, location)
    } else {
      if (process.env.NODE_ENV !== 'production') {
        warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`)
      }
      return _createRoute(null, location)
    }
  }
  function alias ( // 處理別名
    record: RouteRecord,
    location: Location,
    matchAs: string
  ): Route {
    const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`)
    const aliasedMatch = match({
      _normalized: true,
      path: aliasedPath
    })
    if (aliasedMatch) {
      const matched = aliasedMatch.matched
      const aliasedRecord = matched[matched.length - 1]
      location.params = aliasedMatch.params
      return _createRoute(aliasedRecord, location)
    }
    return _createRoute(null, location)
  }
  function _createRoute (  // 創(chuàng)建路由
    record: ?RouteRecord,
    location: Location,
    redirectedFrom?: Location
  ): Route {
    if (record && record.redirect) {
      return redirect(record, redirectedFrom || location)
    }
    if (record && record.matchAs) {
      return alias(record, location, record.matchAs)
    }
    return createRoute(record, location, redirectedFrom, router) // 創(chuàng)建路由對(duì)象
  }
  return {
    match,
    addRoutes
  }
}
// dev/src/create-route-map.js line 7
export function createRouteMap (
  routes: Array<RouteConfig>,
  oldPathList?: Array<string>,
  oldPathMap?: Dictionary<RouteRecord>,
  oldNameMap?: Dictionary<RouteRecord>
): {
  pathList: Array<string>,
  pathMap: Dictionary<RouteRecord>,
  nameMap: Dictionary<RouteRecord>
} {
  // the path list is used to control path matching priority
  const pathList: Array<string> = oldPathList || [] // 創(chuàng)建映射表
  // $flow-disable-line
  const pathMap: Dictionary<RouteRecord> = oldPathMap || Object.create(null)
  // $flow-disable-line
  const nameMap: Dictionary<RouteRecord> = oldNameMap || Object.create(null)
  routes.forEach(route => { // 遍歷路由配置,為每個(gè)配置添加路由記錄
    addRouteRecord(pathList, pathMap, nameMap, route)
  })
  // ensure wildcard routes are always at the end
  for (let i = 0, l = pathList.length; i < l; i++) { // 確保通配符在最后
    if (pathList[i] === '*') {
      pathList.push(pathList.splice(i, 1)[0])
      l--
      i--
    }
  }
  if (process.env.NODE_ENV === 'development') {
    // warn if routes do not include leading slashes
    const found = pathList
    // check for missing leading slash
      .filter(path => path && path.charAt(0) !== '*' && path.charAt(0) !== '/')
    if (found.length > 0) {
      const pathNames = found.map(path => `- ${path}`).join('\n')
      warn(false, `Non-nested routes must include a leading slash character. Fix the following routes: \n${pathNames}`)
    }
  }
  return {
    pathList,
    pathMap,
    nameMap
  }
}
function addRouteRecord ( // 添加路由記錄
  pathList: Array<string>,
  pathMap: Dictionary<RouteRecord>,
  nameMap: Dictionary<RouteRecord>,
  route: RouteConfig,
  parent?: RouteRecord,
  matchAs?: string
) {
  const { path, name } = route // 獲得路由配置下的屬性
  if (process.env.NODE_ENV !== 'production') {
    assert(path != null, `"path" is required in a route configuration.`)
    assert(
      typeof route.component !== 'string',
      `route config "component" for path: ${String(
        path || name
      )} cannot be a ` + `string id. Use an actual component instead.`
    )
  }
  const pathToRegexpOptions: PathToRegexpOptions =
    route.pathToRegexpOptions || {}
  const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict)
  if (typeof route.caseSensitive === 'boolean') {
    pathToRegexpOptions.sensitive = route.caseSensitive
  }
  const record: RouteRecord = { // 生成記錄對(duì)象
    path: normalizedPath,
    regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
    components: route.components || { default: route.component },
    instances: {},
    name,
    parent,
    matchAs,
    redirect: route.redirect,
    beforeEnter: route.beforeEnter,
    meta: route.meta || {},
    props:
      route.props == null
        ? {}
        : route.components
          ? route.props
          : { default: route.props }
  }
  if (route.children) { 
    // Warn if route is named, does not redirect and has a default child route.
    // If users navigate to this route by name, the default child will
    // not be rendered (GH Issue #629)
    if (process.env.NODE_ENV !== 'production') {
      if (
        route.name &&
        !route.redirect &&
        route.children.some(child => /^\/?$/.test(child.path))
      ) {
        warn(
          false,
          `Named Route '${route.name}' has a default child route. ` +
            `When navigating to this named route (:to="{name: '${
              route.name
            }'"), ` +
            `the default child route will not be rendered. Remove the name from ` +
            `this route and use the name of the default child route for named ` +
            `links instead.`
        )
      }
    }
    route.children.forEach(child => { // 遞歸路由配置的 children 屬性,添加路由記錄
      const childMatchAs = matchAs
        ? cleanPath(`${matchAs}/${child.path}`)
        : undefined
      addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)
    })
  }
  if (!pathMap[record.path]) { // 如果有多個(gè)相同的路徑,只有第一個(gè)起作用,后面的會(huì)被忽略
    pathList.push(record.path)
    pathMap[record.path] = record
  }
  if (route.alias !== undefined) { // 如果路由有別名的話,給別名也添加路由記錄
    const aliases = Array.isArray(route.alias) ? route.alias : [route.alias]
    for (let i = 0; i < aliases.length; ++i) {
      const alias = aliases[i]
      if (process.env.NODE_ENV !== 'production' && alias === path) {
        warn(
          false,
          `Found an alias with the same value as the path: "${path}". You have to remove that alias. It will be ignored in development.`
        )
        // skip in dev to make it work
        continue
      }
      const aliasRoute = {
        path: alias,
        children: route.children
      }
      addRouteRecord(
        pathList,
        pathMap,
        nameMap,
        aliasRoute,
        parent,
        record.path || '/' // matchAs
      )
    }
  }
  if (name) {
    if (!nameMap[name]) {
      nameMap[name] = record
    } else if (process.env.NODE_ENV !== 'production' && !matchAs) {
      warn(
        false,
        `Duplicate named routes definition: ` +
          `{ name: "${name}", path: "${record.path}" }`
      )
    }
  }
}

在上文的構(gòu)造函數(shù)中實(shí)例化的HTML5History對(duì)象就是對(duì)于history模式下的路由的處理,主要是通過繼承History對(duì)象以及自身實(shí)現(xiàn)的方法完成路由。在初始化VueRouter時(shí)調(diào)用的init方法調(diào)用了路由切換以及調(diào)用了setupListeners方法實(shí)現(xiàn)了路由的切換的監(jiān)聽回調(diào),注意此時(shí)并沒有在HTML5History對(duì)象的構(gòu)造函數(shù)中直接添加事件監(jiān)聽,這是因?yàn)樾枰苊庠谀承g覽器中調(diào)度第一個(gè)popstate事件,但是由于異步保護(hù),第一個(gè)歷史記錄路由未同時(shí)更新的問題。history模式的代碼結(jié)構(gòu)以及更新視圖的邏輯與hash模式基本類似,主要是監(jiān)聽popstate事件以及對(duì)于push()replace()方法的變動(dòng),使用History對(duì)象的pushState()replaceState()等方法進(jìn)行路由的變換。

// dev/src/index.js line 21
export default class VueRouter {
  //...
  init (app: any /* Vue component instance */) {
    process.env.NODE_ENV !== 'production' &&
      assert(
        install.installed,
        `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
          `before creating root instance.`
      )
    this.apps.push(app)
    // set up app destroyed handler
    // https://github.com/vuejs/vue-router/issues/2639
    app.$once('hook:destroyed', () => {
      // clean out app from this.apps array once destroyed
      const index = this.apps.indexOf(app)
      if (index > -1) this.apps.splice(index, 1)
      // ensure we still have a main app or null if no apps
      // we do not release the router so it can be reused
      if (this.app === app) this.app = this.apps[0] || null
      if (!this.app) this.history.teardown()
    })
    // main app previously initialized
    // return as we don't need to set up new history listener
    if (this.app) {
      return
    }
    this.app = app
    const history = this.history
    if (history instanceof HTML5History || history instanceof HashHistory) {
      const handleInitialScroll = routeOrError => {
        const from = history.current
        const expectScroll = this.options.scrollBehavior
        const supportsScroll = supportsPushState && expectScroll
        if (supportsScroll && 'fullPath' in routeOrError) {
          handleScroll(this, routeOrError, from, false)
        }
      }
      const setupListeners = routeOrError => {
        history.setupListeners() // 初始化添加事件監(jiān)聽
        handleInitialScroll(routeOrError)
      }
      history.transitionTo( // 如果默認(rèn)頁,需要根據(jù)當(dāng)前瀏覽器地址欄里的 path 或者 hash 來激活對(duì)應(yīng)的路由
        history.getCurrentLocation(),
        setupListeners,
        setupListeners
      )
    }
    history.listen(route => {
      this.apps.forEach(app => {
        app._route = route
      })
    })
  }
  //...
}
// dev/src/history/base.js line 24
export class History {
  // ...
  transitionTo (
    location: RawLocation,
    onComplete?: Function,
    onAbort?: Function
  ) {
    let route
    // catch redirect option https://github.com/vuejs/vue-router/issues/3201
    try {
      route = this.router.match(location, this.current) // // 獲取匹配的路由信息
    } catch (e) {
      this.errorCbs.forEach(cb => {
        cb(e)
      })
      // Exception should still be thrown
      throw e
    }
    const prev = this.current
    this.confirmTransition( // 確認(rèn)跳轉(zhuǎn)
      route,
      () => {
        this.updateRoute(route) // 更新當(dāng)前 route 對(duì)象
        onComplete && onComplete(route)
        this.ensureURL() // 子類實(shí)現(xiàn)的更新url地址 對(duì)于 hash 模式的話 就是更新 hash 的值
        this.router.afterHooks.forEach(hook => {
          hook && hook(route, prev)
        })
        // fire ready cbs once
        if (!this.ready) {
          this.ready = true
          this.readyCbs.forEach(cb => {
            cb(route)
          })
        }
      },
      err => {
        if (onAbort) {
          onAbort(err)
        }
        if (err && !this.ready) {
          // Initial redirection should not mark the history as ready yet
          // because it's triggered by the redirection instead
          // https://github.com/vuejs/vue-router/issues/3225
          // https://github.com/vuejs/vue-router/issues/3331
          if (!isNavigationFailure(err, NavigationFailureType.redirected) || prev !== START) {
            this.ready = true
            this.readyErrorCbs.forEach(cb => {
              cb(err)
            })
          }
        }
      }
    )
  }
  confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
    const current = this.current
    this.pending = route
    const abort = err => {
      // changed after adding errors with
      // https://github.com/vuejs/vue-router/pull/3047 before that change,
      // redirect and aborted navigation would produce an err == null
      if (!isNavigationFailure(err) && isError(err)) {
        if (this.errorCbs.length) {
          this.errorCbs.forEach(cb => {
            cb(err)
          })
        } else {
          warn(false, 'uncaught error during route navigation:')
          console.error(err)
        }
      }
      onAbort && onAbort(err)
    }
    const lastRouteIndex = route.matched.length - 1
    const lastCurrentIndex = current.matched.length - 1
    if (
      isSameRoute(route, current) && // 如果是相同的路由就不跳轉(zhuǎn)
      // in the case the route map has been dynamically appended to
      lastRouteIndex === lastCurrentIndex &&
      route.matched[lastRouteIndex] === current.matched[lastCurrentIndex]
    ) {
      this.ensureURL()
      return abort(createNavigationDuplicatedError(current, route))
    }
    const { updated, deactivated, activated } = resolveQueue( // 通過對(duì)比路由解析出可復(fù)用的組件,需要渲染的組件,失活的組件
      this.current.matched,
      route.matched
    )
    const queue: Array<?NavigationGuard> = [].concat( // 導(dǎo)航守衛(wèi)數(shù)組
      // in-component leave guards
      extractLeaveGuards(deactivated),  // 失活的組件鉤子
      // global before hooks
      this.router.beforeHooks, // 全局 beforeEach 鉤子
      // in-component update hooks
      extractUpdateHooks(updated), // 在當(dāng)前路由改變,但是該組件被復(fù)用時(shí)調(diào)用
      // in-config enter guards
      activated.map(m => m.beforeEnter), // 需要渲染組件 enter 守衛(wèi)鉤子
      // async components
      resolveAsyncComponents(activated) // 解析異步路由組件
    )
    const iterator = (hook: NavigationGuard, next) => {
      if (this.pending !== route) { // 路由不相等就不跳轉(zhuǎn)路由
        return abort(createNavigationCancelledError(current, route))
      }
      try {
        hook(route, current, (to: any) => { // 只有執(zhí)行了鉤子函數(shù)中的next,才會(huì)繼續(xù)執(zhí)行下一個(gè)鉤子函數(shù),否則會(huì)暫停跳轉(zhuǎn),以下邏輯是在判斷 next() 中的傳參
          if (to === false) {
            // next(false) -> abort navigation, ensure current URL
            this.ensureURL(true)
            abort(createNavigationAbortedError(current, route))
          } else if (isError(to)) {
            this.ensureURL(true)
            abort(to)
          } else if (
            typeof to === 'string' ||
            (typeof to === 'object' &&
              (typeof to.path === 'string' || typeof to.name === 'string'))
          ) {
            // next('/') or next({ path: '/' }) -> redirect
            abort(createNavigationRedirectedError(current, route))
            if (typeof to === 'object' && to.replace) {
              this.replace(to)
            } else {
              this.push(to)
            }
          } else {
            // confirm transition and pass on the value
            next(to)
          }
        })
      } catch (e) {
        abort(e)
      }
    }
    // ...
  }
  // ...
}
// dev/src/history/html5.js line 10
export class HTML5History extends History {
  _startLocation: string
  constructor (router: Router, base: ?string) {
    super(router, base)
    this._startLocation = getLocation(this.base)
  }
  setupListeners () { // 初始化
    if (this.listeners.length > 0) {
      return
    }
    const router = this.router
    const expectScroll = router.options.scrollBehavior
    const supportsScroll = supportsPushState && expectScroll
    if (supportsScroll) {
      this.listeners.push(setupScroll())
    }
    const handleRoutingEvent = () => {
      const current = this.current
      // Avoiding first `popstate` event dispatched in some browsers but first
      // history route not updated since async guard at the same time.
      const location = getLocation(this.base)
      if (this.current === START && location === this._startLocation) {
        return
      }
      this.transitionTo(location, route => {
        if (supportsScroll) {
          handleScroll(router, route, current, true)
        }
      })
    }
    window.addEventListener('popstate', handleRoutingEvent) // 事件監(jiān)聽
    this.listeners.push(() => {
      window.removeEventListener('popstate', handleRoutingEvent)
    })
  }
  go (n: number) {
    window.history.go(n)
  }
  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      pushState(cleanPath(this.base + route.fullPath))
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    }, onAbort)
  }
  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      replaceState(cleanPath(this.base + route.fullPath))
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    }, onAbort)
  }
  ensureURL (push?: boolean) {
    if (getLocation(this.base) !== this.current.fullPath) {
      const current = cleanPath(this.base + this.current.fullPath)
      push ? pushState(current) : replaceState(current)
    }
  }
  getCurrentLocation (): string {
    return getLocation(this.base)
  }
}

以上就是Vue路由History模式分析的詳細(xì)內(nèi)容,更多關(guān)于Vue路由History模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue使用Proxy監(jiān)聽所有接口狀態(tài)的方法實(shí)現(xiàn)

    Vue使用Proxy監(jiān)聽所有接口狀態(tài)的方法實(shí)現(xiàn)

    這篇文章主要介紹了Vue使用Proxy監(jiān)聽所有接口狀態(tài)的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • vue3+vant4實(shí)現(xiàn)pdf文件上傳與預(yù)覽組件

    vue3+vant4實(shí)現(xiàn)pdf文件上傳與預(yù)覽組件

    這篇文章主要介紹了vue3如何結(jié)合vant4實(shí)現(xiàn)簡(jiǎn)單的pdf文件上傳與預(yù)覽組件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • Vue中如何避免props驗(yàn)證失敗問題

    Vue中如何避免props驗(yàn)證失敗問題

    本文將探討 props 驗(yàn)證失敗的常見原因,并提供解決方法,幫助開發(fā)者在 Vue 組件開發(fā)中避免這些問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • vue3中如何用threejs畫一些簡(jiǎn)單的幾何體

    vue3中如何用threejs畫一些簡(jiǎn)單的幾何體

    最近學(xué)習(xí)threejs有些時(shí)間了,就想著著手做些東西,下面這篇文章主要給大家介紹了關(guān)于vue3中如何用threejs畫一些簡(jiǎn)單的幾何體的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • Vue實(shí)現(xiàn)輸入框@功能的示例代碼

    Vue實(shí)現(xiàn)輸入框@功能的示例代碼

    這篇文章主要為大家介紹了如何利用Vue實(shí)現(xiàn)輸入框@功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Vue有一定的幫助,感興趣的可以學(xué)習(xí)一下
    2022-05-05
  • vue 登錄滑動(dòng)驗(yàn)證實(shí)現(xiàn)代碼

    vue 登錄滑動(dòng)驗(yàn)證實(shí)現(xiàn)代碼

    這篇文章主要介紹了vue 登錄滑動(dòng)驗(yàn)證實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • vue3導(dǎo)入excel并解析excel數(shù)據(jù)渲染到表格中(純前端實(shí)現(xiàn))

    vue3導(dǎo)入excel并解析excel數(shù)據(jù)渲染到表格中(純前端實(shí)現(xiàn))

    在Vue中實(shí)現(xiàn)導(dǎo)出Excel有多種方式,可以通過前端實(shí)現(xiàn),也可以通過前后端配合實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于vue3導(dǎo)入excel并解析excel數(shù)據(jù)渲染到表格中的相關(guān)資料,文中介紹的方法是純前端實(shí)現(xiàn),需要的朋友可以參考下
    2024-04-04
  • elementUI多選框反選的實(shí)現(xiàn)代碼

    elementUI多選框反選的實(shí)現(xiàn)代碼

    這篇文章主要介紹了elementUI多選框反選的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 解決Vue中使用Echarts出現(xiàn)There?is?a?chart?instance?already?initialized?on?the?dom的警告問題

    解決Vue中使用Echarts出現(xiàn)There?is?a?chart?instance?already?ini

    使用echarts的時(shí)候,多次加載會(huì)出現(xiàn)There?is?a?chart?instance?already?initialized?on?the?dom.這個(gè)黃色警告,此警告信息不影響echarts正常加載,但是有bug得解決,本文就帶大家解決這個(gè)問題,感興趣的同學(xué)可以參考閱讀
    2023-06-06
  • vue實(shí)現(xiàn)搜索并高亮文字的兩種方式總結(jié)

    vue實(shí)現(xiàn)搜索并高亮文字的兩種方式總結(jié)

    在做文字處理的項(xiàng)目時(shí)經(jīng)常會(huì)遇到搜索文字并高亮的需求,常見的實(shí)現(xiàn)方式有插入標(biāo)簽和貼標(biāo)簽兩種,這兩種方式適用于不同的場(chǎng)景,各有優(yōu)劣,下面我們就來看看他們的具體實(shí)現(xiàn)吧
    2023-11-11

最新評(píng)論