diff --git a/docs/.vuepress/configs/sidebar/zh.ts b/docs/.vuepress/configs/sidebar/zh.ts index 6622735d..28bf909c 100644 --- a/docs/.vuepress/configs/sidebar/zh.ts +++ b/docs/.vuepress/configs/sidebar/zh.ts @@ -15,6 +15,7 @@ export const zh: SidebarConfig = { text: '基础', children: [ '/zh/guide/directory-structure.md', + '/zh/guide/config.md', '/zh/guide/route.md', '/zh/guide/plugin.md', '/zh/guide/template.md', diff --git a/docs/zh/guide/config.md b/docs/zh/guide/config.md new file mode 100644 index 00000000..69a031b1 --- /dev/null +++ b/docs/zh/guide/config.md @@ -0,0 +1,79 @@ +# 配置 + +`fes` 在 `.fes.js` 文件中添加项目基础配置。一份常见的配置示例如下: +```js +export default { + base: '/foo/', + publicPath: '/', + devServer: { + port: 8080 + } + mock: { + prefix: '/v2' + }, + proxy: { + '/v2': { + 'target': 'https://api.douban.com/', + 'changeOrigin': true, + }, + }, + layout: { + title: "Fes.js", + footer: 'Created by MumbelFe', + multiTabs: false, + menus: [{ + name: 'index' + }, { + name: 'onepiece' + }, { + name: 'store' + }, { + name: 'simpleList' + }] + } +} +``` + +## 配置文件 +可以新建 `.fes.local.js` 作为本地临时配置文件。这份配置会和 `.fes.js` 做 deep merge 后形成最终配置。 +```js +// .fes.js +export default { mock: false }; + +// .fes.local.js +export default { + mock: true, + dvServer: { port: 8080 } +}; +``` +最终的配置是: +```js +{ + mock: true, + devServer: { port: 8080 } +}; +``` +::: tip +`.fes.local.js` 仅在 fes dev 时有效。 + +`.fes.local.js` 是本地验证使用的临时配置,请将其添加到 `.gitignore`,务必不要提交到 git 仓库中。 + +`.fes.local.js` 配置的优先级最高,比 `FES_ENV` 指定的配置更高。 +::: + +## 多环境多份配置 +可以通过环境变量 `FES_ENV` 区分不同环境,来指定配置文件。 +```js + // .fes.js + export default { mock: false }; + + // .fes.local.js + export default { + mock: true, + dvServer: { port: 8080 } + }; +``` +根据指定的 `FES_ENV` 拿对应的配置,这份配置会和 `.fes.js` 做 deep merge 后形成最终配。 +::: tip +`FES_ENV` 指定的配置优先级高于 `.fes.js` 文件的配置 +::: \ No newline at end of file diff --git a/docs/zh/reference/config/README.md b/docs/zh/reference/config/README.md index 85d56734..45b53853 100644 --- a/docs/zh/reference/config/README.md +++ b/docs/zh/reference/config/README.md @@ -4,574 +4,281 @@ sidebar: auto # 配置 -VuePress 配置的参考文档,可以通过配置文件来设置这些配置。 VuePress 约定的配置文件为(按照优先顺序): +以下配置项通过字母排序。 -- 当前工作目录 `cwd` 下: - - `vuepress.config.ts` - - `vuepress.config.js` -- 源文件目录 `sourceDir` 下: - - `.vuepress/config.ts` - - `.vuepress/config.js` +## alias -你也可以通过 [命令行接口](./cli.md) 的 `--config` 选项来指定配置文件。 +- 类型: `object` +- 默认值: `{}` +- 详情: -## 站点配置 + 配置别名,对引用路径进行映射。 -### base +- 示例: +```js +export default { + alias: { + main: 'src/assets/styles/main' + } +} +``` +然后 `import('main')`,实际上是 `import('src/assets/styles/main')`。 + +## base - 类型: `string` - -- 默认值: `/` - -- 详情: - - 部署站点的基础路径。 - - 如果你想让你的网站部署到一个子路径下,你将需要设置它。它的值应当总是以斜杠开始,并以斜杠结束。举例来说,如果你想将你的网站部署到 `https://foo.github.io/bar/`,那么 `base` 应该被设置成 `"/bar/"`。 - - `base` 将会作为前缀自动地插入到所有以 `/` 开始的其他选项的链接中,所以你只需要指定一次。 - -- 参考: - - [指南 > 静态资源 > Base Helper](../guide/assets.md#base-helper) - - [指南 > 部署](../guide/deployment.md) - -### lang - -- 类型: `string` - -- 默认值: `en-US` - -- 详情: - - 站点的语言。 - - 它将会在最终渲染出的 HTML 中作为 `` 标签的 `lang` 属性。 - - 它可以设置在不同语言的 locales 中。 - -- 参考: - - [配置 > locales](#locales) - -### title - -- 类型: `string` - - 默认值: `''` +- 详情: -- 详情: - - 站点的标题。 - - 它将会作为所有页面标题的后缀,并且在默认主题的导航栏中显示。 - - 它可以设置在不同语言的 locales 中。 - -- 参考: - - [配置 > locales](#locales) - -### description - -- 类型: `string` + 设置路由前缀,通常用于部署到非根目录。比如你有路由 `/pageA`、`/pageB`,然后设置了 `base` 为 `/manage/`,那么就可以通过 `/manage/pageA`、`/manage/pageB` 访问到它们。 + +## cssLoader +- 类型: `object` - 默认值: `''` +- 详情: -- 详情: + 设置 [css-loader 配置项](https://github.com/webpack-contrib/css-loader#options)。 - 站点的描述。 - - 它将会在最终渲染出的 HTML 中作为 `` 标签的 `content` 属性。它会被每个页面的 Frontmatter 中的 `description` 字段覆盖。 - - 它可以设置在不同语言的 locales 中。 - -- 参考: - - [配置 > locales](#locales) - - [Frontmatter > description](./frontmatter.md#description) - -### head - -- 类型: `HeadConfig[]` +## copy +- 类型: `Array(string) || Array(object)` - 默认值: `[]` - - 详情: - 在最终渲染出的 HTML 的 `
` 标签内加入的额外标签。 - - 你可以通过 `[tagName, { attrName: attrValue }, innerHTML?]` 的格式来添加标签。 - - 它可以设置在不同语言的 locales 中。 - + 设置要复制到输出目录的文件、文件夹。 + + 配置约定 `from-to` 规则, 其中 `from` 是相对于 `cwd` 的路径,`to` 是相对于输出路径的路径。 - 示例: - - 增加一个自定义的 favicon : - ```js -module.exports = { - head: [ - ['link', { rel: 'icon', href: '/logo.png' }] - ] +export default { + copy: { + from: '/src/assets/images', + to: 'assets/images' + } } ``` +上面示例中,实现了将 `cwd` 路径中的 `/src/assets/images` 文件夹,在编译完成后,`copy` 到输出路径下的 `assets/images` 文件夹。 - 渲染为: - -```html - - - -``` - -- 参考: - - [配置 > locales](#locales) - - [Frontmatter > head](./frontmatter.md#head) - -### locales - -- 类型: `{ [path: string]: Partial` 标签外额外包裹一层。 - - `highlightLines` 和 `lineNumbers` 依赖于这个额外的包裹层。这换句话说,如果你禁用了 `preWrapper` ,那么行高亮和行号也会被同时禁用。 - - 如果你想要在客户端来实现这些功能时,可以禁用该配置项。比如使用 [Prismjs Line Highlight](https://prismjs.com/plugins/line-highlight/) 或者 [Prismjs Line Numbers](https://prismjs.com/plugins/line-numbers/)。 - -##### markdown.code.vPre - -- 类型: `boolean` - -- 默认值: `true` - -- 详情: - - 是否在 `` 标签上添加 `v-pre` 指令。 - -- 参考: - - [指南 > Markdown > 语法扩展 > 代码块 > 添加 v-pre](../guide/markdown.md#添加-v-pre) - -#### markdown.customComponent - -- 类型: `undefined | false` - -- 详情: - - VuePress 内置的 markdown-it custom-component 插件的配置项。 - - 设置为 `false` 可以禁用该插件。 - -::: danger -除非你了解它的用途,否则你不应该设置该配置项。 -::: - -#### markdown.emoji - -- 类型: `EmojiPluginOptions | false` - -- 详情: - - [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji) 的配置项。 - - 设置为 `false` 可以禁用该插件。 - -- 参考: - - [指南 > Markdown > 语法扩展 > Emoji](../guide/markdown.md#emoji) - -#### markdown.extractHeaders - -- 类型: `ExtractHeadersPluginOptions | false` - -- 详情: - - VuePress 内置的 markdown-it extract-headers 插件的配置项。 - - 它将会把页面的标题提取到 Page Data 中,可以用于生成侧边栏、目录等。比如当前页面的侧边栏,就是由这个插件提取出的标题来自动生成的。 - - 设置为 `false` 可以禁用该插件。 - -#### markdown.hoistTags - -- 类型: `HoistTagsPluginOptions | false` - -- 详情: - - VuePress 内置的 markdown-it hoist-tags 插件的配置项。 - - 它将会把你的 Markdown 中特定的 HTML 标签提升到 SFC 的顶层。默认情况下,只有 ` diff --git a/packages/fes-plugin-layout/src/runtime/views/404.vue b/packages/fes-plugin-layout/src/runtime/views/404.vue new file mode 100644 index 00000000..2b1972fa --- /dev/null +++ b/packages/fes-plugin-layout/src/runtime/views/404.vue @@ -0,0 +1,35 @@ + ++ + + +上一页 + ++{ + "layout": false +} + + diff --git a/packages/fes-plugin-layout/src/runtime/views/Menu.vue b/packages/fes-plugin-layout/src/runtime/views/Menu.vue index 5ee7d084..1240fdec 100644 --- a/packages/fes-plugin-layout/src/runtime/views/Menu.vue +++ b/packages/fes-plugin-layout/src/runtime/views/Menu.vue @@ -1,11 +1,11 @@- + [route.path]); return { selectedKeys, - menus: fixedMenus, + fixedMenus, onMenuClick }; } diff --git a/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue b/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue index ac414d7c..1b17ed8c 100644 --- a/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue +++ b/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue @@ -1,18 +1,18 @@ {{page.name}} @@ -36,7 +36,7 @@diff --git a/packages/fes-plugin-request/src/template/helpers.js b/packages/fes-plugin-request/src/template/helpers.js index c0314996..05b52320 100644 --- a/packages/fes-plugin-request/src/template/helpers.js +++ b/packages/fes-plugin-request/src/template/helpers.js @@ -84,8 +84,6 @@ export function trimObj(obj) { obj[key] = value.trim(); } else if (isObject(value)) { trimObj(value); - } else if (Array.isArray(value)) { - trimObj(value); } }); } diff --git a/packages/fes-plugin-request/src/template/request.js b/packages/fes-plugin-request/src/template/request.js index fad41688..744d29c6 100644 --- a/packages/fes-plugin-request/src/template/request.js +++ b/packages/fes-plugin-request/src/template/request.js @@ -46,7 +46,6 @@ async function axiosMiddleware(context, next) { function getRequestInstance() { const { responseDataAdaptor, - errorConfig, requestInterceptors = [], responseInterceptors = [], errorHandler, @@ -82,7 +81,6 @@ function getRequestInstance() { defaultConfig, dataField: REPLACE_DATA_FIELD, // eslint-disable-line responseDataAdaptor, - errorConfig, errorHandler }, request: scheduler.compose() diff --git a/packages/fes-preset-built-in/src/index.js b/packages/fes-preset-built-in/src/index.js index 83060016..0a2883dd 100644 --- a/packages/fes-preset-built-in/src/index.js +++ b/packages/fes-preset-built-in/src/index.js @@ -43,6 +43,7 @@ export default function () { require.resolve('./plugins/features/nodeModulesTransform'), require.resolve('./plugins/features/vueLoader'), require.resolve('./plugins/features/mock'), + require.resolve('./plugins/features/dynamicImport'), // misc require.resolve('./plugins/misc/route'), diff --git a/packages/fes-preset-built-in/src/plugins/features/dynamicImport.js b/packages/fes-preset-built-in/src/plugins/features/dynamicImport.js new file mode 100644 index 00000000..d24cbaa3 --- /dev/null +++ b/packages/fes-preset-built-in/src/plugins/features/dynamicImport.js @@ -0,0 +1,12 @@ + +export default (api) => { + api.describe({ + key: 'dynamicImport', + config: { + schema(joi) { + return joi.boolean(); + } + }, + default: false + }); +}; diff --git a/packages/fes-preset-built-in/src/plugins/misc/route/index.js b/packages/fes-preset-built-in/src/plugins/misc/route/index.js index 509e997a..c0a2dd50 100644 --- a/packages/fes-preset-built-in/src/plugins/misc/route/index.js +++ b/packages/fes-preset-built-in/src/plugins/misc/route/index.js @@ -57,14 +57,15 @@ const getRoutePath = function (parentRoutePath, fileName) { if (fileName === 'index') { fileName = ''; } - let routePath = posix.join(parentRoutePath, fileName); // /@id.vue -> /:id - routePath = routePath.replace(/@/g, ':'); - // /*.vue -> * - if (routePath === '/*') { - routePath = '*'; + if (fileName.startsWith('@')) { + fileName = fileName.replace(/@/, ':'); } - return routePath; + // /*.vue -> :pathMatch(.*) + if (fileName.includes('*')) { + fileName = fileName.replace('*', ':pathMatch(.*)'); + } + return posix.join(parentRoutePath, fileName); }; // TODO 约定 layout 目录作为布局文件夹, @@ -140,7 +141,7 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) { * @param {*} routes */ -const fix = function (routes) { +const rank = function (routes) { routes.forEach((item) => { const path = item.path; let arr = path.split('/'); @@ -150,9 +151,9 @@ const fix = function (routes) { let count = 0; arr.forEach((sonPath) => { count += 4; - if (sonPath.indexOf(':') !== -1) { + if (sonPath.indexOf(':') !== -1 && sonPath.indexOf(':pathMatch(.*)') === -1) { count += 2; - } else if (sonPath.indexOf('*') !== -1) { + } else if (sonPath.indexOf(':pathMatch(.*)') !== -1) { count -= 1; } else if (sonPath === '') { count += 1; @@ -162,7 +163,7 @@ const fix = function (routes) { }); item.count = count; if (item.children && item.children.length) { - fix(item.children); + rank(item.children); } }); routes = routes.sort((a, b) => b.count - a.count); @@ -175,7 +176,7 @@ const getRoutes = function ({ config, absPagesPath }) { const routes = []; genRoutes(routes, absPagesPath, '/', config); - fix(routes); + rank(routes); return routes; }; diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js index d18e6972..c263bcf1 100644 --- a/packages/fes-template/.fes.js +++ b/packages/fes-template/.fes.js @@ -12,7 +12,7 @@ export default { publicPath: '/', access: { roles: { - admin: ["/", "/onepiece", '/store'] + admin: ["/"] } }, request: { @@ -52,5 +52,6 @@ export default { }, vuex: { strict: true - } + }, + dynamicImport: true }; diff --git a/packages/fes-template/src/app.js b/packages/fes-template/src/app.js index 266d9666..7009c2b4 100644 --- a/packages/fes-template/src/app.js +++ b/packages/fes-template/src/app.js @@ -1,13 +1,13 @@ -import { access } from '@webank/fes'; +import { access as accessApi } from '@webank/fes'; import PageLoading from '@/components/PageLoading'; import UserCenter from '@/components/UserCenter'; export const beforeRender = { loading: - + , action() { - const { setRole } = access; + const { setRole } = accessApi; return new Promise((resolve) => { setTimeout(() => { setRole('admin'); @@ -21,4 +21,10 @@ export const beforeRender = { export const layout = { customHeader: + // unAccessHandler({ next }) { + // next(false); + // }, + // noFoundHandler({ next }) { + // next(false); + // } }; diff --git a/packages/fes-template/src/pages/index.vue b/packages/fes-template/src/pages/index.vue index 2cc53afd..6a282adc 100644 --- a/packages/fes-template/src/pages/index.vue +++ b/packages/fes-template/src/pages/index.vue @@ -81,12 +81,10 @@ export default { locale.setLocale({ lang: 'en-US' }); locale.addLocale({ lang: 'ja-JP', messages: { test: 'テスト' } }); console.log(locale.getAllLocales()); - access.addAccess('/onepiece1'); }, 2000); setTimeout(() => { accessId.value = '11'; }, 4000); - // router.push('/onepiece'); console.log('测试 mock!!'); request('/v2/file').then((data) => { diff --git a/yarn.lock b/yarn.lock index 959af3b7..197e9e21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8149,6 +8149,11 @@ es-module-lexer@^0.4.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e" integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA== +es-module-lexer@^0.4.0: + version "0.4.1" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e" + integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA== + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -17408,6 +17413,35 @@ webpack@^5.21.2, webpack@^5.24.2: watchpack "^2.0.0" webpack-sources "^2.1.1" +webpack@^5.24.2: + version "5.24.3" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.24.3.tgz#6ec0f5059f8d7c7961075fa553cfce7b7928acb3" + integrity sha512-x7lrWZ7wlWAdyKdML6YPvfVZkhD1ICuIZGODE5SzKJjqI9A4SpqGTjGJTc6CwaHqn19gGaoOR3ONJ46nYsn9rw== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.46" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/wasm-edit" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + acorn "^8.0.4" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.7.0" + es-module-lexer "^0.4.0" + eslint-scope "^5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.0.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.1" + watchpack "^2.0.0" + webpack-sources "^2.1.1" + webpackbar@^5.0.0-3: version "5.0.0-3" resolved "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz#f4f96c8fb13001b2bb1348252db4c980ab93aaac"