背景:

新公司入职 首次进行代码评审,在看到 this.$router.push('/path1'), 有同事建议使用 name进行路由跳转,给出的理由是更美观,个人虽然有代码洁癖,但是这个理由不是很能接受,于是查看了下vue-router的源码,想从根本上看下下他俩的区别。

在看源码之前我所了解的name 和 path的区别是从vue-router 官方文档中获得的,下面贴下文档的说明

 

源码解读

push 方法执行的顺序

  1. transitionTo
    1. match
      1. 格式化参数:normalizeLocation (name:params做浅拷贝,path:对path进行格式化, 得到基本路由、url上参数merge到query、处理hash 返回{ path, query, hash } })
      2. 匹配路由 name: nameMap[name] 并将params 加在route中; path: pathMap[path]
      3. 创建跳转路由: _createRoute
    2. 执行跳转(confirmTransition)等等。。。 后续操作都一致

后面贴一下源码

首先是transitionTo

  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(
      location,
      route => {
        pushHash(route.fullPath)
        handleScroll(this.router, route, fromRoute, false)
        onComplete && onComplete(route)
      },
      onAbort
    )
  }



 transitionTo (
    location: RawLocation,
    onComplete?: Function,
    onAbort?: Function
  ) {
    const route = this.router.match(location, this.current)
    this.confirmTransition(
      route,
      () => {
        const prev = this.current
        this.updateRoute(route)
        onComplete && onComplete(route)
        this.ensureURL()
        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)
          })
        }
      },
...

匹配路由

function match (
    raw,
    currentRoute,
    redirectedFrom
  ) {
    var location = normalizeLocation(raw, currentRoute, false, router);
    var name = location.name;

    if (name) {
      var record = nameMap[name];
      if (process.env.NODE_ENV !== 'production') {
        warn(record, ("Route with name '" + name + "' does not exist"));
      }
      if (!record) { return _createRoute(null, location) }
      var paramNames = record.regex.keys
        .filter(function (key) { return !key.optional; })
        .map(function (key) { return key.name; });

      if (typeof location.params !== 'object') {
        location.params = {};
      }

      if (currentRoute && typeof currentRoute.params === 'object') {
        for (var 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) {
      location.params = {};
      for (var i = 0; i < pathList.length; i++) {
        var path = pathList[i];
        var record$1 = pathMap[path];
        if (matchRoute(record$1.regex, location.path, location.params)) {
          return _createRoute(record$1, location, redirectedFrom)
        }
      }
    }
    // no match
    return _createRoute(null, location)
  }

normalizeLocation

function normalizeLocation (
  raw,
  current,
  append,
  router
) {
  var next = typeof raw === 'string' ? { path: raw } : raw;
  // named target
  if (next._normalized) {
    return next
  } else if (next.name) {
    next = extend({}, raw);
    var params = next.params;
    if (params && typeof params === 'object') {
      next.params = extend({}, params);
    }
    return next
  }

  // relative params
  if (!next.path && next.params && current) {
    next = extend({}, next);
    next._normalized = true;
    var params$1 = extend(extend({}, current.params), next.params);
    if (current.name) {
      next.name = current.name;
      next.params = params$1;
    } else if (current.matched.length) {
      var rawPath = current.matched[current.matched.length - 1].path;
      next.path = fillParams(rawPath, params$1, ("path " + (current.path)));
    } else if (process.env.NODE_ENV !== 'production') {
      warn(false, "relative params navigation requires a current route.");
    }
    return next
  }

  var parsedPath = parsePath(next.path || '');
  var basePath = (current && current.path) || '/';
  var path = parsedPath.path
    ? resolvePath(parsedPath.path, basePath, append || next.append)
    : basePath;

  var query = resolveQuery(
    parsedPath.query,
    next.query,
    router && router.options.parseQuery
  );

  var hash = next.hash || parsedPath.hash;
  if (hash && hash.charAt(0) !== '#') {
    hash = "#" + hash;
  }

  return {
    _normalized: true,
    path: path,
    query: query,
    hash: hash
  }
}

性能测试

创建两个vue组件测试下性能的区别

<!-- 组件A -->
<template>
  <div></div>
</template>

<script>
import Vue from 'vue'

export default {
  name: 'RouteA',
  created () {
    Vue.count === 0 && console.time('push--path性能测试')
    if (Vue.count < 50) {
      Vue.count++
      this.$router.push({ path: '/routeB' })
      // this.$router.push({ name: 'routeB' })
    } else {
      console.timeEnd('push--path性能测试')
    }
  }
}
</script>

<style scoped>

</style>

<!-- 组件B -->
<template>
  <div></div>
</template>

<script>
export default {
  name: 'RouteB',
  created () {
    this.$router.push({ path: '/routeA' })
  }
}
</script>

<style scoped>

</style>

看下控制台的打印,性能上并没有什么区别。

push--path性能测试: 59.458984375ms
push--path性能测试: 65.81689453125ms
push--path性能测试: 61.808837890625ms
push--path性能测试: 67.8359375ms
push--path性能测试: 63.342041015625ms
push--path性能测试: 63.43017578125ms

push--name性能测试: 63.48193359375ms
push--name性能测试: 61.52392578125ms
push--name性能测试: 69.216064453125ms
push--name性能测试: 63.93017578125ms
push--name性能测试: 66.28515625ms
push--name性能测试: 76.2060546875ms
push--name性能测试: 58.2890625ms

总结

在不传params的情况下(一般很少场景会用到这种传参,因为刷新路由参数会丢失),性能是并没有什么却别,参数传递方式没有优劣之分,项目中定一个规范就好。值得注意的是,本来想大数字测下性能的,但是在超过50之后vue会报错误,这个后续可以研究一下,知道的小伙伴欢迎留言。

[Vue warn]: You may have an infinite update loop in a component render function

 

Logo

前往低代码交流专区

更多推荐