背景:
新公司入職 首次進行代碼評審,在看到 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