From f6c3f5fd885976883d4313bbf4980bbc10f493dc Mon Sep 17 00:00:00 2001 From: harrywan Date: Thu, 22 Oct 2020 10:09:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(fes-cli):=20=E8=B7=AF=E7=94=B1=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=A8=A1=E7=B3=8A=E5=8C=B9=E9=85=8D=E5=92=8C=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E8=B7=AF=E7=94=B1=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 路由支持模糊匹配,例如"pages/*.vue"解析成"*",通过模糊匹配可以实现404 2.根据精准匹配优先原则实现智能路由 3. pages下的名称为componets的文件夹被忽略,其中的vue文件不会被解析成路由 re #32 --- packages/fes-cli/build/preComplie/route.js | 213 +++++++++++------- packages/fes-cli/build/tasks/route.js | 10 +- .../fes-doc/docs/guide/directory-structure.md | 2 + packages/fes-doc/docs/guide/route.md | 45 +--- packages/fes-template/package-lock.json | 94 ++++++++ .../fes-template/src/assets/images/404.png | Bin 0 -> 7442 bytes packages/fes-template/src/pages/*.vue | 42 ++++ .../src/pages/form/components/card.vue | 8 + 8 files changed, 291 insertions(+), 123 deletions(-) create mode 100644 packages/fes-template/src/assets/images/404.png create mode 100644 packages/fes-template/src/pages/*.vue create mode 100644 packages/fes-template/src/pages/form/components/card.vue diff --git a/packages/fes-cli/build/preComplie/route.js b/packages/fes-cli/build/preComplie/route.js index 15fed007..a4e2460b 100644 --- a/packages/fes-cli/build/preComplie/route.js +++ b/packages/fes-cli/build/preComplie/route.js @@ -1,121 +1,164 @@ // pages -// ├── index.fes # 根路由页面 路径 index.html#/ -// ├── a.fes # 路径 /a +// ├── index.vue # 根路由页面 路径 / +// ├── *.vue # 模糊匹配 路径 * +// ├── a.vue # 路径 /a // ├── b -// │ ├── index.fes # 路径 /b -// │ ├── @id.fes # 动态路由 /b/:id -// │ └── c.fes # 路径 /b/c -// └── layout.fes # 根路由下所有page共用的外层 +// │ ├── index.vue # 路径 /b +// │ ├── @id.vue # 动态路由 /b/:id +// │ └── c.vue # 路径 /b/c +// └── layout.vue # 根路由下所有page共用的外层 const fs = require('fs'); const Path = require('path'); -let pagesDir; -let outputPageDir; -let components = []; -function checkHasLayout(path) { +const isProcessFile = function (path) { + const ext = Path.extname(path); + return fs.statSync(path).isFile() && ['.fes', '.vue'].includes(ext); +}; + +const isProcessDirectory = function (path, item) { + const component = Path.posix.join(path, item); + return fs.statSync(component).isDirectory() && !['components'].includes(item); +}; + +const checkHasLayout = function (path) { const dirList = fs.readdirSync(path); - let hasLayout = false; - dirList.forEach((item) => { - if (fs.statSync(`${path}/${item}`).isFile() - && item[0] !== '.' && ['.fes', '.vue'].indexOf(Path.extname(item)) !== -1 - && Path.basename(item, Path.extname(item)) === 'layout') { - hasLayout = true; + return dirList.some((item) => { + if (!isProcessFile(Path.posix.join(path, item))) { + return false; } + const ext = Path.extname(item); + const fileName = Path.basename(item, ext); + return fileName === 'layout'; }); - return hasLayout; -} +}; -function routeUrlFormmter(str) { - return str.replace(/@/g, ':'); -} +const getRouteName = function (parentRoutePath, fileName) { + const routeName = Path.posix.join(parentRoutePath, fileName); + return routeName.slice(1).replace(/\//g, '_').replace(/@/g, '_').replace(/\*/g, 'FUZZYMATCH'); +}; -function genRoute(path, prePathUrl, preRoutes) { - const hasLayout = checkHasLayout(path); +const getRoutePath = function (parentRoutePath, fileName) { + // /index.vue -> / + if (fileName === 'index') { + fileName = ''; + } + let routePath = Path.posix.join(parentRoutePath, fileName); + // /@id.vue -> /:id + routePath = routePath.replace(/@/g, ':'); + // /*.vue -> * + if (routePath === '/*') { + routePath = '*'; + } + return routePath; +}; + +const build = function (components, parentRoutes, path, parentRoutePath) { const dirList = fs.readdirSync(path); - let childRoutes = {}; - const parentRoutes = {}; - const preRouteUrl = routeUrlFormmter(prePathUrl); + const hasLayout = checkHasLayout(path); + const layoutRoute = { + children: [] + }; if (hasLayout) { - parentRoutes[preRouteUrl] = { - subRoutes: childRoutes - }; - } else { - childRoutes = parentRoutes; + layoutRoute.path = parentRoutePath; + parentRoutes.push(layoutRoute); } dirList.forEach((item) => { - if (fs.statSync(`${path}/${item}`).isFile() - && item[0] !== '.' && ['.fes', '.vue'].indexOf(Path.extname(item)) !== -1) { - const fileName = Path.basename(item, Path.extname(item)); - const preRouteName = path.slice(pagesDir.length + 1); - let routePath = Path.posix.join(preRouteUrl, (fileName === 'index' ? '' : fileName.replace(/@/g, ':'))); - const filePath = Path.resolve(path.replace(pagesDir, outputPageDir), item); - let routeName = preRouteName ? `${preRouteName}_${fileName}` : fileName; - routeName = routeName.replace(/\//g, '_').replace(/@/g, ''); - routePath = routePath.replace(/@/g, ':'); - - if (hasLayout && fileName === 'index') { - routePath = '/'; - } else if (hasLayout && fileName !== 'index') { - routePath = routePath.split(preRouteUrl)[1]; - } + // 文件或者目录的绝对路径 + const component = Path.posix.join(path, item); + if (isProcessFile(component)) { + const ext = Path.extname(item); + const fileName = Path.basename(item, ext); + // 路由的path + const routePath = getRoutePath(parentRoutePath, fileName); + // 路由名称 + const routeName = getRouteName(parentRoutePath, fileName); components.push({ name: routeName, - path: filePath.replace(/\\/g, '\\\\') + path: component }); - if (fileName === 'layout') { - parentRoutes[preRouteUrl].component = routeName; - return; + if (hasLayout) { + if (fileName === 'layout') { + layoutRoute.component = routeName; + } else { + layoutRoute.children.push({ + path: routePath, + component: routeName, + name: routeName + }); + } + } else { + parentRoutes.push({ + path: routePath, + component: routeName, + name: routeName + }); } - childRoutes[routePath] = { - name: routeName || 'index', - component: routeName - }; } }); - preRoutes = Object.assign(preRoutes, parentRoutes); - const toNextRoutes = hasLayout ? childRoutes : preRoutes; dirList.forEach((item) => { - if (fs.statSync(`${path}/${item}`).isDirectory()) { - let toNextPreRouteUrl = Path.posix.join(prePathUrl, item); + if (isProcessDirectory(path, item)) { + // 文件或者目录的绝对路径 + const component = Path.posix.join(path, item); + const nextParentRouteUrl = Path.posix.join(parentRoutePath, item); if (hasLayout) { - toNextPreRouteUrl = Path.posix.join('/', item); + build(components, layoutRoute.children, component, nextParentRouteUrl); + } else { + build(components, parentRoutes, component, nextParentRouteUrl); } - genRoute(`${path}/${item}`, toNextPreRouteUrl, toNextRoutes, outputPageDir, components); } }); -} +}; -function fixRouter2(routes, newRoutes, f) { - Object.keys(routes).forEach((p) => { - const item = { - path: f ? p.slice(1) : p, - component: routes[p].component - }; - if (routes[p].name) { - item.name = routes[p].name; +/** + * 智能路由 + * 1、路由的路径是多个“/”组成的字符串,使用“/”分割后得到不同的子项 + * 2、计算子项个数,用个数乘以4,计入得分 + * 3、判断子项是否是静态的,即不包含“:”、“*”等特殊字符串,若是计入3分。 + * 4、判断子项是否是动态的,即包含“:”特殊字符,若是计入2分。 + * 5、判断子项是否是模糊匹配,即包含“*”特殊字符,若是扣除1分。 + * 6、判断子项是否是根端,即只是“/”,若是计入1分。 + + * @param {*} routes + */ +const fix = function (routes) { + routes.forEach((item) => { + const path = item.path; + let arr = path.split('/'); + // console.log(arr); + if (arr[0] === '') { + arr = arr.slice(1); } - if (routes[p].subRoutes) { - item.children = []; - fixRouter2(routes[p].subRoutes, item.children, true); + let count = 0; + arr.forEach((sonPath) => { + count += 4; + if (sonPath.indexOf(':') !== -1) { + count += 2; + } else if (sonPath.indexOf('*') !== -1) { + count -= 1; + } else if (sonPath === '') { + count += 1; + } else { + count += 3; + } + }); + item.count = count; + if (item.children && item.children.length) { + fix(item.children); } - newRoutes.push(item); }); -} + routes = routes.sort((a, b) => b.count - a.count); +}; -module.exports = function (_pagesDir, _outputPageDir) { - const routes = {}; - const newRoutes = []; - components = []; - pagesDir = _pagesDir; - outputPageDir = _outputPageDir; - genRoute(pagesDir, '/', routes); - fixRouter2(routes, newRoutes); +module.exports = function (pageDir) { + const components = []; + const routes = []; + build(components, routes, pageDir, '/'); + fix(routes); return { - routes, - newRoutes, - components + components, + routes }; }; diff --git a/packages/fes-cli/build/tasks/route.js b/packages/fes-cli/build/tasks/route.js index 9543b7d9..121c20f6 100644 --- a/packages/fes-cli/build/tasks/route.js +++ b/packages/fes-cli/build/tasks/route.js @@ -14,22 +14,22 @@ function generateRoute(config) { export default {{routes}}; `; - const routes = getRoute(config.folders.PROJECT_PAGE_DIR, config.folders.PROJECT_PAGE_DIR); + const { components, routes } = getRoute(config.folders.PROJECT_PAGE_DIR); const componentsTemplate = []; let template = ''; if (config.lazyRouter) { const componentsObj = {}; - routes.components.forEach((item) => { + components.forEach((item) => { componentsObj[item.name] = item.path; }); // component: () => import( /* webpackChunkName: "home" */ '../views/Home.vue') template = render(MAIN_TEMPLATE, { include: '', - routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, ($0, $1) => `"component": () => import( /* webpackChunkName: "${$1}" */ '${componentsObj[$1]}')`) + routes: JSON.stringify(routes).replace(/"component":"(.+?)"/g, ($0, $1) => `"component": () => import( /* webpackChunkName: "${$1}" */ '${componentsObj[$1]}')`) }); } else { - routes.components.forEach((item) => { + components.forEach((item) => { componentsTemplate.push(render(IMPORT_TEMPLATE, { name: item.name, path: item.path @@ -38,7 +38,7 @@ export default {{routes}}; template = render(MAIN_TEMPLATE, { include: componentsTemplate.join(endOfLine), - routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, '"component": $1') + routes: JSON.stringify(routes).replace(/"component":"(.+?)"/g, '"component": $1') }); } diff --git a/packages/fes-doc/docs/guide/directory-structure.md b/packages/fes-doc/docs/guide/directory-structure.md index 4921da4f..14e04d5e 100644 --- a/packages/fes-doc/docs/guide/directory-structure.md +++ b/packages/fes-doc/docs/guide/directory-structure.md @@ -29,6 +29,8 @@ fes-project │   ├── home │   │   └── index.vue │   └── list + │   ├── components + │   │ └── common.vue │   ├── edit │   │   └── index.vue │   └── index.vue diff --git a/packages/fes-doc/docs/guide/route.md b/packages/fes-doc/docs/guide/route.md index 2b8de416..1165dcc0 100644 --- a/packages/fes-doc/docs/guide/route.md +++ b/packages/fes-doc/docs/guide/route.md @@ -6,6 +6,7 @@ ``` pages ├── index.vue # 根路由页面 路径 index.html#/ + ├── *.vue # 模糊匹配 路径 * ├── a.vue # 路径 /a ├── b # 文件夹b │ ├── index.vue # 路径 /b @@ -14,41 +15,19 @@ └── layout.vue # 根路由下所有page共用的外层 ``` 1. 如果目录下有`layout.vue`,则子目录对应的路径是当前目录对应路径的子路由。如果没有则子目录对应的路径和当前目录对应路径是平级的。 -2. 带参数的路径使用`@[filename].vue`的方式 +2. 带参数的路径使用`@[filename].vue`的方式,例如@id.vue +3. 支持模糊匹配,例如`pages/*.vue`对应的路径是`*`,可以通过此路由实现404效果 +4. pages下的components文件夹下的.vue不被解析成路由,可以存放跟业务相关的公共组件。 -
-Fes编译代码之前会根据 pages 目录结构生成下面的配置代码: +## 智能路由匹配 +根据精准匹配优先算法原则设计出路由排名算法,对匹配到的路由打分,选择分数最高的路由: +- 路由的路径每个子项得到4分 +- 子项为静态细分(/list)再加3分 +- 子项为动态细分(/:orderId)再加2分 +- 根段加1分 +- 通配符匹配到的减去1分 -```javascript -import layout from 'D:\\git\\fes-template\\src\\pages\\layout.vue'; -import index from 'D:\\git\\fes-template\\src\\pages\\index.vue'; -import a from 'D:\\git\\fes-template\\src\\pages\\a.vue'; -import b_index from 'D:\\git\\fes-template\\src\\pages\\b\\index.vue'; -import b__id from 'D:\\git\\fes-template\\src\\pages\\b\\@id.vue'; -import b_c from 'D:\\git\\fes-template\\src\\pages\\b\\b_c'; -export default { - '/': { - component: layout, - subRoutes: { - '/' : { - name: 'index', component: index - }, - '/a' : { - name: 'a', component: a - }, - '/b' : { - name: 'b_index', component: b_index - }, - '/b/:id' : { - name: 'b__id', component: b__id - }, - '/c' : { - name: 'b_c', component: b_c - } - } - } -}; -``` +通过智能路由匹配可以解决类似`/list/create`和`/list/:id`到底匹配什么路由的问题。 ## 跳转路由 API参考[Vue-router](https://router.vuejs.org/zh-cn/)。[路由实例](https://router.vuejs.org/zh-cn/api/router-instance.html)路由实例会挂载在FesApp对象上。 diff --git a/packages/fes-template/package-lock.json b/packages/fes-template/package-lock.json index 39b138d9..b809ad16 100644 --- a/packages/fes-template/package-lock.json +++ b/packages/fes-template/package-lock.json @@ -380,6 +380,28 @@ "vue-eslint-parser": "^6.0.4" } }, + "@webank/fes-core": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@webank/fes-core/-/fes-core-0.2.4.tgz", + "integrity": "sha512-sBpNzpp5EJrsEh2fx8wQGtYiZdRG0eSWe9cr+LTCtou119zFZ3Ed9Bsth0mgLAx//Z57c0/FyxMsSsK3q2Lrmg==", + "requires": { + "axios": "^0.16.2", + "lodash": "^4.17.15", + "vue": "^2.6.10", + "vue-i18n": "^8.4.0", + "vue-router": "^2.6.0", + "vue-template-compiler": "^2.6.10" + } + }, + "@webank/fes-ui": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@webank/fes-ui/-/fes-ui-0.2.4.tgz", + "integrity": "sha512-ovemdhoY1w65Op7RYnMnGv2IZkj+LD1cMxzyjyb48LhZaQj2ZkO3y8ViLUHVxAUpapEQ2qlCrd5y1IFs9bwefg==", + "requires": { + "async-validator": "^1.8.2", + "xss": "^1.0.7" + } + }, "abs-svg-path": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", @@ -508,6 +530,20 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async-validator": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.12.2.tgz", + "integrity": "sha512-57EETfCPFiB7M4QscvQzWSGNsmtkjjzZv318SK1CBlstk+hycV72ocjriMOOM48HjvmoAoJGpJNjC7Z76RlnZA==" + }, + "axios": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz", + "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=", + "requires": { + "follow-redirects": "^1.2.3", + "is-buffer": "^1.1.5" + } + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -687,6 +723,11 @@ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "dev": true }, + "cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" + }, "d3-array": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", @@ -817,6 +858,11 @@ "lodash": "^4.17.15" } }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -1316,6 +1362,11 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1394,6 +1445,11 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -1504,6 +1560,11 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", @@ -2482,6 +2543,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "vue": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz", + "integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==" + }, "vue-eslint-parser": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-6.0.5.tgz", @@ -2496,6 +2562,25 @@ "lodash": "^4.17.11" } }, + "vue-i18n": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.22.1.tgz", + "integrity": "sha512-JNgiEJ5a8YPfk5y2lKyfOAGLmkpAVfhaUi+T4wGpSppRYZ3XSyawSDDketY5KV2CsAiBLAGEIO6jO+0l2hQubg==" + }, + "vue-router": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-2.8.1.tgz", + "integrity": "sha512-MC4jacHBhTPKtmcfzvaj2N7g6jgJ/Z/eIjZdt+yUaUOM1iKC0OUIlO/xCtz6OZFFTNUJs/1YNro2GN/lE+nOXA==" + }, + "vue-template-compiler": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz", + "integrity": "sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==", + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2530,6 +2615,15 @@ "requires": { "mkdirp": "^0.5.1" } + }, + "xss": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.8.tgz", + "integrity": "sha512-3MgPdaXV8rfQ/pNn16Eio6VXYPTkqwa0vc7GkiymmY/DqR1SE/7VPAAVZz1GJsJFrllMYO3RHfEaiUGjab6TNw==", + "requires": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + } } } } diff --git a/packages/fes-template/src/assets/images/404.png b/packages/fes-template/src/assets/images/404.png new file mode 100644 index 0000000000000000000000000000000000000000..736d8dbebcc6e390ab6bd03da815d3642281df21 GIT binary patch literal 7442 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 +
+ +
404
+
对不起, 你访问的页面不存在!
+ 返回首页 +
+ + + diff --git a/packages/fes-template/src/pages/form/components/card.vue b/packages/fes-template/src/pages/form/components/card.vue new file mode 100644 index 00000000..12e5036b --- /dev/null +++ b/packages/fes-template/src/pages/form/components/card.vue @@ -0,0 +1,8 @@ + +