You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Vue Router is not designed for tons of static path. Usually people should use dynamic routes when rendering lots of contents from database. Usually Vue Router won't need to map a looooong routes array to find a matching route. But in VuePress, all routes are static, and to provide redirects for /a/b and /a/b.md to /a/b.html, we are writing 3 routes for one page into vue-router.
The performance issue is from:
1 markdown files generate 1 final record and 2 redirect record
vue-router support dynamic route in records, so it use expensive RegExp.test() againest all routes rather than simple string compare, and vue-router are "Mapping all routes" in each "route resolve"
Suggested solution
To simply solve this, we can change the array of static routes into a map, which is much faster.
We should:
stop registering any static routes and maintain a page map
router.beforeResolve(async(to,from)=>{if(to.path!==from.path||from===START_LOCATION){letpageId='404'if(pageMap.has(to.path){pageId=pageMap.get(to.path).id}if(to.path.endsWith('/index.html')){// find if the one without suffix existsconstredirectedPath=to.path.substring(0,to.path.length-10);if(pageMap.has(redirectedPath){pageId=pageMap.get(redirectedPath).id}}if(to.path.endsWith('.html')){// find if the one without suffix exists}// also check the url encoded one and .md suffix// if no match at this point, then we should provide 404 page// write to meta into the recordto.meta=pagesMap.get(pageId).meta;;[to.meta._data]=awaitPromise.all([resolvers.resolvePageData(pageId),pagesComponents[to.nameasstring]?.__asyncLoader(),])}})
In the above approach, we can even merge the original pagesComponents with it, as there won't 2 different pages using same path (an extra check and warnings can be done at node to check app.pages during prepare stage), changing the pagesComponents key from page hash key to page path can done the job:
constpagesMap=newMap({'/a': {content: ()=>import('./a.html.vue'),data: ()=>import('./a.html.js'),// previously route metameta: {title: "A"}},'/b/': {content: ()=>import('./b.index.html.vue'),data: ()=>import('./b.index.html.js'),// previously route metameta: {title: "A"}},// ...'404': {content: ()=>import('./404.html.vue'),}})router.beforeResolve(async(to,from)=>{if(to.path!==from.path||from===START_LOCATION){leturl='404'if(pageMap.has(to.path){pageComponent=to.path}if(to.path.endsWith('/index.html')){// find if the one without suffix existsconstredirectedPath=to.path.substring(0,to.path.length-10);if(pageMap.has(redirectedPath){url=redirectedPath}}if(to.path.endsWith('.html')){// find if the one without suffix exists}// also check the url encoded one and .md suffix// if no match at this point, then we should provide 404 page;[to.meta._data]=awaitPromise.all([resolvers.resolvePageData(url),pageMap[url]?.__asyncLoader(),])}})
That is, we just remove key usage at client, each page is identified by its final url.
Besides reducing the high cost with router.resolve(), we also minify route maps (with redirects) and page component maps into 1 single map. To archive furthermore, we can also merge pagesData map into it, so there is only 1 map holding vuepress client data.
Other tricky things can be done to reduce the output size, one is we can use shorter key with ts enums:
constenumPageMapKey{content: 'c',data: 'd',meta: 'm',}constpagesMap=newMap({'/a': {// will be compiled to 'c'[PageMapKey.content]: ()=>import('./a.html.vue'),[PageMapKey.data]: ()=>import('./a.html.js'),[PageMapKey.meta]: {title: "A"}},// ...}
Since the current behavior will change the result of router.getRoutes(), we need to expose the pageMap in @vuepress/client for developers and users to get page routes and contents.
Meanwhile, we can add built-in functions to cover normal usage:
Clear and concise description of the problem
Vue Router is not designed for tons of static path. Usually people should use dynamic routes when rendering lots of contents from database. Usually Vue Router won't need to map a looooong routes array to find a matching route. But in VuePress, all routes are static, and to provide redirects for /a/b and /a/b.md to /a/b.html, we are writing 3 routes for one page into vue-router.
The performance issue is from:
RegExp.test()
againest all routes rather than simple string compare, and vue-router are "Mapping all routes" in each "route resolve"Suggested solution
To simply solve this, we can change the array of static routes into a map, which is much faster.
We should:
use a catchAll (or fallback) route to render both static pages and 404 page.
Since all pages are using
VuePress
component, we just need to modify the logic of getting page data:In the above approach, we can even merge the original
pagesComponents
with it, as there won't 2 different pages using same path (an extra check and warnings can be done at node to checkapp.pages
during prepare stage), changing the pagesComponents key from page hash key to page path can done the job:That is, we just remove key usage at client, each page is identified by its final url.
Besides reducing the high cost with
router.resolve()
, we also minify route maps (with redirects) and page component maps into 1 single map. To archive furthermore, we can also mergepagesData
map into it, so there is only 1 map holding vuepress client data.Other tricky things can be done to reduce the output size, one is we can use shorter key with ts enums:
router.getRoutes()
, we need to expose thepageMap
in@vuepress/client
for developers and users to get page routes and contents.Meanwhile, we can add built-in functions to cover normal usage:
Alternative
No response
Additional context
No response
The text was updated successfully, but these errors were encountered: