From 1a406164d5623a7bd45fe3e7b905417bd4d4c333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=BA=AF?= Date: Fri, 5 Mar 2021 17:34:04 +0800 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20=20=E7=8E=AF=E5=A2=83=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E4=B8=AD=E4=BC=98=E5=85=88=E7=BA=A7=EF=BC=9A=20local?= =?UTF-8?q?=20>=20fes=5Fenv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fes-compiler/src/service/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fes-compiler/src/service/index.js b/packages/fes-compiler/src/service/index.js index c63f17ff..f3646740 100644 --- a/packages/fes-compiler/src/service/index.js +++ b/packages/fes-compiler/src/service/index.js @@ -152,10 +152,10 @@ export default class Service extends EventEmitter { const basePath = join(this.cwd, '.env'); const localPath = `${basePath}.local`; loadDotEnv(basePath); - loadDotEnv(localPath); if (process.env.FES_ENV) { loadDotEnv(`${basePath}.${process.env.FES_ENV}`); } + loadDotEnv(localPath); } async init() { From b32f087e1234cf9ee74a527c0ce9bbaa68301f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=BA=AF?= Date: Fri, 5 Mar 2021 17:35:38 +0800 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20=20=E4=BC=98=E5=8C=96plugin-layout?= =?UTF-8?q?=EF=BC=8C=E6=8F=92=E4=BB=B6=E7=9A=84=E9=85=8D=E7=BD=AE=20>=20?= =?UTF-8?q?=20=E5=85=B3=E8=81=94=E7=9A=84=E8=B7=AF=E7=94=B1=E5=85=83?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/runtime/helpers/index.js | 46 ++++++------------- packages/fes-template/.fes.js | 8 ++-- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/packages/fes-plugin-layout/src/runtime/helpers/index.js b/packages/fes-plugin-layout/src/runtime/helpers/index.js index d21db0c0..a9795055 100644 --- a/packages/fes-plugin-layout/src/runtime/helpers/index.js +++ b/packages/fes-plugin-layout/src/runtime/helpers/index.js @@ -1,38 +1,17 @@ export const noop = () => {}; const matchName = (config, name) => { - let res; - if (Array.isArray(config)) { - for (let i = 0; i < config.length; i++) { - const item = config[i]; - if (item.meta && item.meta.name === name) { - res = item.meta; - res.path = item.path; - break; - } - if (item.children && item.children.length > 0) { - res = matchName(item.children, name); - if (res) { - break; - } - } - } - } - return res; -}; - -const matchPath = (config, path) => { let res = {}; if (Array.isArray(config)) { for (let i = 0; i < config.length; i++) { const item = config[i]; - if (item.path && item.path === path) { + if (item.meta && item.meta.name === name) { res = item.meta || {}; res.path = item.path; break; } if (item.children && item.children.length > 0) { - res = matchPath(item.children, path); + res = matchName(item.children, name); if (res) { break; } @@ -49,16 +28,21 @@ export const fillMenuData = (menuConfig, routeConfig, dep = 0) => { } const arr = []; if (Array.isArray(menuConfig) && Array.isArray(routeConfig)) { - menuConfig.forEach((item) => { - if (item.path !== undefined && item.path !== null) { - Object.assign(item, matchPath(routeConfig, item.path)); - } else { - Object.assign(item, matchName(routeConfig, item.name)); + menuConfig.forEach((menu) => { + const pageConfig = {}; + if (menu.name) { + Object.assign(pageConfig, matchName(routeConfig, menu.name)); } - if (item.children && item.children.length > 0) { - item.children = fillMenuData(item.children, routeConfig, dep); + // menu的配置优先级高,当menu存在配置时,忽略页面的配置 + Object.keys(pageConfig).forEach((prop) => { + if (menu[prop] === undefined || menu[prop] === null || menu[prop] === '') { + menu[prop] = pageConfig[prop]; + } + }); + if (menu.children && menu.children.length > 0) { + menu.children = fillMenuData(menu.children, routeConfig, dep); } - arr.push(item); + arr.push(menu); }); } return arr; diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js index c263bcf1..ab30984a 100644 --- a/packages/fes-template/.fes.js +++ b/packages/fes-template/.fes.js @@ -12,7 +12,7 @@ export default { publicPath: '/', access: { roles: { - admin: ["/"] + admin: ["/", "https://www.baidu.com"] } }, request: { @@ -31,10 +31,12 @@ export default { title: "Fes.js", footer: 'Created by MumbelFe', multiTabs: false, + navigation: 'mixin', menus: [{ - name: 'index' + name: 'index', }, { - name: 'onepiece' + name: 'onepiece', + path: 'https://www.baidu.com' }, { name: 'store' }, { From 1673ef4b7e1460e3d0a2bf9aac57eef540bba225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=BA=AF?= Date: Fri, 5 Mar 2021 18:04:10 +0800 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=20=E4=BC=98=E5=8C=96plugin-locale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fes-plugin-locale/src/runtime/core.tpl | 16 ++++++++-------- packages/fes-template/src/pages/index.vue | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/fes-plugin-locale/src/runtime/core.tpl b/packages/fes-plugin-locale/src/runtime/core.tpl index 3372a7ef..6fa1216d 100644 --- a/packages/fes-plugin-locale/src/runtime/core.tpl +++ b/packages/fes-plugin-locale/src/runtime/core.tpl @@ -52,21 +52,21 @@ const i18n = createI18n({ }); window.localStorage.setItem("fes_locale", i18n.global.locale); -const setLocale = ({ lang }) => { +const setLocale = ({ locale }) => { if (isRef(i18n.global.locale)) { - i18n.global.locale.value = lang; + i18n.global.locale.value = locale; } else { - i18n.global.locale = lang; + i18n.global.locale = locale; } - window.localStorage.setItem("fes_locale", lang); + window.localStorage.setItem("fes_locale", locale); }; -const addLocale = ({ lang, messages }) => { - messages[lang] = messages; +const addLocale = ({ locale, messages }) => { + messages[locale] = messages; if (isRef(i18n.global.messages)) { - i18n.global.messages.value[lang] = messages; + i18n.global.messages.value[locale] = messages; } else { - i18n.global.messages[lang] = messages; + i18n.global.messages[locale] = messages; } }; diff --git a/packages/fes-template/src/pages/index.vue b/packages/fes-template/src/pages/index.vue index 6a282adc..ae769050 100644 --- a/packages/fes-template/src/pages/index.vue +++ b/packages/fes-template/src/pages/index.vue @@ -78,8 +78,8 @@ export default { onMounted(() => { console.log(router); setTimeout(() => { - locale.setLocale({ lang: 'en-US' }); - locale.addLocale({ lang: 'ja-JP', messages: { test: 'テスト' } }); + locale.setLocale({ locale: 'en-US' }); + locale.addLocale({ locale: 'ja-JP', messages: { test: 'テスト' } }); console.log(locale.getAllLocales()); }, 2000); setTimeout(() => { From c367445c2f3f5d4b5abe117e229616ef4ab5db10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=BA=AF?= Date: Fri, 5 Mar 2021 18:49:13 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20plugin-locale=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=9C=80=E7=BB=88=E6=98=AF=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fes-plugin-locale/src/runtime/core.tpl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/fes-plugin-locale/src/runtime/core.tpl b/packages/fes-plugin-locale/src/runtime/core.tpl index 6fa1216d..232acc8c 100644 --- a/packages/fes-plugin-locale/src/runtime/core.tpl +++ b/packages/fes-plugin-locale/src/runtime/core.tpl @@ -35,7 +35,10 @@ const getDefaultLocale = () => { fallbackLocale: window.navigator.language, }; } - return {}; + return { + locale: 'zh-CN', + fallbackLocale: 'zh-CN', + }; }; const messages = {}; From 611d4cb3f0e642afd3308e37862498987b3d16c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=BA=AF?= Date: Fri, 5 Mar 2021 18:49:31 +0800 Subject: [PATCH 5/7] =?UTF-8?q?docs:=20=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vuepress/public/mixin.png | Bin 0 -> 33822 bytes docs/.vuepress/public/side.png | Bin 0 -> 33854 bytes docs/.vuepress/public/top.png | Bin 0 -> 33626 bytes docs/zh/guide/env.md | 2 +- docs/zh/guide/route.md | 7 +- docs/zh/reference/plugin/plugins/access.md | 84 ++++++--- docs/zh/reference/plugin/plugins/layout.md | 182 ++++++++++++++++++- docs/zh/reference/plugin/plugins/locale.md | 197 ++++++++++++++++++++- packages/fes-plugin-layout/README.md | 1 + 9 files changed, 444 insertions(+), 29 deletions(-) create mode 100644 docs/.vuepress/public/mixin.png create mode 100644 docs/.vuepress/public/side.png create mode 100644 docs/.vuepress/public/top.png diff --git a/docs/.vuepress/public/mixin.png b/docs/.vuepress/public/mixin.png new file mode 100644 index 0000000000000000000000000000000000000000..f35a3407cdc73f5672447c5e15756ce3f8055d0b GIT binary patch literal 33822 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 literal 0 HcmV?d00001 diff --git a/docs/.vuepress/public/side.png b/docs/.vuepress/public/side.png new file mode 100644 index 0000000000000000000000000000000000000000..426ccbfaebfe060b835992af4cd44d442b3ebd3c GIT binary patch literal 33854 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 T_Y6&l|8w?~^-0(Xw_E=QF^DJU literal 0 HcmV?d00001 diff --git a/docs/.vuepress/public/top.png b/docs/.vuepress/public/top.png new file mode 100644 index 0000000000000000000000000000000000000000..54500d0cf98a4bfccc85cb54879d8ed0f642243f GIT binary patch literal 33626 zcmeHwXH-*Z*S2C8m2oVHG?ifx5fD(28XI6oap)vL5D*cN5=sJO&{0%M#GzNEhzKNv z76>H40ucg4q=X&>A&@{I2_b}#C;!voj$$qN`Rk- z*KPMzs}3f(xw!m%?(o6)*RQ*{yl>M`-y9HP{qSL|wF|tZp|zo

bdPvy4<{=aD@> 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 临时配置 > .env +临时配置 > 环境配置 > .env ::: tip 如果两份配置中存在相同的项,则优先级高的会覆盖优先级低的。 diff --git a/docs/zh/guide/route.md b/docs/zh/guide/route.md index d13917c2..6cd4b49d 100644 --- a/docs/zh/guide/route.md +++ b/docs/zh/guide/route.md @@ -92,7 +92,7 @@ pages "count": 5 }, { - "path": "*", + "path": "/:pathMatch(.*)", "component": require('@/pages/*').default, "name": "FUZZYMATCH", "meta": {}, @@ -140,7 +140,7 @@ pages ``` ### 模糊匹配 -Fes.js 下约定文件名为 `*` 的路由是模糊匹配路由,可以用此特性实现 `404` 路由。 +Fes.js 下约定文件名为 `*` 的路由是模糊匹配路由,可以用此特性实现 [404 路由](https://next.router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E6%8D%95%E8%8E%B7%E6%89%80%E6%9C%89%E8%B7%AF%E7%94%B1%E6%88%96-404-not-found-%E8%B7%AF%E7%94%B1)。 比如以下目录结构: @@ -156,7 +156,7 @@ pages path: '/', component: require('@/pages/index').default, count: 5 }, { - path: '*', component: require('@/pages/**').default, count: 3 + path: '/:pathMatch(.*)', component: require('@/pages/**').default, count: 3 } ] ``` @@ -216,6 +216,7 @@ const router = new VueRouter({ 当我们跳转路由时,如果 URL 匹配到多个路由,则选择分数最高的路由。 ## 路由跳转 +想学习更多,可以查看 [Vue Router 官方文档](https://next.router.vuejs.org/zh/guide/essentials/navigation.html#%E6%9B%BF%E6%8D%A2%E5%BD%93%E5%89%8D%E4%BD%8D%E7%BD%AE)。 ### 声明式 ```vue diff --git a/docs/zh/reference/plugin/plugins/access.md b/docs/zh/reference/plugin/plugins/access.md index a4688966..58e8bed1 100644 --- a/docs/zh/reference/plugin/plugins/access.md +++ b/docs/zh/reference/plugin/plugins/access.md @@ -45,8 +45,8 @@ Fes.js 用角色定义一组资源。当访问 Fes.js 应用时,使用插件 ## 配置 -### 编译配置 -在 `.fes.js` 中配置: +### 编译时配置 +在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置: ```js export default { access: { @@ -68,35 +68,74 @@ export default { ### 运行时配置 -在 `app.js` 中配置: +在 `app.js` 中配置 + +#### unAccessHandler +- **类型**:`Function` + +- **默认值**:`null` + +- **详情**: + + 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。 +- **参数** + - router:createRouter 创建的路由实例 + - to: 准备进入的路由 + - from:离开的路由 + - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next) + +比如: ```js export const access = { - noAccessHandler({ router, to, from, next}) { - console.log("被拦截"); - next(false); + unAccessHandler({ to, next }) { + const accesssIds = accessApi.getAccess(); + if (to.path === '/404') { + accessApi.setAccess(accesssIds.concat(['/404'])); + return next('/404'); + } + if (!accesssIds.includes('/403')) { + accessApi.setAccess(accesssIds.concat(['/403'])); + } + next('/403'); } }; ``` -#### noAccessHandler -- **类型**:函数 + +#### noFoundHandler +- **类型**:`Function` -- **默认值**:null +- **默认值**:`null` - **详情**: - 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `noAccessHandler` 函数。 + 当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。 - **参数** - - router - - to - - from - - next + - router:createRouter 创建的路由实例 + - to: 准备进入的路由 + - from:离开的路由 + - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next) + +比如: +```js +export const access = { + noFoundHandler({ next }) { + const accesssIds = accessApi.getAccess(); + if (!accesssIds.includes('/404')) { + accessApi.setAccess(accesssIds.concat(['/404'])); + } + next('/404'); + } +}; + +``` ## API ### access +插件 API 通过 `@webank/fes` 导出: ```js -import { access } from '@webank/fes-plugin-access' +import { access } from '@webank/fes' ``` #### access.hasAccess @@ -107,15 +146,15 @@ import { access } from '@webank/fes-plugin-access' - accessId,资源Id - **返回值**:Boolean -#### access.hasLoading +#### access.isDataReady - **类型**:函数 -- **详情**:可以用异步数据来设置权限,`hasLoading` 用来判断异步数据是否已经加载完毕。 +- **详情**:可以用异步数据来设置权限,`isDataReady` 用来判断异步数据是否已经加载完毕。 - **参数**:null - **返回值**:Boolean ```js import { access } from '@webank/fes'; -console.log(access.hasLoading()) +console.log(access.isDataReady()) ``` @@ -145,16 +184,15 @@ import { access } from '@webank/fes'; access.setAccess(['/a', '/b', '/c']) ``` -#### access.addAccess +#### access.getAccess - **类型**:函数 -- **详情**:添加某个资源Id为可见。 -- **参数**: - - accessId,资源Id +- **详情**:返回当前可见的资源列表。 +- **参数**:null ```js import { access } from '@webank/fes'; -access.addAccess("aaa"); +access.getAccess(); ``` ### useAccess diff --git a/docs/zh/reference/plugin/plugins/layout.md b/docs/zh/reference/plugin/plugins/layout.md index 2c702865..b92851cc 100644 --- a/docs/zh/reference/plugin/plugins/layout.md +++ b/docs/zh/reference/plugin/plugins/layout.md @@ -1,8 +1,188 @@ # @webank/fes-plugin-layout +## 介绍 +为了进一步降低研发成本,我们尝试将布局通过 fes 插件的方式内置,只需通过简单的配置即可拥有布局,包括导航以及侧边栏。从而做到用户无需关心布局。 +- 侧边栏菜单数据根据路由中的配置自动生成。 +- 布局,提供 `side`、 `top`、`mixin` 三种布局。 +- 主题,提供 `light`、`dark` 两种主题。 +- 默认实现对路由的 404、403 处理。 +- 搭配 [@webank/fes-plugin-access](./access.html) 插件使用,可以完成对路由的权限控制。 +- 搭配 [@webank/fes-plugin-loacle](./locale.html) 插件使用,提供切换语言的能力。 +- 支持自定义头部区域。 + +- 可配置页面是否需要 layout。 + +## 布局类型 +默认是 `side` + +### side +![side](/side.png) +### top +![top](/top.png) +### mixin +![mixin](/mixin.png) ## 启用方式 +在 `package.json` 中引入依赖: +```json +{ + "dependencies": { + "@webank/fes": "^2.0.0", + "@webank/fes-plugin-layout": "^2.0.0" + }, +} +``` + +### 页面禁用布局 +Fes.js 渲染路由时,如果路由元信息存在配置 `layout` 为 `false`,则表示禁用此配置,用户只需要如下配置: +```vue + +{ + "layout": false +} + + +``` ## 配置 -## API \ No newline at end of file +### 编译时配置 +在 `.fes.js` 中配置: +```js +export default { + layout: { + title: "Fes.js", + footer: 'Created by MumbelFe', + multiTabs: false, + menus: [{ + name: 'index' + }, { + name: 'onepiece' + }, { + name: 'store' + }, { + name: 'simpleList' + }] + }, +``` + +#### title +- **类型**:`String` + +- **默认值**:`name` in package.json + +- **详情**:产品名,会显示在 Logo 旁边。 + +#### logo +- **类型**:`String` + +- **默认值**:默认提供 fes.js 的 Logo + +- **详情**:Logo,会显示在布局上。 + +#### locale +- **类型**:`boolean` + +- **默认值**:`false` + +- **详情**:是否显示语言选择框。 + +#### multiTabs +- **类型**:`boolean` + +- **默认值**:`false` + +- **详情**:是否开启多页。 + +#### menus +- **类型**:`Array` + +- **默认值**:`[]` + +- **详情**:菜单配置,子项具体配置如下: + + - **name**:菜单的名称。通过匹配 `name` 和路由元信息 [meta](http://localhost:8080/zh/guide/route.html#%E6%89%A9%E5%B1%95%E8%B7%AF%E7%94%B1%E5%85%83%E4%BF%A1%E6%81%AF) 中的 `name`,把菜单和路由关联起来,然后使用路由元信息补充菜单配置,比如 `title`、`path` 等。 + + - **path**:菜单的路径,可配置第三方地址。 + + - **title**:菜单的标题。 + + - **children**:子菜单配置。 + + +### 运行时配置 +在 `app.js` 中配置: +```js +import UserCenter from '@/components/UserCenter'; +export const layout = { + customHeader: +}; + +``` +#### customHeader +- **类型**:Vue Component + +- **默认值**:`null` + +- **详情**:布局的 Header 部位提供组件自定义功能。 + +#### unAccessHandler +- **类型**:`Function` + +- **默认值**:`null` + +- **详情**: + + 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。 +- **参数** + - router:createRouter 创建的路由实例 + - to: 准备进入的路由 + - from:离开的路由 + - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next) + +比如: +```js +export const access = { + unAccessHandler({ to, next }) { + const accesssIds = accessApi.getAccess(); + if (to.path === '/404') { + accessApi.setAccess(accesssIds.concat(['/404'])); + return next('/404'); + } + if (!accesssIds.includes('/403')) { + accessApi.setAccess(accesssIds.concat(['/403'])); + } + next('/403'); + } +}; + +``` + +#### noFoundHandler +- **类型**:函数 + +- **默认值**:null + +- **详情**: + + 当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。 +- **参数** + - router:createRouter 创建的路由实例 + - to: 准备进入的路由 + - from:离开的路由 + - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next) + +比如: +```js +export const access = { + noFoundHandler({ next }) { + const accesssIds = accessApi.getAccess(); + if (!accesssIds.includes('/404')) { + accessApi.setAccess(accesssIds.concat(['/404'])); + } + next('/404'); + } +}; + +``` \ No newline at end of file diff --git a/docs/zh/reference/plugin/plugins/locale.md b/docs/zh/reference/plugin/plugins/locale.md index 7e6c396d..4b9fa6af 100644 --- a/docs/zh/reference/plugin/plugins/locale.md +++ b/docs/zh/reference/plugin/plugins/locale.md @@ -1,7 +1,202 @@ # @webank/fes-plugin-locale +## 介绍 +国际化插件,基于 [Vue I18n](https://github.com/intlify/vue-i18n-next),用于解决 i18n 问题。 ## 启用方式 +在 `package.json` 中引入依赖: +```json +{ + "dependencies": { + "@webank/fes": "^2.0.0", + "@webank/fes-plugin-locale": "^2.0.0" + }, +} +``` + ## 配置 -## API \ No newline at end of file +### 约定式配置 +Fes.js 约定如下目录,项目就拥有了 `zh-CN` 与 `en-US` 国际化语言切换: +``` +src + ├── locales + │ ├── zh-CN.js + │ └── en-US.js + └── pages + │ └── index.vue + └── app.js +``` +多语言文件的命名规范:`-.js` + +多语言文件的内容规范:键值组成的字面量,如下: +```js +// src/locales/zh-CN.js +export default { + menu: { + interface: '接口' + }, + overview: '概述', + i18n: { + internationalization: '国际化,基于', + achieve: '实现。', + ui: 'UI组件' + } +}; +``` +```js +// src/locales/zh-CN.js +export default { + menu: { + interface: 'interface' + }, + overview: 'Overview', + i18n: { + internationalization: 'internationalization,base on', + achieve: 'to achieve.', + ui: 'UI components' + } +}; +``` +想了解更多语言信息配置、匹配规则,请参考 [Vue I18n](https://vue-i18n.intlify.dev/guide/essentials/syntax.html) 文档。 + + +### 编译时配置 +在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置: +```js +export default { + locale: { + } +} +``` +默认配置为: +```js +export default { + locale: { + locale: 'zh-CN', // default locale + fallbackLocale: 'zh-CN', // set fallback locale + baseNavigator: true, // 开启浏览器语言检测 + share: true, // 用户是否需要手动改变语言 + } +} +``` +所有配置项如下: + +#### locale +- **类型**:`String` + +- **默认值**:`zh-CN` + +- **详情**:当前的语言。 + +#### fallbackLocale +- **类型**:`String` + +- **默认值**:`zh-CN` + +- **详情**:兜底的语言,如果当前语言找不到配置,则使用默认语言,需要保证默认语言配置文件存在。 + +#### baseNavigator +- **类型**:`Boolean` + +- **默认值**:`true` + +- **详情**:开启浏览器语言检测。 + +默认情况下,当前语言环境的识别按照:`localStorage` 中 `fes_locale` 值 > 浏览器检测 > `default` 设置的默认语言 > `zh-CN` 中文。 + +#### share +- **类型**:`Boolean` + +- **默认值**:`true` + +- **详情**:是否共享API,共享语言选择器 `{ SelectLang } `,其他插件可以获取到共享内容。 + +比如: +```js +import { plugin } from "@@/core/coreExports"; +const localeShared = plugin.getShared("locale"); +``` + + +### 运行时配置 +暂无。 + +## API + +### locale +插件 API 通过 `@webank/fes` 导出: +```js +import { locale } from '@webank/fes' +``` + +#### locale.messages +- **类型**:`Object` + +- **详情**:当前的配置的语言信息。 + +#### locale.setLocale +- **类型**:`Function` + +- **详情**:设置当前的语言。 +- **参数**: + - locale,语言的名称,应该是符合 `-` 规范的名称。 +- **返回值**:`null` +```js +import { locale } from '@webank/fes'; +locale.setLocale({ locale: 'en-US' }); +``` + +#### locale.addLocale +- **类型**:`Function` + +- **详情**:手动添加语言配置。 +- **参数**: + - locale,语言的名称,符合 `-` 规范的名称。 + - messages, 语言信息。 +- **返回值**:`null` +```js +import { locale } from '@webank/fes' +locale.addLocale({ locale: 'ja-JP', messages: { test: 'テスト' } }); +``` + + +#### locale.getAllLocales +- **类型**:`Function` + +- **详情**:获取当前获得所有国际化文件的列表,默认会在 locales 文件夹下寻找类似 `en-US.js` 文件。 +- **参数**:null +- **返回值**:`Array` +```js +import { locale } from '@webank/fes'; +console.log(locale.getAllLocales()); +// ["en-US", "id-ID", "ja-JP", "pt-BR", "zh-CN", "zh-TW"] +``` + + +### useI18n +Composition API, 只能在 `setup` 函数中使用,更多细节参考 [Vue I18n](https://vue-i18n.intlify.dev/api/composition.html#usei18n)。 +举个例子: +```vue + + + +``` + +`useI18n()`返回结果是 [Composer](https://vue-i18n.intlify.dev/api/composition.html#composer),提供类似 `t`、`n`、`d` 等转换函数,在模板中使用。 \ No newline at end of file diff --git a/packages/fes-plugin-layout/README.md b/packages/fes-plugin-layout/README.md index 5bcd3b30..3dd57ad4 100644 --- a/packages/fes-plugin-layout/README.md +++ b/packages/fes-plugin-layout/README.md @@ -5,6 +5,7 @@ multi tabs: 是/否 ## todo-list +1. 菜单的国际化 ### theme 1. 主题light-白色 From 8fed154ae60e1aef5a39882ecf8a56739bda696b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=BA=AF?= Date: Fri, 5 Mar 2021 18:58:14 +0800 Subject: [PATCH 6/7] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E5=8C=85?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh/README.md | 6 +++--- docs/zh/guide/config.md | 2 +- docs/zh/guide/contributing.md | 18 +++++++++--------- docs/zh/guide/directory-structure.md | 4 ++-- docs/zh/guide/getting-started.md | 8 ++++---- docs/zh/guide/plugin.md | 6 +++--- docs/zh/guide/route.md | 2 +- docs/zh/reference/api/README.md | 14 +++++++------- docs/zh/reference/cli/README.md | 8 ++++---- docs/zh/reference/config/README.md | 2 +- docs/zh/reference/plugin/plugins/access.md | 20 ++++++++++---------- docs/zh/reference/plugin/plugins/enums.md | 10 +++++----- docs/zh/reference/plugin/plugins/icon.md | 6 +++--- docs/zh/reference/plugin/plugins/jest.md | 2 +- docs/zh/reference/plugin/plugins/layout.md | 10 +++++----- docs/zh/reference/plugin/plugins/locale.md | 18 +++++++++--------- docs/zh/reference/plugin/plugins/model.md | 2 +- docs/zh/reference/plugin/plugins/request.md | 14 +++++++------- docs/zh/reference/plugin/plugins/vuex.md | 8 ++++---- 19 files changed, 80 insertions(+), 80 deletions(-) diff --git a/docs/zh/README.md b/docs/zh/README.md index 06abda8a..4ce085d6 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -30,7 +30,7 @@ footer: MIT Licensed | Copyright © 2020-present Webank ```bash # 创建模板 -yarn create @webank/fes-app myapp +yarn create @fesjs/fes-app myapp # 安装依赖 yarn @@ -45,7 +45,7 @@ yarn dev ```bash # 创建模板 -npx @webank/create-fes-app myapp +npx @fesjs/create-fes-app myapp # 安装依赖 npm install @@ -61,5 +61,5 @@ npm run dev | Github Issue | 微信群 | Fes.js开源运营小助手 | | --- | --- | --- | -| [@webank/fes.js/issues](https://github.com/WeBankFinTech/fes.js/issues) | | | +| [@fesjs/fes.js/issues](https://github.com/WeBankFinTech/fes.js/issues) | | | diff --git a/docs/zh/guide/config.md b/docs/zh/guide/config.md index 69a031b1..206bc963 100644 --- a/docs/zh/guide/config.md +++ b/docs/zh/guide/config.md @@ -1,6 +1,6 @@ # 配置 -`fes` 在 `.fes.js` 文件中添加项目基础配置。一份常见的配置示例如下: +Fes.js 约定 `.fes.js` 文件为项目基础配置文件,一份常见的配置示例如下: ```js export default { base: '/foo/', diff --git a/docs/zh/guide/contributing.md b/docs/zh/guide/contributing.md index c4428fdc..b17a1746 100644 --- a/docs/zh/guide/contributing.md +++ b/docs/zh/guide/contributing.md @@ -4,23 +4,23 @@ 项目仓库借助于 [Yarn Classic 工作区](https://classic.yarnpkg.com/zh-Hans/docs/workspaces) 来实现 [Monorepo](https://en.wikipedia.org/wiki/Monorepo) ,在 `packages` 目录下存放了多个互相关联的独立 Package 。 -- `@webank/create-fes-app`: 创建项目模板模块。提供`create-fes-app`命令,提供创建多种类型项目模板的能力。 +- `@fesjs/create-fes-app`: 创建项目模板模块。提供`create-fes-app`命令,提供创建多种类型项目模板的能力。 -- `@webank/fes`: 入口模块。提供`fes`命令和 API 入口。 +- `@fesjs/fes`: 入口模块。提供`fes`命令和 API 入口。 -- `@webank/fes-compiler`: 编译时插件管理模块。定义插件的生命周期、插件配置、插件通讯机制等。 +- `@fesjs/compiler`: 编译时插件管理模块。定义插件的生命周期、插件配置、插件通讯机制等。 -- `@webank/fes-runtime`: 运行时插件模块。集成了vue-router,定义运行时插件生命周期、插件通讯机制。 +- `@fesjs/runtime`: 运行时插件模块。集成了vue-router,定义运行时插件生命周期、插件通讯机制。 -- `@webank/fes-preset-build-in`: 内置插件集。包含`dev`、`build`等命令,集成webpack5+babel,提供方便编写插件的API,入口文件处理,路由处理等能力。 +- `@fesjs/preset-build-in`: 内置插件集。包含`dev`、`build`等命令,集成webpack5+babel,提供方便编写插件的API,入口文件处理,路由处理等能力。 -- `@webank/fes-template`: 适用于PC类型的模板项目。 +- `@fesjs/fes-template`: 适用于PC类型的模板项目。 -- `@webank/fes-template-h5`: 适用于H5类型的模板项目。 +- `@fesjs/fes-template-h5`: 适用于H5类型的模板项目。 -- `@webank/fes-plugin-${name}`: 官方插件。 +- `@fesjs/plugin-${name}`: 官方插件。 -- `@webank/fes`: 是 `@webank/compiler` + `@webank/fes-runtime` + `@webank/fes-preset-build-in` 的封装。用户只需要安装此依赖和额外的插件或者插件集。 +- `@fesjs/fes`: 是 `@fesjs/compiler` + `@fesjs/runtime` + `@fesjs/preset-build-in` 的封装。用户只需要安装此依赖和额外的插件或者插件集。 ## 开发配置 diff --git a/docs/zh/guide/directory-structure.md b/docs/zh/guide/directory-structure.md index 2623ce60..321ef685 100644 --- a/docs/zh/guide/directory-structure.md +++ b/docs/zh/guide/directory-structure.md @@ -21,10 +21,10 @@ fes-template ### 根目录 #### package.json -包含插件和插件集,以 `@webank/fes-preset-`、`@webank/fes-plugin-`、`fes-preset-` 和 `fes-plugin-` 开头的依赖会被自动注册为插件或插件集。 +包含插件和插件集,以 `@fesjs/preset-`、`@fesjs/plugin-`、`fes-preset-` 和 `fes-plugin-` 开头的依赖会被自动注册为插件或插件集。 #### tsconfig.json -解决 `@webank/fes` 和使用 `@` 的 API 提示 +解决 `@fesjs/fes` 和使用 `@` 的 API 提示 #### .fes.js 配置文件,包含 Fes.js 内置功能和插件的配置。 diff --git a/docs/zh/guide/getting-started.md b/docs/zh/guide/getting-started.md index 726500c0..cfa0cdc0 100644 --- a/docs/zh/guide/getting-started.md +++ b/docs/zh/guide/getting-started.md @@ -37,7 +37,7 @@ cd workspace ```bash # 创建模板 -yarn create @webank/fes-app myapp +yarn create @fesjs/fes-app myapp ``` @@ -46,7 +46,7 @@ yarn create @webank/fes-app myapp ```bash # 创建模板 -npx @webank/create-fes-app myapp +npx @fesjs/create-fes-app myapp ``` @@ -112,7 +112,7 @@ Starting the development server http://localhost:8080 ... npm run dev -> @webank/fes-template@2.0.0-alpha.1 dev /Users/harrywan/company/git/fes.js/packages/fes-template +> @fesjs/fes-template@2.0.0-alpha.1 dev /Users/harrywan/company/git/fes.js/packages/fes-template > fes dev Starting the development server http://localhost:8080 ... @@ -159,7 +159,7 @@ $ fes build # 构建 npm run build -> @webank/fes-template@2.0.0-alpha.1 build /Users/harrywan/company/git/fes.js/packages/fes-template +> @fesjs/fes-template@2.0.0-alpha.1 build /Users/harrywan/company/git/fes.js/packages/fes-template > fes build ✔ Webpack diff --git a/docs/zh/guide/plugin.md b/docs/zh/guide/plugin.md index c6122041..b81a3570 100644 --- a/docs/zh/guide/plugin.md +++ b/docs/zh/guide/plugin.md @@ -3,7 +3,7 @@ ## 插件的 id 和 key 每个插件都会对应一个 `id` 和一个 `key`,**`id` 是路径的简写,`key` 是进一步简化后用于配置的唯一值**。 -比如插件 `/node_modules/@webank/fes-plugin-foo/index.js`,通常来说,其 `id` 为 `@webank/fes-plugin-foo`,`key` 为 `foo`。 +比如插件 `/node_modules/@fesjs/plugin-foo/index.js`,通常来说,其 `id` 为 `@fesjs/plugin-foo`,`key` 为 `foo`。 ::: tip id 一般用不上,对于普通开发者 key 用来配置插件,而插件开发者可以使用 key 判断是否安装某个插件。 @@ -17,11 +17,11 @@ Fes.js 会自动检测 `dependencies` 和 `devDependencies` 里的 fes 插件, ```json { "dependencies": { - "@webank/fes-plugin-request": "^2.0.0" + "@fesjs/plugin-request": "^2.0.0" } } ``` -那么 `@webank/fes-plugin-request` 会自动被注册,无需在配置里重复声明。 +那么 `@fesjs/plugin-request` 会自动被注册,无需在配置里重复声明。 ### 配置 在配置里可通过 `presets` 和 `plugins` 配置插件,比如: diff --git a/docs/zh/guide/route.md b/docs/zh/guide/route.md index 6cd4b49d..d684ca77 100644 --- a/docs/zh/guide/route.md +++ b/docs/zh/guide/route.md @@ -229,7 +229,7 @@ const router = new VueRouter({ 页面跳转 API 由 `router` 实例提供,查看 [Vue Rouer 文档](https://next.router.vuejs.org/zh/api/#router-%E6%96%B9%E6%B3%95)了解更多。 ```js -import { useRouter } from '@webank/fes'; +import { useRouter } from '@fesjs/fes'; export default { setup(){ diff --git a/docs/zh/reference/api/README.md b/docs/zh/reference/api/README.md index c4e6000f..10c76697 100644 --- a/docs/zh/reference/api/README.md +++ b/docs/zh/reference/api/README.md @@ -3,9 +3,9 @@ sidebar: auto --- # API -Fes.js 统一了API的出口,所有运行时API(包含Fes.js内置API和插件提供的API)全部通过`@webank/fes`导出。 +Fes.js 统一了API的出口,所有运行时API(包含Fes.js内置API和插件提供的API)全部通过`@fesjs/fes`导出。 ```js -import { someApi } from "@webank/fes" +import { someApi } from "@fesjs/fes" ``` ## 基础API @@ -73,7 +73,7 @@ Fes.js 路由基于 [Vue Router 4.0](https://next.router.vuejs.org/introduction. ### useRoute 返回当前 `route` 实例,相当于在模板内使用 `$route`。必须在 `setup` 函数内调用。 ```js -import { useRoute } from "@webank/fes"; +import { useRoute } from "@fesjs/fes"; export default { setup(){ const route = useRoute() @@ -84,7 +84,7 @@ export default { ### useRouter 返回 `router` 实例,相当于在模板语法中使用 `$router`。必须在 `setup` 函数内调用。 ```js -import { useRouter } from "@webank/fes"; +import { useRouter } from "@fesjs/fes"; export default { setup(){ const router = useRouter() @@ -95,7 +95,7 @@ export default { ### onBeforeRouteUpdate 添加导航守卫,在当前路由即将更新时触发。类似于之前的`beforeRouteUpdate`,但是可用于任何组件。卸载组件时,将移除守卫。 ```js -import { onBeforeRouteUpdate } from "@webank/fes"; +import { onBeforeRouteUpdate } from "@fesjs/fes"; export default { setup(){ onBeforeRouteUpdate((to, from, next)=>{ @@ -106,7 +106,7 @@ export default { ### onBeforeRouteLeave 添加导航守卫,在当前路由即将离开时触发。类似于之前的`beforeRouteLeave`,但可用于任何组件。卸载组件时,将移除守卫。 ```js -import { onBeforeRouteLeave } from "@webank/fes"; +import { onBeforeRouteLeave } from "@fesjs/fes"; export default { setup(){ onBeforeRouteLeave((to, from, next)=>{ @@ -146,7 +146,7 @@ export default { ### useLink 返回的结果跟 RouterLink 的作用域插槽的属性一致,查看[官方API](https://next.router.vuejs.org/api/#router-link-s-v-slot)了解更多。 ```js -import { RouterLink, useLink } from '@webank/fes' +import { RouterLink, useLink } from '@fesjs/fes' export default { name: 'AppLink', diff --git a/docs/zh/reference/cli/README.md b/docs/zh/reference/cli/README.md index fc9a9d50..261a435d 100644 --- a/docs/zh/reference/cli/README.md +++ b/docs/zh/reference/cli/README.md @@ -22,7 +22,7 @@ Options: ```bash # 全局安装 -yarn global add @webank/create-fes-app +yarn global add @fesjs/create-fes-app # 创建模板 create-fes-app fes-app @@ -34,7 +34,7 @@ create-fes-app fes-app ```bash # 全局安装 -npm i -g @webank/create-fes-app +npm i -g @fesjs/create-fes-app # 创建模板 create-fes-app fes-app @@ -50,7 +50,7 @@ create-fes-app fes-app ```bash # 创建模板 -yarn create @webank/fes-app myapp +yarn create @fesjs/fes-app myapp # 安装依赖 yarn @@ -65,7 +65,7 @@ yarn dev ```bash # 创建模板 -npx @webank/create-fes-app myapp +npx @fesjs/create-fes-app myapp # 安装依赖 npm install diff --git a/docs/zh/reference/config/README.md b/docs/zh/reference/config/README.md index 45b53853..ecb61f97 100644 --- a/docs/zh/reference/config/README.md +++ b/docs/zh/reference/config/README.md @@ -221,7 +221,7 @@ export default { - 默认值: `false` - 详情: - 配置是否启用单数模式的目录。 比如 `src/pages` 的约定在开启后为 `src/page` 目录,@webank/fes-plugins 插件也遵照此配置的约定。 + 配置是否启用单数模式的目录。 比如 `src/pages` 的约定在开启后为 `src/page` 目录,@fesjs/fes-plugins 插件也遵照此配置的约定。 ## targets - 类型: `object` diff --git a/docs/zh/reference/plugin/plugins/access.md b/docs/zh/reference/plugin/plugins/access.md index 58e8bed1..be8bf179 100644 --- a/docs/zh/reference/plugin/plugins/access.md +++ b/docs/zh/reference/plugin/plugins/access.md @@ -1,4 +1,4 @@ -# @webank/fes-plugin-access +# @fesjs/plugin-access @@ -37,8 +37,8 @@ Fes.js 用角色定义一组资源。当访问 Fes.js 应用时,使用插件 ```json { "dependencies": { - "@webank/fes": "^2.0.0", - "@webank/fes-plugin-access": "^2.0.0" + "@fesjs/fes": "^2.0.0", + "@fesjs/plugin-access": "^2.0.0" }, } ``` @@ -133,9 +133,9 @@ export const access = { ## API ### access -插件 API 通过 `@webank/fes` 导出: +插件 API 通过 `@fesjs/fes` 导出: ```js -import { access } from '@webank/fes' +import { access } from '@fesjs/fes' ``` #### access.hasAccess @@ -153,7 +153,7 @@ import { access } from '@webank/fes' - **参数**:null - **返回值**:Boolean ```js -import { access } from '@webank/fes'; +import { access } from '@fesjs/fes'; console.log(access.isDataReady()) ``` @@ -167,7 +167,7 @@ console.log(access.isDataReady()) - String,对应着 `roles` 配置对象中的 `key`。 - Promise,Promise resolve 的结果应对应着 `roles` 配置对象中的 `key`。 ```js -import { access } from '@webank/fes'; +import { access } from '@fesjs/fes'; access.setRole(['admin']) ``` @@ -180,7 +180,7 @@ access.setRole(['admin']) - Array,数组项对应着 `roles` 配置对象中的 `key`。 - Promise,Promise resolve 的结果应该是`Array`。 ```js -import { access } from '@webank/fes'; +import { access } from '@fesjs/fes'; access.setAccess(['/a', '/b', '/c']) ``` @@ -191,7 +191,7 @@ access.setAccess(['/a', '/b', '/c']) - **参数**:null ```js -import { access } from '@webank/fes'; +import { access } from '@fesjs/fes'; access.getAccess(); ``` @@ -208,7 +208,7 @@ access.getAccess();

- - -``` - -**Output** - -_Hello, {{ msg }}_ - - - -_Current count is: {{ count }}_ - - - - - - - - diff --git a/docs/guide/advanced/plugin.md b/docs/guide/advanced/plugin.md deleted file mode 100644 index d0251ed7..00000000 --- a/docs/guide/advanced/plugin.md +++ /dev/null @@ -1,3 +0,0 @@ -# Writing a Plugin - -> TODO diff --git a/docs/guide/advanced/theme.md b/docs/guide/advanced/theme.md deleted file mode 100644 index 89476481..00000000 --- a/docs/guide/advanced/theme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Writing a Theme - -> TODO diff --git a/docs/guide/assets.md b/docs/guide/assets.md deleted file mode 100644 index 5cd1b8b3..00000000 --- a/docs/guide/assets.md +++ /dev/null @@ -1,104 +0,0 @@ -# Assets - -## Relative URLs - -You can reference any assets using relative URLs in your Markdown content: - -```md -![An image](./image.png) -``` - -This is generally the suggested way to import images, as users usually place images near the Markdown file that references them. - -## Public Files - -You can put some static assets inside public directory, and they will be copied to the root of the generated directory. - -The default public directory is `.vuepress/public`, which can be changed in config. - -It would be useful in some cases: - -- You may need to provide static assets that are not directly referenced in any of your Markdown files, for example, favicon and PWA icons. -- You may need to serve some shared static assets, which may even be referenced outside your site, for example, logo images. -- You may want to reference images using absolute URLs in your Markdown content. - -Take our documentation source files as an example, we are putting the logo of VuePress inside the public directory: - -```bash -└─ docs - ├─ .vuepress - | └─ public - | └─ hero.png # <- Logo file - └─ guide - └─ assets.md # <- Here we are -``` - -We can reference our logo in current page like this: - -**Input** - -```md -![VuePress Logo](/hero.png) -``` - -**Output** - -![VuePress Logo](/hero.png) - -::: tip -Config reference: [public](../reference/config.md#public) -::: - -### Base Helper - -If your site is deployed to a non-root URL, i.e. the [base](../reference/config.md#base) is not `"/"`, you will need to prepend the `base` to the absolute URLs of your public files. - -For example, if you plan to deploy your site to `https://foo.github.io/bar/`, then `base` should be set to `"/bar/"`, and you have to reference your public files in Markdown like this: - -```md -![VuePress Logo](/bar/hero.png) -``` - -Obviously, it is brittle if you ever decide to change the `base`. This is the reason why we suggest to reference static assets using relative URLs. - -To help with that, VuePress provides a built-in helper `$withBase` that generates the correct path: - -```md -VuePress Logo -``` - -The helper is verbose in Markdown. So it might be more helpful for theme and plugin authors. - -::: tip -Config reference: [base](../reference/config.md#base) -::: - -## Packages and Path Aliases - -Although it is not a common usage, you can reference images from dependent packages: - -```bash -npm install -D package-name -``` - -```md -![Image from dependency](package-name/image.png) -``` - -The path aliases that set in config file are also supported: - -```js -module.exports = { - alias: { - '@alias': '/path/to/some/dir', - }, -} -``` - -```md -![Image from path alias](@alias/image.png) -``` - -::: tip -Config reference: [alias](../reference/config.md#alias) -::: diff --git a/docs/guide/bundler.md b/docs/guide/bundler.md deleted file mode 100644 index 82ea1f1c..00000000 --- a/docs/guide/bundler.md +++ /dev/null @@ -1,3 +0,0 @@ -# Bundler - -> TODO diff --git a/docs/guide/config.md b/docs/guide/config.md new file mode 100644 index 00000000..206bc963 --- /dev/null +++ b/docs/guide/config.md @@ -0,0 +1,79 @@ +# 配置 + +Fes.js 约定 `.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/guide/configuration.md b/docs/guide/configuration.md deleted file mode 100644 index 0aaf3f66..00000000 --- a/docs/guide/configuration.md +++ /dev/null @@ -1,84 +0,0 @@ -# Configuration - -## Config File - -Without any configuration, the VuePress site is pretty minimal. To customize your site, let’s first create a `.vuepress` directory inside your docs directory. This is where all VuePress-specific files will be placed. Your project structure is probably like this: - -``` -├─ docs -│ ├─ .vuepress -│ │ └─ config.js -│ └─ README.md -├─ .gitignore -└─ package.json -``` - -The essential file for configuring a VuePress site is `.vuepress/config.js`, which should export a JavaScript object. If you are using TypeScript, you can use `.vuepress/config.ts` instead to get better types hint for VuePress Config. - - - - -```js -module.exports = { - lang: 'en-US', - title: 'Hello, VuePress!', - description: 'This is my first VuePress site', - - themeConfig: { - logo: 'https://vuejs.org/images/logo.png', - }, -} -``` - - - - - -```ts -import type { UserConfig, DefaultThemeOptions } from 'vuepress' - -const config: UserConfig = { - lang: 'en-US', - title: 'Hello VuePress', - description: 'Just playing around', - - themeConfig: { - logo: 'https://vuejs.org/images/logo.png', - }, -} - -export = config -``` - - - - -::: tip -We will refer the config object as **VuePress Config**. -::: - -## Config Scopes - -You may have noticed that there is a `themeConfig` option in VuePress Config. - -Options outside `themeConfig` are **Site Config**, while options inside `themeConfig` are **Theme Config**. - -### Site Config - -Site config means that, no matter what theme you are using, these configurations are always valid. - -As we know, every site should have its own `lang`, `title`, `description`, etc. Thus, VuePress has built-in support for those options. - -::: tip -Check out the [Config Reference](../reference/config.md) for a full list of site config. -::: - -### Theme Config - -Theme config will be processed by VuePress theme, so it depends on the theme you are using. - -If you don't specify the `theme` option of VuePress Config, the default theme will be used. - -::: tip -Check out the [Default Theme > Config Reference](../reference/default-theme/config.md) for theme config of default theme. -::: diff --git a/docs/guide/contributing.md b/docs/guide/contributing.md new file mode 100644 index 00000000..b17a1746 --- /dev/null +++ b/docs/guide/contributing.md @@ -0,0 +1,70 @@ +# 贡献指南 + +## 概览 + +项目仓库借助于 [Yarn Classic 工作区](https://classic.yarnpkg.com/zh-Hans/docs/workspaces) 来实现 [Monorepo](https://en.wikipedia.org/wiki/Monorepo) ,在 `packages` 目录下存放了多个互相关联的独立 Package 。 + +- `@fesjs/create-fes-app`: 创建项目模板模块。提供`create-fes-app`命令,提供创建多种类型项目模板的能力。 + +- `@fesjs/fes`: 入口模块。提供`fes`命令和 API 入口。 + +- `@fesjs/compiler`: 编译时插件管理模块。定义插件的生命周期、插件配置、插件通讯机制等。 + +- `@fesjs/runtime`: 运行时插件模块。集成了vue-router,定义运行时插件生命周期、插件通讯机制。 + +- `@fesjs/preset-build-in`: 内置插件集。包含`dev`、`build`等命令,集成webpack5+babel,提供方便编写插件的API,入口文件处理,路由处理等能力。 + +- `@fesjs/fes-template`: 适用于PC类型的模板项目。 + +- `@fesjs/fes-template-h5`: 适用于H5类型的模板项目。 + +- `@fesjs/plugin-${name}`: 官方插件。 + +- `@fesjs/fes`: 是 `@fesjs/compiler` + `@fesjs/runtime` + `@fesjs/preset-build-in` 的封装。用户只需要安装此依赖和额外的插件或者插件集。 + +## 开发配置 + +开发要求: + +- [Node.js](http://nodejs.org) **version 12+** +- [Yarn v1 classic](https://classic.yarnpkg.com/zh-Hans/docs/install) + +克隆代码仓库,并安装依赖: + +```bash +yarn +``` + +监听源文件修改: + +```bash +yarn build +``` + +打开另一个终端,开始开发项目文档网站: + +```bash +yarn docs:dev +``` + +本项目开发使用的一些主要工具: + +- [Jest](https://jestjs.io/) 用于单元测试 +- [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) 用于代码检查和格式化 +- [@umi/father](https://github.com/umijs/father) 用于将ES6语法编译成ES5或者CommonJS + +## 开发脚本 + +### `yarn build` + +`build` 命令会使用 `father-build` 将 ES6 编译为 CommonJS。 + +本项目在编写Node端的代码时也用ES6,所以你在克隆代码仓库后,可能需要先执行该命令来确保项目代码可以顺利运行,因为编译后的 JS 文件被 `.gitignore` 排除在仓库以外了。 +### `yarn docs:dev` +`docs:` 前缀表明,这些命令是针对文档 (documentation) 进行操作的,即 `docs` 目录。 +使用 Vue Press在本地启动文档网站服务器,用于实时查看文档效果。 + +### 调试功能 +在开发完插件代码后,需要在template项目中验证功能 +- 进入`packages/template`目录 +- 执行`yarn dev` \ No newline at end of file diff --git a/docs/guide/deployment.md b/docs/guide/deployment.md deleted file mode 100644 index 5906a27c..00000000 --- a/docs/guide/deployment.md +++ /dev/null @@ -1,205 +0,0 @@ -# Deployment - -The following guides are based on some shared assumptions: - -- You are placing your Markdown source files inside the `docs` directory of your project; -- You are using the default build output location (`.vuepress/dist`); -- You are using [yarn classic](https://classic.yarnpkg.com/en/) as package manager, while npm is also supported; -- VuePress is installed as a local dependency in your project, and you have setup the following script in `package.json`: - -```json -{ - "scripts": { - "docs:build": "vuepress build docs" - } -} -``` - -## GitHub Pages - -1. Set the correct [base](../reference/config.md#base) config. - - If you are deploying to `https://.github.io/`, you can omit this step as `base` defaults to `"/"`. - - If you are deploying to `https://.github.io//`, for example your repository is at `https://github.com//`, then set `base` to `"//"`. - -2. Choose your preferred CI tools. Here we take [GitHub Actions](https://github.com/features/actions) as an example. - - Create `.github/workflows/docs.yml` to set up the workflow. - -::: details Click to expand sample config -```yaml -name: docs - -on: - # trigger deployment on every push to main branch - push: - branches: [main] - # trigger deployment manually - workflow_dispatch: - -jobs: - docs: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - # fetch all commits to get last updated time or other git log info - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v1 - with: - # choose node.js version to use - node-version: '14' - - # cache node_modules - - name: Cache dependencies - uses: actions/cache@v2 - id: yarn-cache - with: - path: | - **/node_modules - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - # install dependencies if the cache did not hit - - name: Install dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile - - # run build script - - name: Build VuePress site - run: yarn docs:build - - # please check out the docs of the workflow for more details - # @see https://github.com/crazy-max/ghaction-github-pages - - name: Deploy to GitHub Pages - uses: crazy-max/ghaction-github-pages@v2 - with: - # deploy to gh-pages branch - target_branch: gh-pages - # deploy the default output dir of VuePress - build_dir: docs/.vuepress/dist -``` -::: - -::: tip -Please refer to [GitHub Pages official guide](https://pages.github.com/) for more details. -::: - -## GitLab Pages - -1. Set the correct [base](../reference/config.md#base) config. - - If you are deploying to `https://.gitlab.io/`, you can omit `base` as it defaults to `"/"`. - - If you are deploying to `https://.gitlab.io//`, for example your repository is at `https://gitlab.com//`, then set `base` to `"//"`. - -2. Create `.gitlab-ci.yml` to set up [GitLab CI](https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/) workflow. - -::: details Click to expand sample config -```yaml -# choose a docker image to use -image: node:14-buster - -pages: - # trigger deployment on every push to main branch - only: - - main - - # cache node_modules - cache: - paths: - - node_modules/ - - # install dependencies and run build script - script: - - yarn --frozen-lockfile - - yarn docs:build --dest public - - artifacts: - paths: - - public -``` -::: - -::: tip -Please refer to [GitLab Pages official guide](https://docs.gitlab.com/ce/user/project/pages/#getting-started) for more details. -::: - -## Google Firebase - -1. Make sure you have [firebase-tools](https://www.npmjs.com/package/firebase-tools) installed. - -2. Create `firebase.json` and `.firebaserc` at the root of your project with the following content: - -`firebase.json`: - -```json -{ - "hosting": { - "public": "./docs/.vuepress/dist", - "ignore": [] - } -} -``` - -`.firebaserc`: - -```json -{ - "projects": { - "default": "" - } -} -``` - -3. After running `yarn docs:build`, deploy using the command `firebase deploy`. - -::: tip -Please refer to [Firebase CLI official guide](https://firebase.google.com/docs/cli) for more details. -::: - -## Heroku - -1. Install [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). - -2. Create a Heroku account by [signing up](https://signup.heroku.com). - -3. Run `heroku login` and fill in your Heroku credentials: - -```bash -heroku login -``` - -4. Create a file called `static.json` in the root of your project with the below content: - -`static.json`: - -```json -{ - "root": "./docs/.vuepress/dist" -} -``` - -This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). - -## Netlify - -1. On [Netlify](https://netlify.com), set up a new project from GitHub with the following settings: - - - **Build Command:** `yarn docs:build` - - **Publish directory:** `docs/.vuepress/dist` - -2. Set [Environment variables](https://docs.netlify.com/configure-builds/environment-variables) to choose node version: - - - `NODE_VERSION`: 14 - -3. Hit the deploy button. - -## Vercel - -See [Creating and Deploying a VuePress App with Vercel](https://vercel.com/guides/deploying-vuepress-to-vercel). diff --git a/docs/guide/directory-structure.md b/docs/guide/directory-structure.md new file mode 100644 index 00000000..321ef685 --- /dev/null +++ b/docs/guide/directory-structure.md @@ -0,0 +1,67 @@ +# 目录结构 + +在[快速上手](./getting-started.html)中,大家对框架应该有初步的印象,接下来我们了解下目录结构。Fes.js 遵循 `约定优于配置` 的原则,一个基础的 Fes.js 项目大致是这样的: +``` +fes-template +├── package.json +├── tsconfig.json +├── mock.js +├── .fes.js +├── .env +├── dist +├── public +│ └── index.html +└── src + ├── .fes + └── pages + │ └── index.vue + └── app.js +``` + +### 根目录 + +#### package.json +包含插件和插件集,以 `@fesjs/preset-`、`@fesjs/plugin-`、`fes-preset-` 和 `fes-plugin-` 开头的依赖会被自动注册为插件或插件集。 + +#### tsconfig.json +解决 `@fesjs/fes` 和使用 `@` 的 API 提示 + +#### .fes.js +配置文件,包含 Fes.js 内置功能和插件的配置。 + +#### .env +定义环境变量。 + +比如 `.env` 文件内容如下: +``` +PORT=8888 +FES_ENV=prod +``` +等同于 node 端运行时,设置如下: +``` +process.env.PORT = '8888'; +process.env.FES_ENV = 'prod'; +``` + +#### mock.js +mock 数据的配置文件。 + +### dist 目录 +执行 `fes build` 后,产物默认会存放在这里。 + +### public 目录 +此目录下所有文件会被 `copy` 到输出路径。 + +#### index.html +默认的 `html` 模板文件,如果删除此 `html` 则会使用内置的 `html` 模板文件。 + +### src 目录 + +#### .fes 目录 +临时文件目录,比如入口文件、路由等,都会被临时生成到这里。不要提交 `.fes` 目录到 `git` 仓库,他们会在 `fes dev` 和 `fes build` 时被删除并重新生成。 + +#### pages 目录 +所有路由组件存放在这里。 + +#### app.js +运行时配置文件,可以在这里扩展运行时的能力,比如修改路由等。 \ No newline at end of file diff --git a/docs/guide/env.md b/docs/guide/env.md new file mode 100644 index 00000000..8dfa1ec6 --- /dev/null +++ b/docs/guide/env.md @@ -0,0 +1,109 @@ +# 环境变量 + +## 设置环境变量 + +### 执行命令时添加 +比如: +```bash +# OS X, Linux +PORT=3000 umi dev + +# Windows (cmd.exe) +set PORT=3000 && umi dev +``` +如果要同时考虑 OS X 和 Windows,可借助三方工具 [cross-env](https://github.com/kentcdodds/cross-env) + + + +```bash +yarn add cross-env --dev +cross-env PORT=3000 umi dev +``` + + + + +```bash +npm i cross-env --save-dev +cross-env PORT=3000 umi dev +``` + + + + +### 在 .env 文件中定义 +Fes.js 中约定根目录下以 `.env` 开头的文件为环境变量配置文件。 + +比如: +```bash +PORT=3000 +``` +然后执行 +```bash +fes dev +``` +会以 3000 端口启动 dev server。 + +#### 本地临时配置 +可以新建 `.env.local`,这份配置会和 `.env` 做 `merge` 后形成最终配置。 + +#### 多环境多份配置 +可以通过环境变量 `FES_ENV` 区分不同环境来指定配置,这时候必须在执行命令前添加 `FES_ENV` 保证执行加载环境变量配置文件逻辑前 `FES_ENV` 已设置。 + +举个 🌰 : +```bash +FES_ENV=sit umi dev +``` +如果存在 `.env.sit` 文件,则会将 `.env.sit` 的配置和 `.env` 做 `merge` 后形成最终配置。 + +#### 配置优先级 + +临时配置 > 环境配置 > .env + +::: tip +如果两份配置中存在相同的项,则优先级高的会覆盖优先级低的。 +::: + + +## 环境变量列表 + +### FES_ENV +指定当前的环境,不同环境各自的配置文件。 + +### FES_PRESETS +添加额外的插件集入口 + +### FES_PLUGINS +添加额外的插件入口 + +### PORT +`fes dev` 时服务指定的端口号,默认是 `8080` + +### HOST +默认是 `localhost`。 + +### HTTPS +默认是 `false`。 + +### WATCH +设为 none 时不监听文件变更。比如: +``` +WATCH=none fes dev +``` + +### BABEL_CACHE +默认开启 Babel 编译缓存,值为 none 时禁用缓存。 + +### ANALYZE +用于分析 bundle 构成,默认关闭。 + +比如: +``` +ANALYZE=1 fes build +``` + +### ANALYZE_MODE +默认是`server` + +### ANALYZE_PORT +默认是`8888` diff --git a/docs/guide/faq.md b/docs/guide/faq.md new file mode 100644 index 00000000..307d253c --- /dev/null +++ b/docs/guide/faq.md @@ -0,0 +1 @@ +# 常见问题 \ No newline at end of file diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 1561404b..cfa0cdc0 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -1,29 +1,43 @@ -# Getting Started - -## Prerequisites - -- [Node.js v12+](https://nodejs.org/) -- [Yarn v1 classic](https://classic.yarnpkg.com/en/) (Optional) - -## Manual Installation - -This section will help you build a basic VuePress documentation site from ground up. If you already have an existing project and would like to keep documentation inside the project, start from Step 3. - -- **Step 1**: Create and change into a new directory +# 快速上手 +## 依赖环境 +首先得有 [Node.js](https://nodejs.org/),并确保 node 版本是 10.13 或以上。 ```bash -mkdir vuepress-starter -cd vuepress-starter +# 打印 node 版本 +node -v +v10.13.0 +``` +推荐使用 yarn 管理 npm 依赖 +```bash +# 全局安装 yarn +npm i yarn -g ``` -- **Step 2**: Initialize your project +## 安装模板 +这一章节会帮助你从头搭建一个简单的 Fes.js 前端应用。 + +##### 步骤1 创建工作空间 +如果不存在,则创建 +```bash +# 创建目录 workspace +mkdir workspace +# 进入目录 workspace +cd workspace +``` +如果已存在工作空间,则直接进入 +```bash +# 进入目录 workspace +cd workspace +``` + +##### 步骤2 创建模板 ```bash -git init -yarn init +# 创建模板 +yarn create @fesjs/fes-app myapp ``` @@ -31,20 +45,30 @@ yarn init ```bash -git init -npm init +# 创建模板 +npx @fesjs/create-fes-app myapp ``` -- **Step 3**: Install VuePress locally +如果项目目录 `workspace/myapp` 已经存在,则会提示目录已存在,你可以选择 `Overwrite` 删除目录后重新创建项目,也可以选择 `Merge` 使用模板文件覆盖当前目录文件。 +![目录已存在提示](/pickTemplateTip.png) + +如果项目目录 `workspace/myapp` 不存在,你会被提示选取一个 template。你可以选默认适用于中后台前端应用的 `PC` 类型,也可以选适用于移动端的 `H5` 类型。 + +![选择模板类型](/pickTemplate.png) + +##### 步骤3 安装依赖 ```bash -yarn add -D vuepress@next +# 进入项目目录 +cd myapp +# 安装依赖 +yarn ``` @@ -52,43 +76,31 @@ yarn add -D vuepress@next ```bash -npm install -D vuepress@next +# 进入项目目录 +cd myapp +# 安装依赖 +npm i ``` -- **Step 4**: Add some [scripts](https://classic.yarnpkg.com/en/docs/package-json#toc-scripts) to `package.json` - -```json -{ - "scripts": { - "docs:dev": "vuepress dev docs", - "docs:build": "vuepress build docs" - } -} -``` - -- **Step 5**: Add the default temp and cache directory to `.gitignore` file - -```bash -echo 'node_modules\n.temp\n.cache' >> .gitignore -``` - -- **Step 6**: Create your first document - -```bash -mkdir docs -echo '# Hello VuePress' > docs/README.md -``` - -- **Step 7**: Serve the documentation site in the local server - +## 启动项目 ```bash -yarn docs:dev +# 开发调试 +yarn dev + +yarn run v1.22.4 +$ fes dev +Starting the development server http://localhost:8080 ... + +✔ Webpack + Compiled successfully in 15.91s + + DONE Compiled successfully in 15917ms 11:17:08 AM ``` @@ -96,12 +108,85 @@ yarn docs:dev ```bash -npm run docs:dev +# 开发调试 +npm run dev + + +> @fesjs/fes-template@2.0.0-alpha.1 dev /Users/harrywan/company/git/fes.js/packages/fes-template +> fes dev + +Starting the development server http://localhost:8080 ... + +✔ Webpack + Compiled successfully in 3.66s + + DONE Compiled successfully in 3662ms 11:17:46 AM ``` - VuePress will start a hot-reloading development server at [http://localhost:8080](http://localhost:8080). When you modify your markdown files, the content in the browser will be auto updated. -By now, you should have a basic but functional VuePress documentation site. Next, learn about the basics of [configuration](./configuration.md) in VuePress. +Fes.js 会在 [http://localhost:8080](http://localhost:8080) 启动一个热重载的开发服务器。当你修改你的 .vue 文件时,浏览器中的内容也会自动更新。 + +![home](/home.png) + + +## 部署发布 + +### 构建 + + + +```bash +# 构建 +yarn build + +yarn run v1.22.4 +$ fes build + +✔ Webpack + Compiled successfully in 45.37s + +✨ Done in 48.87s. +``` + + + + + +```bash +# 构建 +npm run build + +> @fesjs/fes-template@2.0.0-alpha.1 build /Users/harrywan/company/git/fes.js/packages/fes-template +> fes build + +✔ Webpack + Compiled successfully in 45.37s +``` + + + + +构建产物默认生成到 ./dist 下,然后通过 tree 命令查看。 +```base +tree ./dist + +dist +├── chunk-vendors.27cd4686.js +├── chunk-vendors.a5f5de67.css +├── index.11411d43.css +├── index.d72f1ba2.js +├── index.html +├── logo.png +└── static + └── logo.0f85bba0.png +``` + +### 本地验证 +发布之前,可以通过 [serve](https://github.com/vercel/serve) 做本地验证,验证结果应该跟执行 `dev` 的结果一样。 + + +### 部署 +本地验证完,就可以部署了。你需要把 dist 目录部署到服务器上。 \ No newline at end of file diff --git a/docs/guide/i18n.md b/docs/guide/i18n.md deleted file mode 100644 index 18ed8f86..00000000 --- a/docs/guide/i18n.md +++ /dev/null @@ -1,70 +0,0 @@ -# I18n - -## Site I18n Config - -To take advantage of multi-language support in VuePress, you first need to use the following file and directory structure: - -``` -docs -├─ README.md -├─ foo.md -├─ nested -│  └─ README.md -└─ zh - ├─ README.md - ├─ foo.md - └─ nested -    └─ README.md -``` - -Then, specify the `locales` option in your [config file](./configuration.md#config-file): - -```js -module.exports = { - locales: { - // The key is the path for the locale to be nested under. - // As a special case, the default locale can use '/' as its path. - '/': { - lang: 'en-US', - title: 'VuePress', - description: 'Vue-powered Static Site Generator', - }, - '/zh/': { - lang: 'zh-CN', - title: 'VuePress', - description: 'Vue 驱动的静态网站生成器', - }, - }, -} -``` - -If a locale does not have a `lang`, `title`, `description` or `head`, VuePress will fallback to the root-level values. You can omit the root level config as long as they are provided in each locale. - -::: tip -Config reference: [locales](../reference/config.md#locales) -::: - -## Theme I18n Config - -VuePress does not restrict how themes provide multi-language support, so each theme may have different way to handle i18n, and some themes may not provide multi-language support at all. You'd better refer to the theme documentation for detailed guide. - -If you are using default theme, the multi-language support is the same with above: - -```js -module.exports = { - themeConfig: { - locales: { - '/': { - selectLanguageName: 'English', - }, - '/zh/': { - selectLanguageName: '简体中文', - }, - }, - }, -} -``` - -::: tip -Config reference: [Default Theme > locales](../reference/default-theme/config.md#locales) -::: diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md deleted file mode 100644 index b67f60be..00000000 --- a/docs/guide/markdown.md +++ /dev/null @@ -1,366 +0,0 @@ -# Markdown - -Make sure you have known Markdown well before reading this section. If not, please learn some [Markdown tutorials](https://commonmark.org/help/) first. - -## Syntax Extensions - -The Markdown content in VuePress will be parsed by [markdown-it](https://github.com/markdown-it/markdown-it), which supports [syntax extensions](https://github.com/markdown-it/markdown-it#syntax-extensions) via markdown-it plugins. - -This section will introduce built-in Markdown syntax extensions of VuePress. - -You can also configure those built-in extensions, load more markdown-it plugins and implement your own extensions via [markdown](../reference/config.md#markdown) option and [extendsMarkdown](../reference/plugin-api.md#extendsmarkdown) option. - -### Embedded - -Embedded by markdown-it: - -- [Tables](https://help.github.com/articles/organizing-information-with-tables/) (GFM) -- [Strikethrough](https://help.github.com/articles/basic-writing-and-formatting-syntax/#styling-text) (GFM) - -### Header Anchors - -You might have noticed that, a `#` anchor is displayed when you hover the mouse on the headers of each section. By clicking the `#` anchor, you can jump to the section directly. - -::: tip -This header anchors extension is supported by [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor). - -Config reference: [markdown.anchor](../reference/config.md#markdown-anchor) -::: - -### Links - -When using Markdown [link syntax](https://spec.commonmark.org/0.29/#link-reference-definitions), VuePress will implement some conversions for you. - -Take our documentation source files as an example: - -```bash -└─ docs - ├─ guide - │ ├─ getting-started.md - │ ├─ markdown.md # <- Here we are - │ └─ README.md - ├─ reference - │ └─ config.md - └─ README.md -``` - -**Raw Markdown** - -```md -[Home](/README.md) -[Guide](/guide/) -[Getting Started](./getting-started.md) -[markdown.links](../reference/config.md#links) -[GitHub](https://github.com) -``` - -**Converted to** - -```vue -Home -Guide -Getting Started -markdown.links -GitHub -``` - -**Rendered as** - -[Home](/README.md) -[Guide](/guide/) -[Getting Started](./getting-started.md) -[markdown.links](../reference/config.md#links) -[GitHub](https://github.com) - -**Explanation** - -- Internal links will be converted to `` for SPA navigation. -- Internal links to `.md` files will be converted to the [page route path](./page.md#routing), and both absolute path and relative path are supported. -- External links will get `target="_blank" rel="noopener noreferrer"` attrs and a indicator. - -::: tip -This links extension is supported by our built-in plugin. - -Config reference: [markdown.links](../reference/config.md#markdown-links) - -Also see: [Built-in Components > OutboundLink](../reference/components.md#outboundlink) -::: - -### Emoji :tada: - -You can add emoji to your Markdown content by typing `:EMOJICODE:`. - -For a full list of available emoji and codes, check out [emoji-cheat-sheet.com](https://emoji-cheat-sheet.com/). - -**Input** - -```md -VuePress 2 is out :tada: ! -``` - -**Output** - -VuePress 2 is out :tada: ! - -::: tip -This emoji extension is supported by [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji). - -Config reference: [markdown.emoji](../reference/config.md#markdown-emoji) -::: - -### Table of Contents - -If you want to put the table of contents (TOC) of your current page inside your Markdown content, you can use the `[[toc]]` syntax. - -**Input** - -```md -[[toc]] -``` - -**Output** - -[[toc]] - -The headers in TOC will link to the corresponding [header anchors](#header-anchors), so TOC won't work well if you disable header anchors. - -::: tip -This toc extension is supported by our built-in plugin, which is forked and modified from [markdown-it-toc-done-right](https://github.com/nagaozen/markdown-it-toc-done-right). - -Config reference: [markdown.toc](../reference/config.md#markdown-toc) -::: - -### Code Blocks - -Following code blocks extensions are implemented during markdown parsing in Node side. That means, the code blocks won't be processed in client side. - -If you want to implement client-side syntax highlighting via [prism.js](https://prismjs.com/#basic-usage) or [highlight.js](https://highlightjs.org/), you could disable our code blocks extensions, and introduce your library manually in client side. - -#### Syntax Highlighting - -VuePress uses [Prism](https://prismjs.com/) to highlight language syntax in Markdown code blocks, using coloured text. - -Prism supports a wide variety of programming languages. For a full list of available languages, check out [Prism supported languages](https://prismjs.com/#supported-languages). - -You can add an optional language identifier to enable syntax highlighting in your fenced code blocks: - -**Input** - -````md -```ts -import type { UserConfig } from '@vuepress/cli' - -export const config: UserConfig = { - title: 'Hello, VuePress', -} -``` -```` - -**Output** - -```ts -import type { UserConfig } from '@vuepress/cli' - -export const config: UserConfig = { - title: 'Hello, VuePress', -} -``` - -::: tip -This syntax highlighting extension is supported by our built-in plugin. - -Config reference: [markdown.code.highlight](../reference/config.md#markdown-code-highlight) -::: - -#### Line Highlighting - -You can highlight specified lines of your code blocks by adding line ranges mark in your fenced code blocks: - -**Input** - -````md -```ts{1,6-8} -import type { UserConfig } from '@vuepress/cli' - -export const config: UserConfig = { - title: 'Hello, VuePress', - - themeConfig: { - logo: 'https://vuejs.org/images/logo.png', - }, -} -``` -```` - -**Output** - -```ts{1,6-8} -import type { UserConfig } from '@vuepress/cli' - -export const config: UserConfig = { - title: 'Hello, VuePress', - - themeConfig: { - logo: 'https://vuejs.org/images/logo.png', - }, -} -``` - -Examples for line ranges mark: - -- Line ranges: `{5-8}` -- Multiple single lines: `{4,7,9}` -- Combined: `{4,7-13,16,23-27,40}` - -::: tip -This line highlighting extension is supported by our built-in plugin, which is forked and modified from [markdown-it-highlight-lines](https://github.com/egoist/markdown-it-highlight-lines). - -Config reference: [markdown.code.highlightLines](../reference/config.md#markdown-code-highlightlines) -::: - -#### Line Numbers - -You must have noticed that the number of lines is displayed on the left side of code blocks. This is enabled by default and you can disable it in config. - -You can add `:line-numbers` / `:no-line-numbers` mark in your fenced code blocks to override the value set in config. - -**Input** - -````md -```ts -// line-numbers is enabled by default -const line2 = 'This is line 2' -const line3 = 'This is line 3' -``` - -```ts:no-line-numbers -// line-numbers is disabled -const line2 = 'This is line 2' -const line3 = 'This is line 3' -``` -```` - -**Output** - -```ts -// line-numbers is enabled by default -const line2 = 'This is line 2' -const line3 = 'This is line 3' -``` - -```ts:no-line-numbers -// line-numbers is disabled -const line2 = 'This is line 2' -const line3 = 'This is line 3' -``` - -::: tip -This line numbers extension is supported by our built-in plugin. - -Config reference: [markdown.code.lineNumbers](../reference/config.md#markdown-code-linenumbers) -::: - -#### Wrap with v-pre - -As [template syntax is allowed in Markdown](#template-syntax), it would also work in code blocks, too. - -To avoid your code blocks being compiled by Vue, VuePress will add [v-pre](https://v3.vuejs.org/api/directives.html#v-pre) directive to your code blocks by default, which can be disabled in config. - -You can add `:v-pre` / `:no-v-pre` mark in your fenced code blocks to override the value set in config. - -::: warning -The template syntax characters, for example, the "Mustache" syntax (double curly braces) might be parsed by the syntax highlighter. Thus, as the following example, `:no-v-pre` might not work well in some languages. - -If you want to make Vue syntax work in those languages anyway, try to disable the default syntax highlighting and implement your own syntax highlighting in client side. -::: - -**Input** - -````md -```md - -1 + 2 + 3 = {{ 1 + 2 + 3 }} -``` - -```md:no-v-pre - -1 + 2 + 3 = {{ 1 + 2 + 3 }} -``` - -```js:no-v-pre -// This won't be compiled correctly because of js syntax highlighting -const onePlusTwoPlusThree = {{ 1 + 2 + 3 }} -``` -```` - -**Output** - -```md - -1 + 2 + 3 = {{ 1 + 2 + 3 }} -``` - -```md:no-v-pre - -1 + 2 + 3 = {{ 1 + 2 + 3 }} -``` - -```js:no-v-pre -// This won't be compiled correctly because of js syntax highlighting -const onePlusTwoPlusThree = {{ 1 + 2 + 3 }} -``` - -::: tip -This v-pre extension is supported by our built-in plugin. - -Config reference: [markdown.code.vPre](../reference/config.md#markdown-vpre) -::: - -## Using Vue in Markdown - -This section will introduce some basic usage of Vue in Markdown. - -Check out [Advanced > Markdown and Vue SFC](./advanced/markdown.md) for more details. - -### Template Syntax - -As we know: - -- HTML is allowed in Markdown. -- Vue template syntax is compatible with HTML. - -That means, [Vue template syntax](https://v3.vuejs.org/guide/template-syntax.html) is allowed in Markdown. - -**Input** - -```md -One plus one equals: {{ 1 + 1 }} - - span: {{ i }} -``` - -**Output** - -One plus one equals: {{ 1 + 1 }} - - span: {{ i }} - -### Components - -You can use Vue components directly in Markdown. - -**Input** - -```md -This is default theme built-in `` component -``` - -**Output** - -This is default theme built-in `` component - -::: tip -Check out the [Built-in Components](../reference/components.md) for a full list of built-in components. - -Check out the [Default Theme > Built-in Components](../reference/default-theme/components.md) for a full list of default theme built-in components. -::: diff --git a/docs/guide/mock.md b/docs/guide/mock.md new file mode 100644 index 00000000..a2147e6c --- /dev/null +++ b/docs/guide/mock.md @@ -0,0 +1,183 @@ +# Mock 数据 + +Mock 数据是前端开发过程中必不可少的一环,是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发独立自主,不会被服务端的开发所阻塞。 + +## 约定式 Mock 文件 + +Fes.js 约定 `src/mock.js` 为 mock 文件。 + +比如: +``` +. +├── mock.js +└── src + └── pages + └── index.vue +``` + +## 编写 Mock 文件 + +可以参考如下例子: +``` js +module.exports = function ({ cgiMock, mockjs, utils }) { + const { Random } = mockjs; + + // 测试 proxy 与 mock 用例集合 + cgiMock('/movie/in_theaters_mock', (req, res) => { + res.send(JSON.stringify({ + code: '0', + msg: '', + result: { + text: 'movie: movie/in_theaters_mock ~~~~~' + } + })); + }); + cgiMock('/movie/test_mock', (req, res) => { + res.send(JSON.stringify({ + code: '0', + msg: '', + result: { + text: 'mock: movie/test_mock' + } + })); + }); + + // 测试用例: mock.js change,重现请求,需要能拉最新的数据 + cgiMock('/watchtest', (req, res) => { + res.send(JSON.stringify({ + code: '0', + msg: '', + result: { + text: '通过 register 测试 mock watch: 初始状态' + } + })); + }); + + // 返回一个数字 + // cgiMock('/number', 666); + cgiMock('/number', 999); + + // 返回一个json + cgiMock({ + url: '/json', + result: { + code: '400101', msg: "不合法的请求:Missing cookie 'wb_app_id' for method parameter of type String", transactionTime: '20170309171146', success: false + } + }); + + // 利用 mock.js 产生随机文本 + cgiMock('/text', Random.cparagraph()); + + // 返回一个字符串 利用 mock.js 产生随机字符 + cgiMock('/random', mockjs.mock({ + 'string|1-10': '★' + })); + + // 正则匹配url, 返回一个字符串 + cgiMock(/\/abc|\/xyz/, 'regexp test!'); + + // option.result 参数如果是一个函数, 可以实现自定义返回内容, 接收的参数是是经过 express 封装的 req 和 res 对象. + cgiMock(/\/function$/, (req, res) => { + res.send('function test'); + }); + + // 返回文本 readFileSync + cgiMock('/file', utils.file('./package.json')); + + // 更复杂的规则配置 + cgiMock({ + url: /\/who/, + method: 'GET', + result(req, res) { + if (req.query.name === 'kwan') { + res.json({ kwan: '孤独患者' }); + } else { + res.send('Nooooooooooo'); + } + }, + headers: { + 'Content-Type': 'text/plain', + 'Content-Length': '123', + ETag: '12345' + }, + cookies: [ + { + name: 'myname', value: 'kwan', maxAge: 900000, httpOnly: true + } + ] + }); + + // 携带参数的请求 + cgiMock('/v2/audit/list', (req, res) => { + const { + currentPage, pageSize, isAudited + } = req.body; + res.send({ + code: '0', + msg: '', + data: { + currentPage, + pageSize, + totalPage: 2, + totalCount: 12, + pageData: Array.from({ length: pageSize }, () => ({ + title: Random.title(), + authorName: Random.cname(), + authorId: Random.name(), + createTime: Date.now(), + updateTime: Date.now(), + readCount: Random.integer(60, 1000), + favoriteCount: Random.integer(1, 50), + postId: '12323', + serviceTag: '业务类型', + productTag: '产品类型', + requestTag: '需求类型', + handleTag: '已采纳', + postType: 'voice', + postStatus: isAudited ? 'pass' : 'auditing', + auditStatus: 'audit1' + })) + } + }); + }); + + // multipart/form-data 类型 + cgiMock('/v2/upload', (req, res) => { + res.send({ + code: '0', + msg: '文件上传成功' + }); + }); +}; +``` + +### cgiMock 参数 +创建一个 mock 接口,参数非常灵活,参考上面的 demo 即可。 + + +### mockjs 参数 +[Mock.js](http://mockjs.com/) 是常用的辅助生成模拟数据的三方库,借助他可以提升我们的 mock 数据能力。 + +比如: +```js +module.exports = function ({ cgiMock, mockjs, utils }) { + cgiMock('/random', mockjs.mock({ + 'string|1-10': '★' + })); +} +``` + +### utils 参数 +工具函数: +- utils.file(path),从项目根目录根据path寻找文件,返回文件流。 + +## 配置 Mock +详见配置 #mock。 + +## 关闭 Mock +可以通过配置关闭。 +```js +export default { + mock: false, +}; +``` diff --git a/docs/guide/page.md b/docs/guide/page.md deleted file mode 100644 index b67d9918..00000000 --- a/docs/guide/page.md +++ /dev/null @@ -1,55 +0,0 @@ -# Page - -VuePress is markdown-centered. Each markdown file inside your project is a standalone page. - -## Routing - -By default, the route path of a page is determined by the relative path of your markdown file. - -Assuming this is the directory structure of your markdown files: - -``` -└─ docs - ├─ guide - │ ├─ getting-started.md - │ └─ README.md - ├─ contributing.md - └─ README.md -``` - -Take the `docs` directory as your [sourceDir](../reference/cli.md), e.g. your are running `vuepress dev docs` command. Then the route paths of your markdown files would be: - -| Relative Path | Route Path | -|--------------------|----------------------| -| `/README.md` | `/` | -| `/contributing.md` | `/contributing.html` | -| `/guide/README.md` | `/guide/` | -| `/guide/page.md` | `/guide/page.html` | - -## Frontmatter - -A markdown file could contain a [YAML](https://yaml.org/) frontmatter. The frontmatter must be at the top of the Markdown file and must be wrapped with a couple of triple-dashed lines. Here is a basic example: - -```md ---- -lang: en-US -title: Title of this page -description: Description of this page ---- -``` - -You must have noticed that those fields are similar with the [Site Config](./configuration.md#site-config) of in the [Config File](./configuration.md#config-file). You can override `lang`, `title`, `description`, etc., of current page via frontmatter. So you can take frontmatter as page scope config. - -Also, VuePress has built-in support for some frontmatter fields, and your theme may have its own special frontmatter, too. - -::: tip -Check out the [Frontmatter Reference](../reference/frontmatter.md) for a full list of VuePress built-in frontmatter. - -Check out the [Default Theme > Frontmatter Reference](../reference/default-theme/frontmatter.md) for the frontmatter of default theme. -::: - -## Content - -The main content of your page is written in Markdown. VuePress will firstly transform your Markdown to HTML code, then treat the HTML code as `