vue-router中 push方法 name 和 path 路由跳转的区别(从源码的角度讲)附带性能测试
背景:新公司入职 首次进行代码评审,在看到 this.$router.push('/path1'), 有同事建议使用 name进行路由跳转,给出的理由是更美观,个人虽然有代码洁癖,但是这个理由不是很能接受,于是查看了下vue-router的源码,想从根本上看下下他俩的区别。在看源码之前我所了解的name 和 path的区别是从vue-router 官方文档中获得的,下面贴下文档的说明源码解读pus
·
背景:
新公司入职 首次进行代码评审,在看到 this.$router.push('/path1'), 有同事建议使用 name进行路由跳转,给出的理由是更美观,个人虽然有代码洁癖,但是这个理由不是很能接受,于是查看了下vue-router的源码,想从根本上看下下他俩的区别。
在看源码之前我所了解的name 和 path的区别是从vue-router 官方文档中获得的,下面贴下文档的说明
源码解读
push 方法执行的顺序
- transitionTo
- match
- 格式化参数:normalizeLocation (name:params做浅拷贝,path:对path进行格式化, 得到基本路由、url上参数merge到query、处理hash 返回{ path, query, hash } })
- 匹配路由 name: nameMap[name] 并将params 加在route中; path: pathMap[path]
- 创建跳转路由: _createRoute
- 执行跳转(confirmTransition)等等。。。 后续操作都一致
- match
后面贴一下源码
首先是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
更多推荐
已为社区贡献2条内容
所有评论(0)