Comments (2)
关于生成的routes的顺序:
之前忽略了createRoutes最后的sortRoutes方法:
const DYNAMIC_ROUTE_REGEX = /^\/([:*])/;
const sortRoutes = function sortRoutes (routes) {
routes.sort((a, b) => {
if (!a.path.length) {
return -1
}
if (!b.path.length) {
return 1
}
// Order: /static, /index, /:dynamic
// Match exact route before index: /login before /index/_slug
if (a.path === '/') {
return DYNAMIC_ROUTE_REGEX.test(b.path) ? -1 : 1
}
if (b.path === '/') {
return DYNAMIC_ROUTE_REGEX.test(a.path) ? 1 : -1
}
let i;
let res = 0;
let y = 0;
let z = 0;
const _a = a.path.split('/');
const _b = b.path.split('/');
for (i = 0; i < _a.length; i++) {
if (res !== 0) {
break
}
y = _a[i] === '*' ? 2 : _a[i].includes(':') ? 1 : 0;
z = _b[i] === '*' ? 2 : _b[i].includes(':') ? 1 : 0;
res = y - z;
// If a.length >= b.length
if (i === _b.length - 1 && res === 0) {
// unless * found sort by level, then alphabetically
res = _a[i] === '*' ? -1 : (
_a.length === _b.length ? a.path.localeCompare(b.path) : (_a.length - _b.length)
);
}
}
if (res === 0) {
// unless * found sort by level, then alphabetically
res = _a[i - 1] === '*' && _b[i] ? 1 : (
_a.length === _b.length ? a.path.localeCompare(b.path) : (_a.length - _b.length)
);
}
return res
});
routes.forEach((route) => {
if (route.children) {
sortRoutes(route.children);
}
});
return routes
};
使用了sort方法,依据route.path进行排序。
两个同级路由对象a,b的排序按下面的规则:
- 按level遍历, 在level i中没有比较结果的按下一个level继续比较
- 静态路由先于动态路由先于unknown动态路由(*)
- 规则i比较结果为相等,但a已经没有下一个level,但b仍有下一个level
- 若a,b的这一level都是*,则b先于a
- 否则a先于b
- 规则i比较结果为相等,且a,b都没有下一个level,则按字母表顺序
- 特殊处理:
- 对于path是'/'的路由,除了第1个level就是动态路由或*的路由排在它的后面,其余路由都先于'/'
注:
关于如何命名文件来代表dynamic routes,dynamic nested routes和unknown dynamic routes
举例:
(仅作为例子,实际上不会这样写,这里并不是所有路由都能被访问到)
目录结构:
pages/
--| index.vue
--| b.vue
--| detail/
-----| _.vue
-----| _a.vue
-----| _a/
-------| _b.vue
-----| _id.vue
-----| c.vue
-----| d/
-------| e.vue
--| u/
-----| _id.vue
生成routes:
[
{
name: 'detail',
path: '/detail',
component: '/pages/detail/index.vue',
chunkName: 'pages/detail/index'
},
{
name: 'detail-c',
path: '/detail/c',
component: '/pages/detail/c.vue',
chunkName: 'pages/detail/c'
},
{
name: 'detail-d-e',
path: '/detail/d/e',
component: '/pages/detail/d/e.vue',
chunkName: 'pages/detail/d/e'
},
{
name: 'detail-a',
path: '/detail/:a',
component: '/pages/detail/_a.vue',
chunkName: 'pages/detail/_a',
children: [ [Object] ]
},
{
name: 'detail-id',
path: '/detail/:id',
component: '/pages/detail/_id.vue',
chunkName: 'pages/detail/_id'
},
{
name: 'u-id',
path: '/u/:id?',
component: '/pages/u/_id.vue',
chunkName: 'pages/u/_id'
},
{
name: 'detail-all',
path: '/detail/*',
component: '/pages/detail/_.vue',
chunkName: 'pages/detail/_'
},
{
name: 'index',
path: '/',
component: '/pages/index.vue',
chunkName: 'pages/index'
}
]
from fe-weekly.
关于 .nuxt/router.js 的文件是如何生成的
在 vue-app/template
中有一个对应的 router.js
routerOptions
export const routerOptions = {
mode: '<%= router.mode %>',
base: '<%= router.base %>',
linkActiveClass: '<%= router.linkActiveClass %>',
linkExactActiveClass: '<%= router.linkExactActiveClass %>',
scrollBehavior,
<%= isTest ? '/* eslint-disable array-bracket-spacing, quotes, quote-props, object-curly-spacing, key-spacing */' : '' %>
routes: [<%= _routes %>],
<%= isTest ? '/* eslint-enable array-bracket-spacing, quotes, quote-props, object-curly-spacing, key-spacing */' : '' %>
<% if (router.parseQuery) { %>parseQuery: <%= serializeFunction(router.parseQuery) %>,<% } %>
<% if (router.stringifyQuery) { %>stringifyQuery: <%= serializeFunction(router.stringifyQuery) %>,<% } %>
fallback: <%= router.fallback %>
}
转换后的:
import scrollBehavior from './router.scrollBehavior.js'
export const routerOptions = {
mode: 'history',
base: decodeURI('/'),
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [],
fallback: false
}
核心
packages/builder/src/builder.js,依赖了 lodash/template
import template from 'lodash/template'
import TemplateContext from './context/template'
export default class Builder {
constructor (nuxt, bundleBuilder) {
}
async build () {}
}
compileTemplates
import fsExtra from 'fs-extra'
async compileTemplates (templateContext) {
await Promise.all(
templateFiles.map(async (templateFile) => {
const { src, dst, custom } = templateFile
// ...
const fileContent = await fsExtra.readFile(src, 'utf8')
let content
try {
const templateFunction = template(fileContent, templateOptions)
} catch (err) {
// ...
}
})
)
}
packages/builder/src/context/template
export default class TemplateContext {
constructor (builder, options) {
this.templateFiles = Array.from(builder.template.files)
this.templateVars = {}
}
get templateOptions () {
}
}
generateRoutesAndFiles
async generateRoutesAndFiles () {
const templateContext = this.createTemplateContext()
}
createTemplateContext
import TemplateContext from './context/template'
createTemplateContext () {
return new TemplateContext(this, this.options)
}
.nuxt 目录如何生成
import {
r
} from '@nuxt/utils'
async build () {
await fsExtra.emptyDir(r(this.options.buildDir))
}
依赖了一个 lodash
import uniqBy from 'lodash/uniqBy'
循环生成:
import { interopDefault } from './utils'
<%= uniqBy(_components, '_name').map((route) => {
if (!route.component) return ''
const path = relativeToBuild(route.component)
const chunkName = wChunk(route.chunkName)
const name = route._name
if (splitChunks.pages) {
return `const ${name} = () => interopDefault(import('${path}' /* webpackChunkName: "${chunkName}" */))`
} else {
return `import ${name} from '${path}'`
}
}).join('\n')%>
输出的:
import { interopDefault } from './utils'
const _1bdf586c = () => interopDefault(import('../pages/a.vue' /* webpackChunkName: "pages/a" */))
const _f963b7de = () => interopDefault(import('../pages/a/b.vue' /* webpackChunkName: "pages/a/b" */))
const _bef819ea = () => interopDefault(import('../pages/a/c.vue' /* webpackChunkName: "pages/a/c" */))
interopDefault
文件:/packages/vue-app/template/utils.js
export function interopDefault (promise) {
return promise.then(m => m.default || m)
}
hash
import hash from 'hash-sum'
在 packages/builder
定义了依赖:
"hash-sum": "^2.0.0"
模板里面的
packages/builder/src/context.js
导入几个方法:hash
、serialize
、uniqBy
、devalue
、wChunk
import hash from 'hash-sum'
import serialize from 'serialize-javascript'
import uniqBy from 'lodash/uniqBy'
import devalue from '@nuxt/devalue'
import { r, wp, wChunk, serializeFunction, isFullStatic } from '@nuxt/utils'
export default class TemplateContext {
get templateOptions () {
return {
imports: {
uniqBy,
serialize,
devalue,
hash,
wChunk,
...
},
interpolate: /<%=([\s\S]+?)%>/g
}
}
}
recursiveRoutes
<% function recursiveRoutes(routes, tab, components, indentCount) {
let res = ''
const baseIndent = tab.repeat(indentCount)
const firstIndent = '\n' + tab.repeat(indentCount + 1)
const nextIndent = ',' + firstIndent
routes.forEach((route, i) => {
})
return res
}
const _components = []
const _routes = recursiveRoutes(router.routes, ' ', _components, 1)
%>
循环 routes
,这里面就会用到 hash
routes.forEach((route, i) => {
let resMap = ''
// If need to handle named views
if (route.components) {
let _name = '_' + hash(route.components.default)
if (splitChunks.pages) {
resMap += `${firstIndent}${tab}default: ${_name}`
} else {
resMap += `${firstIndent}${tab}default: () => ${_name}.default || ${_name}`
}
for (const k in route.components) {
_name = '_' + hash(route.components[k])
const component = { _name, component: route.components[k] }
if (k === 'default') {
components.push({
...component,
name: route.name,
chunkName: route.chunkName
})
} else {
components.push({
...component,
name: `${route.name}-${k}`,
chunkName: route.chunkNames[k]
})
if (splitChunks.pages) {
resMap += `${nextIndent}${tab}${k}: ${_name}`
} else {
resMap += `${nextIndent}${tab}${k}: () => ${_name}.default || ${_name}`
}
}
}
route.component = false
} else {
route._name = '_' + hash(route.component)
components.push({ _name: route._name, component: route.component, name: route.name, chunkName: route.chunkName })
}
// @see: https://router.vuejs.org/api/#router-construction-options
res += '{'
res += firstIndent + 'path: ' + JSON.stringify(route.path)
res += (route.components) ? nextIndent + 'components: {' + resMap + '\n' + baseIndent + tab + '}' : ''
res += (route.component) ? nextIndent + 'component: ' + route._name : ''
res += (route.redirect) ? nextIndent + 'redirect: ' + JSON.stringify(route.redirect) : ''
res += (route.meta) ? nextIndent + 'meta: ' + JSON.stringify(route.meta) : ''
res += (typeof route.props !== 'undefined') ? nextIndent + 'props: ' + (typeof route.props === 'function' ? serialize(route.props) : JSON.stringify(route.props)) : ''
res += (typeof route.caseSensitive !== 'undefined') ? nextIndent + 'caseSensitive: ' + JSON.stringify(route.caseSensitive) : ''
res += (route.alias) ? nextIndent + 'alias: ' + JSON.stringify(route.alias) : ''
res += (route.pathToRegexpOptions) ? nextIndent + 'pathToRegexpOptions: ' + JSON.stringify(route.pathToRegexpOptions) : ''
res += (route.name) ? nextIndent + 'name: ' + JSON.stringify(route.name) : ''
if (route.beforeEnter) {
if(isTest) { res += ',\n/* eslint-disable indent, semi */' }
res += (isTest ? firstIndent : nextIndent) + 'beforeEnter: ' + serialize(route.beforeEnter)
if(isTest) { res += firstIndent + '/* eslint-enable indent, semi */' }
}
res += (route.children) ? nextIndent + 'children: [' + recursiveRoutes(routes[i].children, tab, components, indentCount + 1) + ']' : ''
res += '\n' + baseIndent + '}' + (i + 1 === routes.length ? '' : ', ')
})
@nuxt/utils
wChunk
export const wChunk = function wChunk (p = '') {
return p
}
from fe-weekly.
Related Issues (20)
- cssnano 相关
- vue-meta HOT 3
- vuex命名空间 HOT 2
- pinia 初探
- Vue.observable 相关
- nuxt之上下文对象context HOT 2
- Server middleware HOT 2
- Nuxt3 HOT 3
- nuxtServerInit HOT 1
- Nuxt 3 useRuntimeConfig
- Nuxt3 请求数据
- Nuxt3 config配置 HOT 5
- vue3 api使用方法 HOT 5
- Nuxt3数据获取
- V3 setup方法与setup语法糖探究
- watchQuery HOT 3
- SEO优化 HOT 1
- undici
- 2022-7-21技术分享总结 HOT 5
- vue3 相关 HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fe-weekly.