From 67064451833d7c327ec9038d114de503951285af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=A4=9A=E7=9B=8A?= Date: Tue, 29 Dec 2020 12:21:10 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E9=AB=98=E7=BA=A7?= =?UTF-8?q?=E5=88=86=E7=B1=BB=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=9B=BD=E9=99=85?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E6=89=A9=E5=B1=95=E7=8E=B0=E6=9C=89=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E6=96=87=E6=A1=A3=20(#1269)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 完善扩展相关文档初步 * 文档完善 * 文档文字完善 --- docs/components/form/formitem.md | 2 +- docs/concepts/datascope-and-datachain.md | 73 ++++- docs/extend/addon.md | 147 +++++++++ .../custom.md => extend/custom-react.md} | 278 +----------------- docs/extend/custom-sdk.md | 141 +++++++++ docs/extend/i18n.md | 83 ++++++ docs/extend/internal.md | 151 ++++++++++ docs/start/faq.md | 11 +- docs/start/getting-started.md | 40 +-- docs/style/index.md | 2 +- examples/components/Doc.tsx | 69 +++-- examples/embed.tsx | 2 + 12 files changed, 672 insertions(+), 327 deletions(-) create mode 100644 docs/extend/addon.md rename docs/{start/custom.md => extend/custom-react.md} (54%) create mode 100644 docs/extend/custom-sdk.md create mode 100644 docs/extend/i18n.md create mode 100644 docs/extend/internal.md diff --git a/docs/components/form/formitem.md b/docs/components/form/formitem.md index ba0cb074b..c2f4efdea 100755 --- a/docs/components/form/formitem.md +++ b/docs/components/form/formitem.md @@ -29,7 +29,7 @@ order: 1 } ``` -- `name`: **必填属性**,标识表单数据域中,当前表单项值的`key` +- `name`: **必填属性**,标识表单数据域中,当前表单项值的`key`,这个 name 可以是深层结构,比如 `aa.bb` - `type`: **必填属性**,标识表单项类型 - `label`: 标识表单项的标签 diff --git a/docs/concepts/datascope-and-datachain.md b/docs/concepts/datascope-and-datachain.md index dee034968..b00115ad7 100755 --- a/docs/concepts/datascope-and-datachain.md +++ b/docs/concepts/datascope-and-datachain.md @@ -46,7 +46,7 @@ order: 10 渲染后页面如下: -```schema:height="200" +```schema:height="50" { "type": "page", "initApi": "https://houtai.baidu.com/api/mock2/page/initData", @@ -81,7 +81,7 @@ order: 10 毫无疑问,`${text}`将会解析为空白文本,最终渲染的文本是 `Hello` -```schema:height="200" +```schema:height="50" { "type": "page", "body": "Hello ${text}" @@ -92,7 +92,7 @@ order: 10 再观察下面这段配置: -```schema:height="200" +```schema:height="50" { "data": { "text": "World!" @@ -106,7 +106,7 @@ order: 10 相信你可能已经猜到,**组件的`data`属性值是数据域的一种形式**,实际上当我们没有显式的配置数据域时,可以假想成这样: -```schema:height="200" +```schema:height="50" { "data": {}, // 空的数据域 "type": "page", @@ -114,8 +114,6 @@ order: 10 } ``` -> amis 中大部分组件都具有数据域。 -> > 而前面我们知道 amis 的特性之一是基于组件树,因此自然数据域也会形成类似于树型结构,如何来处理这些数据域之间的联系呢,这就是我们马上要介绍到的 **[数据链](./datascope-and-datachain#%E6%95%B0%E6%8D%AE%E9%93%BE)** ## 数据链 @@ -132,7 +130,7 @@ order: 10 继续来看这个例子: -```schema:height="200" +```schema:height="70" { "type": "page", "data": { @@ -193,6 +191,67 @@ page > **注意:** 当前例子中,对数据域中数据的获取使用的是 **\${xxx}** 模板语法,但是在不同的组件配置项中,获取数据的语法会有差异,我们会在后续的[模板](./template)和[表达式章节](./expression)中一一介绍。 +### 常见误解 + +需要注意,只有少数几个容器组件会创建新的数据域,除了最顶层的 Page,还有 CRUD、Dialog、IFrame、Form、Serice 等。 + +常见的错误写法是给容器组件加 data 属性,比如: + +```schema:height="70" +{ + "type": "page", + "data": { + "name": "zhangsan" + }, + "body": [ + { + "type": "tpl", + "tpl": "my name is ${name}" + }, + { + "type": "container", + "data": { + "name": "lisi" + }, + "body": { + "type": "tpl", + "tpl": "my name is ${name}" + } + } + ] +} +``` + +这样是不会生效的,正确的做法是使用 Service 包裹一层,如下所示 + +```schema:height="70" +{ + "type": "page", + "data": { + "name": "zhangsan" + }, + "body": [ + { + "type": "tpl", + "tpl": "my name is ${name}" + }, + { + "type": "service", + "data": { + "name": "lisi" + }, + "body": { + "type": "container", + "body": { + "type": "tpl", + "tpl": "my name is ${name}" + } + } + } + ] +} +``` + ## 初始化数据域 通过上面的介绍你可能发现,初始化数据域有两种方式: diff --git a/docs/extend/addon.md b/docs/extend/addon.md new file mode 100644 index 000000000..e26ef1038 --- /dev/null +++ b/docs/extend/addon.md @@ -0,0 +1,147 @@ +--- +title: 扩展现有组件 +--- + +除了新增组件,在 amis 中还能扩展和修改现有组件。 + +## 事件扩展 + +amis 默认会将配置项剩余参数都作为 React 的 props 传入对应标签,使得可以添加自己的自定义事件,比如 下面的例子 + +```javascript +let amis = amisRequire('amis/embed'); +let amisLib = amisRequire('amis'); +let amisScoped = amis.embed('#root', { + type: 'page', + title: '表单页面', + body: { + type: 'form', + mode: 'horizontal', + api: '/saveForm', + controls: [ + { + type: 'button', + label: '按钮', + onClick: () => { + amisLib.toast.info('消息通知'); + } + } + ] + } +}); +``` + +这样就能在点击按钮的时候执行自定义代码了。 + +## 同时支持多种类型编辑 + +在表单编辑中,每个 name 一般对应一种类型,如果这个 name 有多种类型,比如下面的例子中 id 的值有可能是字符串,也有可能是数字,但 type 只能设置为一种类型,这种情况如何处理? + +```schema:height="200" scope="body" +{ + "type": "form", + "mode": "horizontal", + "controls": [ + { + "name": "id", + "type": "text", + "label": "id" + } + ] +} +``` + +有两种方式: + +### 使用另一个名称作为状态 + +```schema:height="250" scope="body" +{ + "type": "form", + "mode": "horizontal", + "controls": [ + { + "name": "idIsNumber", + "type": "switch", + "label": "id 是数字类型" + }, + { + "name": "id", + "type": "text", + "label": "id", + "hiddenOn": "data.idIsNumber" + }, + { + "name": "id", + "type": "number", + "label": "id", + "visibleOn": "data.idIsNumber" + } + ] +} +``` + +可以看到在一个 form 中可以有两个 name 相同的组件,通过 hiddenOn 或 visibleOn 来控制同时只显示一个。 + +### 使用 PipeIn/PipeOut 方法 + +如果不想增加一个新的 name,在 JS SDK 或 React 还有更高级的处理方法,可以增加一个 name 同样为 id 的 switch,实现 PipeIn/PipeOut 函数来进行自动识别,下面是个示例: + +```javascript +let amis = amisRequire('amis/embed'); +let amisScoped = amis.embed('#root', { + type: 'page', + title: '表单页面', + // 可以通过去掉下面的注释来测试 + // data: { + // id: 1 + // }, + body: { + type: 'form', + mode: 'horizontal', + api: '/saveForm', + controls: [ + { + type: 'switch', + label: 'id 是数字', + name: 'id', + // pipeIn 返回的应该是这个组件所需的值,比如 switch 的返回值就应该是 true 或 false + // 这里的 value 就是初始值,如果不设置将会是 undefined + pipeIn: (value, data) => { + if (typeof value === 'undefined') { + return false; + } + return typeof value !== 'string'; + }, + // 这里的 value 是点击 switch 之后的值,比如打开就是 true,关闭就是 false + pipeOut: (value, oldValue, data) => { + if (value) { + return 1; // 切换到数字之后的默认值 + } else { + return 'id1'; // 关闭之后的默认值 + } + } + }, + { + name: 'id', + type: 'text', + label: 'id', + visibleOn: + 'typeof data.id === "undefined" || typeof data.id === "string"' + }, + { + name: 'id', + type: 'number', + label: 'id', + visibleOn: 'typeof data.id === "number"' + } + ] + } +}); +``` + +不过这种写法的复杂度较高 + +## 修改组件标签 + +有些组件可以设置 `wrapperComponent`,比如 Form 下默认使用 form 标签,在浏览器中会自带回车提交功能,如果想去掉这个功能,可以将 `wrapperComponent` 设置为 `div`。 diff --git a/docs/start/custom.md b/docs/extend/custom-react.md similarity index 54% rename from docs/start/custom.md rename to docs/extend/custom-react.md index fc86c3313..f8afe46d4 100644 --- a/docs/start/custom.md +++ b/docs/extend/custom-react.md @@ -1,123 +1,7 @@ --- -title: 自定义 +title: 自定义组件 - React --- -如果默认的组件不能满足需求,可以通过自定义组件来进行扩展,根据不同使用方式,一共有四种方法: - -1. JS SDK - 1. 使用 custom 组件临时扩展,但不支持组件复用。 - 2. JS SDK 注册组件,支持组件复用。 -2. React - 1. React 临时扩展,适合无需复用的组件。 - 2. React 注册自定义类型,支持组件复用。 - -## 使用 custom 组件临时扩展 - -基于 custom 组件可以直接在 amis 配置实现自定义功能,它的支持面最广,是唯一支持在可视化编辑器中使用的方法。 - -使用 custom 组件类似如下写法: - -```javascript -{ - label: '使用 custom 组件', - name: 'username', // 如果要放在 form 中,需要设置 name,onChange 将会设置这个值 - type: 'custom', - // onMount 将会在组件创建时执行,默认会创建一个空 div 标签,也可以设置 inline: true 来创建 span 标签 - // dom 是 dom 节点,value 是初始数据,比如表单 name 初始拿到的数据,onChange 只有在表单下才会有 - onMount: (dom, value, onChange) => { - const button = document.createElement('button'); - button.innerText = '点击修改姓名'; - button.onclick = event => { - onChange('new name'); - event.preventDefault(); - }; - dom.appendChild(button); - }, - // onUpdate 将会在数据更新时被调用 - // dom 是 dom 节点、data 将包含表单所有数据,prevData 是之前表单的所有数据 - onUpdate: (dom, data, prevData) => { - console.log('数据有变化', data); - }, - // onUnmount 将会在组件被销毁的时候调用,用于清理资源 - onUnmount:() => { - console.log('组件被销毁'); - } -} -``` - -注意上面的代码用到了 JavaScript 函数,无法转成 json 格式,但这三个函数还支持字符串形式,上面的代码可以改成如下形式,这样就能在可视化编辑器里支持自定义组件了: - -```schema:height="330" scope="body" -{ - "type": "form", - "title": "custom 组件", - "controls": [ - { - "type": "text", - "name": "username", - "label": "姓名" - }, - { - "name": "username", - "type": "custom", - "label": "自定义组件", - "onMount": "const button = document.createElement('button'); button.innerText = '点击修改姓名'; button.onclick = event => { onChange('new name'); event.preventDefault(); }; dom.appendChild(button);" - } - ] -} -``` - -注意上面的例子中两个组件的 name 是一样的,这是为了方便示例,因为 amis 中的数据是双向绑定的,因此 onChange 修改自身的时候,另一个「姓名」输入框由于 name 一样,也会同步更新。 - -关于 custom 组件的更多属性请参考「[Custom 组件](../components/custom)」。 - -## JS SDK 注册组件 - -amis 组件都是基于 React 的,所以需要使用一个简单的 React 组件来注册,可以是函数组件也可以是类组件,下面以函数组件为例,将[快速开始](getting-started)中的代码替换成如下示例: - -```javascript -(function () { - let amis = amisRequire('amis/embed'); - let amisLib = amisRequire('amis'); - let React = amisRequire('react'); - - // 自定义组件 - function CustomComponent() { - let dom = React.useRef(null); - React.useEffect(function () { - // 从这里开始写自定义代码,dom.current 就是新创建的 dom 节点 - // 可以基于这个 dom 节点对接任意 JavaScript 框架,比如 jQuery/Vue 等 - dom.current.innerHTML = 'custom'; - }); - return React.createElement('div', { - ref: dom - }); - } - - //注册自定义组件,请参考后续对工作原理的介绍 - amisLib.Renderer({ - test: /(^|\/)my-custom/ - })(CustomComponent); - - let amisScoped = amis.embed('#root', { - type: 'page', - title: '表单页面', - body: { - type: 'form', - mode: 'horizontal', - api: '/saveForm', - controls: [ - { - label: 'Name', - type: 'my-custom', // 注意这个的 type 对应之前注册的 test - name: 'custom' - } - ] - } - }); -})(); -``` - ## React 临时扩展 amis 的配置最终会转成 React 组件来执行,所以如果只是想在某个配置中加入定制功能,可以直接在这个 JSON 配置里写 React 代码,比如下面这个例子: @@ -163,157 +47,7 @@ amis 的配置最终会转成 React 组件来执行,所以如果只是想在 ## React 注册自定义类型 -注册自定义类型需要了解 amis 的工作原理。 - -### 工作原理 - -amis 的渲染过程是将 `json` 转成对应的 React 组件。先通过 `json` 的 type 找到对应的 `Component` 然后,然后把其他属性作为 `props` 传递过去完成渲染。 - -拿一个表单页面来说,如果用 React 组件开发一般长这样。 - -```jsx - -
- -``` - -把以上配置方式换成 amis JSON, 则是: - -```json -{ - "type": "page", - "title": "页面标题", - "subTitle": "副标题", - "body": { - "type": "form", - "title": "用户登录", - "controls": [ - { - "type": "text", - "name": "username", - "label": "用户名" - } - ] - } -} -``` - -那么,amis 是如何将 JSON 转成组件的呢?直接根据节点的 type 去跟组件一一对应?这样会重名,比如在表格里面展示的类型 `text` 跟表单里面的 `text` 是完全不一样的,一个负责展示,一个却负责输入。所以说一个节点要被什么组件渲染,还需要携带上下文(context)信息。 - -如何携带上下文(context)信息?amis 中是用节点的路径(path)来作为上下文信息。从上面的例子来看,一共有三个节点,path 信息分别是。 - -- `page` 页面节点 -- `page/body/form` 表单节点 -- `page/body/form/controls/0/text` 文本框节点。 - -根据 path 的信息就能很容易注册组件跟节点对应了。 - -Page 组件的示例代码 - -```jsx -import * as React from 'react'; -import {Renderer} from 'amis'; - -@Renderer({ - test: /^page$/ - // ... 其他信息隐藏了 -}) -export class PageRenderer extends React.Component { - // ... 其他信息隐藏了 - render() { - const { - title, - body, - render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。 - } = this.props; - return ( -
-

{title}

-
- {render('body', body) /*渲染孩子节点*/} -
-
- ); - } -} - -// 如果不支持 Decorators 语法也可以使用如下写法 -export Renderer({ - test: /^page$/ -})(class PageRenderer extends React.Component { - render() { - // ...同上 - } -}) -``` - -Form 组件的示例代码 - -```jsx -@Renderer({ - test: /(^|\/)form$/ - // ... 其他信息隐藏了 -}) -export class FormRenderer extends React.Component { - // ... 其他信息隐藏了 - render() { - const { - title, - controls, - render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。 - } = this.props; - return ( - - {controls.map((control, index) => ( -
- {render(`${index}/control`, control)} -
- ))} -
- ); - } -} -``` - -Text 组件的示例代码 - -```jsx -@Renderer({ - test: /(^|\/)form(?:\/\d+)?\/control(?\/\d+)?\/text$/ - // ... 其他信息隐藏了 -}) -export class FormItemTextRenderer extends React.Component { - // ... 其他信息隐藏了 - render() { - const { - label, - name, - onChange - } = this.props; - return ( -
-
- ); - } -} -``` - -那么渲染过程就是根据节点 path 信息,跟组件池中的组件 `test` (检测) 信息做匹配,如果命中,则把当前节点转给对应组件渲染,节点中其他属性将作为目标组件的 props。需要注意的是,如果是容器组件,比如以上例子中的 `page` 组件,从 props 中拿到的 `body` 是一个子节点,由于节点类型是不固定,由使用者决定,所以不能直接完成渲染,所以交给属性中下发的 `render` 方法去完成渲染,`{render('body', body)}`,他的工作就是拿子节点的 path 信息去组件池里面找到对应的渲染器,然后交给对应组件去完成渲染。 - -### 编写自定义组件 - -了解了基本原理后,来看个简单的例子: +首先需要了解「[基本原理](internal)」,了解了基本原理后,来看个简单的例子: ```jsx import * as React from 'react'; @@ -330,6 +64,14 @@ class CustomRenderer extends React.Component { } ``` +> 上面这个语法需要开启 Decorator 功能,如果不支持,可以改成如下写法 + +```javascript +Renderer({ + test: /(^|\/)my\-renderer$/ +})(CustomRenderer); +``` + 有了以上这段代码后,就可以这样使用了。 ```json diff --git a/docs/extend/custom-sdk.md b/docs/extend/custom-sdk.md new file mode 100644 index 000000000..bf74183a6 --- /dev/null +++ b/docs/extend/custom-sdk.md @@ -0,0 +1,141 @@ +--- +title: 自定义组件 - SDK +--- + +## 使用 custom 组件临时扩展 + +基于 custom 组件可以直接在 amis 配置实现自定义功能,它的支持面最广,是唯一支持在可视化编辑器中使用的方法。 + +使用 custom 组件类似如下写法: + +```javascript +{ + label: '使用 custom 组件', + name: 'username', // 如果要放在 form 中,需要设置 name,onChange 将会设置这个值 + type: 'custom', + // onMount 将会在组件创建时执行,默认会创建一个空 div 标签,也可以设置 inline: true 来创建 span 标签 + // dom 是 dom 节点,value 是初始数据,比如表单 name 初始拿到的数据,onChange 只有在表单下才会有 + onMount: (dom, value, onChange) => { + const button = document.createElement('button'); + button.innerText = '点击修改姓名'; + button.onclick = event => { + onChange('new name'); + event.preventDefault(); + }; + dom.appendChild(button); + }, + // onUpdate 将会在数据更新时被调用 + // dom 是 dom 节点、data 将包含表单所有数据,prevData 是之前表单的所有数据 + onUpdate: (dom, data, prevData) => { + console.log('数据有变化', data); + }, + // onUnmount 将会在组件被销毁的时候调用,用于清理资源 + onUnmount:() => { + console.log('组件被销毁'); + } +} +``` + +注意上面的代码用到了 JavaScript 函数,无法转成 json 格式,但这三个函数还支持字符串形式,上面的代码可以改成如下形式,这样就能在可视化编辑器里支持自定义组件了: + +```schema:height="330" scope="body" +{ + "type": "form", + "title": "custom 组件", + "controls": [ + { + "type": "text", + "name": "username", + "label": "姓名" + }, + { + "name": "username", + "type": "custom", + "label": "自定义组件", + "onMount": "const button = document.createElement('button'); button.innerText = '点击修改姓名'; button.onclick = event => { onChange('new name'); event.preventDefault(); }; dom.appendChild(button);" + } + ] +} +``` + +注意上面的例子中两个组件的 name 是一样的,这是为了方便示例,因为 amis 中的数据是双向绑定的,因此 onChange 修改自身的时候,另一个「姓名」输入框由于 name 一样,也会同步更新。 + +关于 custom 组件的更多属性请参考「[Custom 组件](../components/custom)」。 + +## JS SDK 注册组件 + +amis 组件都是基于 React 的,所以需要使用一个简单的 React 组件来注册,可以是函数组件也可以是类组件,下面以函数组件为例,将[快速开始](../start/getting-started)中的代码替换成如下示例: + +```javascript +let amis = amisRequire('amis/embed'); +let amisLib = amisRequire('amis'); +let React = amisRequire('react'); + +// 自定义组件,props 中可以拿到配置中的所有参数,比如 props.label 是 'Name' +function CustomComponent(props) { + let dom = React.useRef(null); + React.useEffect(function () { + // 从这里开始写自定义代码,dom.current 就是新创建的 dom 节点 + // 可以基于这个 dom 节点对接任意 JavaScript 框架,比如 jQuery/Vue 等 + dom.current.innerHTML = 'custom'; + // 而 props 中能拿到这个 + }); + return React.createElement('div', { + ref: dom + }); +} + +//注册自定义组件,请参考后续对工作原理的介绍 +amisLib.Renderer({ + test: /(^|\/)my-custom/ +})(CustomComponent); + +let amisScoped = amis.embed('#root', { + type: 'page', + title: '表单页面', + body: { + type: 'form', + mode: 'horizontal', + api: '/saveForm', + controls: [ + { + label: 'Name', + type: 'my-custom', // 注意这个的 type 对应之前注册的 test + name: 'custom' + } + ] + } +}); +``` + +### 示例:引入 Element UI + +首先在页面中加入 Element UI 所需的依赖 + +```html + + + +``` + +然后将前面的 `React.useEffect` 改成如下即可: + +```javascript +React.useEffect(function () { + dom.current.innerHTML = ` + Button + +

Try Element

+
+ `; + new Vue({ + el: dom.current, + data: function () { + return {visible: false}; + } + }); +}); +``` diff --git a/docs/extend/i18n.md b/docs/extend/i18n.md new file mode 100644 index 000000000..399e25483 --- /dev/null +++ b/docs/extend/i18n.md @@ -0,0 +1,83 @@ +--- +title: 多语言 +--- + +amis 内置对英文的支持,同时你也可以扩展其他语言。 + +## JS SDK + +从 1.1.0 版本开始已经自带英文翻译,所以只需要在 props 里设置 locale 即可。 + +```javascript +let amisScoped = amis.embed( + '#root', + { + type: 'page', + title: '表单页面', + body: { + type: 'form', + mode: 'horizontal', + api: '/saveForm', + controls: [ + { + label: 'Name', + type: 'text', + name: 'name' + } + ] + } + }, + { + locale: 'en' + } +); +``` + +## React + +React 中没有内置英文版本,需要自己 import,使用如下方法: + +```javascript +import 'amis/lib/locale/en'; +``` + +在渲染 amis 组件的时候设置 locale 为 en + +```javascript +{ + renderAmis( + { + type: 'page', + title: '简单页面', + body: '内容' + }, + { + locale: 'en' + } + ); +} +``` + +## 扩展其它语言 + +如果想扩展其他语言,首先参考 `https://github.com/baidu/amis/blob/master/src/locale/en.ts` 文件,了解需要翻译哪些文字,以中文为 key,然后参考后面的示例注册新语言,未翻译的文字都将使用默认语言,即中文。 + +> 目前这种方式将会在未来修改,为了支持更多语言而不再使用中文为 key + +### JS SDK 扩展方法 + +```javascript +let amisLib = amisRequire('amis'); +amisLib.registerLocale('jp', { + 提交: '送信' +}); +``` + +### React 扩展方法 + +```javascript +import {registerLocale} from 'amis'; +registerLocale('jp', { + 提交: '送信' +}); +``` diff --git a/docs/extend/internal.md b/docs/extend/internal.md new file mode 100644 index 000000000..11dfb0729 --- /dev/null +++ b/docs/extend/internal.md @@ -0,0 +1,151 @@ +--- +title: 工作原理 +--- + +实现自定义类型需要了解 amis 的工作原理。 + +## 工作原理 + +amis 的渲染过程是将 `json` 转成对应的 React 组件。先通过 `json` 的 type 找到对应的 `Component` 然后,然后把其他属性作为 `props` 传递过去完成渲染。 + +拿一个表单页面来说,如果用 React 组件开发一般长这样。 + +```jsx + +
+ +``` + +把以上配置方式换成 amis JSON, 则是: + +```json +{ + "type": "page", + "title": "页面标题", + "subTitle": "副标题", + "body": { + "type": "form", + "title": "用户登录", + "controls": [ + { + "type": "text", + "name": "username", + "label": "用户名" + } + ] + } +} +``` + +那么,amis 是如何将 JSON 转成组件的呢?直接根据节点的 type 去跟组件一一对应?这样会重名,比如在表格里面展示的类型 `text` 跟表单里面的 `text` 是完全不一样的,一个负责展示,一个却负责输入。所以说一个节点要被什么组件渲染,还需要携带上下文(context)信息。 + +如何携带上下文(context)信息?amis 中是用节点的路径(path)来作为上下文信息。从上面的例子来看,一共有三个节点,path 信息分别是。 + +- `page` 页面节点 +- `page/body/form` 表单节点 +- `page/body/form/controls/0/text` 文本框节点。 + +根据 path 的信息就能很容易注册组件跟节点对应了。 + +Page 组件的示例代码 + +```jsx +import * as React from 'react'; +import {Renderer} from 'amis'; + +@Renderer({ + test: /^page$/ + // ... 其他信息隐藏了 +}) +export class PageRenderer extends React.Component { + // ... 其他信息隐藏了 + render() { + const { + title, + body, + render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。 + } = this.props; + return ( +
+

{title}

+
+ {render('body', body) /*渲染孩子节点*/} +
+
+ ); + } +} + +// 如果不支持 Decorators 语法也可以使用如下写法 +export Renderer({ + test: /^page$/ +})(class PageRenderer extends React.Component { + render() { + // ...同上 + } +}) +``` + +Form 组件的示例代码 + +```jsx +@Renderer({ + test: /(^|\/)form$/ + // ... 其他信息隐藏了 +}) +export class FormRenderer extends React.Component { + // ... 其他信息隐藏了 + render() { + const { + title, + controls, + render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。 + } = this.props; + return ( + + {controls.map((control, index) => ( +
+ {render(`${index}/control`, control)} +
+ ))} +
+ ); + } +} +``` + +Text 组件的示例代码 + +```jsx +@Renderer({ + test: /(^|\/)form(?:\/\d+)?\/control(?\/\d+)?\/text$/ + // ... 其他信息隐藏了 +}) +export class FormItemTextRenderer extends React.Component { + // ... 其他信息隐藏了 + render() { + const { + label, + name, + onChange + } = this.props; + return ( +
+
+ ); + } +} +``` + +那么渲染过程就是根据节点 path 信息,跟组件池中的组件 `test` (检测) 信息做匹配,如果命中,则把当前节点转给对应组件渲染,节点中其他属性将作为目标组件的 props。需要注意的是,如果是容器组件,比如以上例子中的 `page` 组件,从 props 中拿到的 `body` 是一个子节点,由于节点类型是不固定,由使用者决定,所以不能直接完成渲染,所以交给属性中下发的 `render` 方法去完成渲染,`{render('body', body)}`,他的工作就是拿子节点的 path 信息去组件池里面找到对应的渲染器,然后交给对应组件去完成渲染。 diff --git a/docs/start/faq.md b/docs/start/faq.md index 39d27b2a6..326b360f8 100644 --- a/docs/start/faq.md +++ b/docs/start/faq.md @@ -2,7 +2,7 @@ title: 常见问题 --- -### 如何实现左侧导航栏页面跳转? +## 如何实现左侧导航栏页面跳转? 为了能更容易嵌入其它平台,amis 只负责单页面渲染,不接管前端路由,因此无法只靠 amis 配置实现多页面切换功能,推荐使用以下几种方法: @@ -10,6 +10,13 @@ title: 常见问题 2. 使用传统的页面跳转,这样就能使用 amis 的 aside,其中通过 link 类型来跳转到另一个页面。 3. 使用「[爱速搭](http://suda.baidu.com/)」,它可以配置左侧导航,还自带了权限管理等功能。 -### 集成到 React 项目中报错 +## 集成到 React 项目中报错 一般都是因为 React、Mobx、mobx-react 版本有关,参考 amis 项目的 [package.json](https://github.com/baidu/amis/blob/master/package.json),将版本保持一致,尤其是 Mobx,目前 amis 中使用的版本是 4,因为兼容性的考虑短期内不会升级到 5,使用 MobX 5 肯定会报错。 + +## 有的功能在官网示例中能用,但在 React/SDK 中无法使用 + +amis 大概每个月发布一个正式版本,但官网是 master 分支的版本,因此很可能是正式版本未更新。 + +- React 可以使用最新 beta 版本。 +- SDK 可以手动编译一个,下载开源项目源码后运行 `fis3 release publish-sdk -c`,然后在 sdk 目录就能找到。 diff --git a/docs/start/getting-started.md b/docs/start/getting-started.md index d4d0d8c7c..0a3df5ee6 100644 --- a/docs/start/getting-started.md +++ b/docs/start/getting-started.md @@ -5,10 +5,8 @@ description: amis 有两种使用方法: -- [JS SDK](#SDK) -- [React](#react) - -React 版本可以完整使用 amis 的所有功能。 +- [JS SDK](#SDK),可以用在任意项目中 +- [React](#react),可以用在 React 项目中 SDK 版本适合对前端或 React 不了解的开发者,它不依赖 npm 及 webpack,可以像 Vue/jQuery 那样外链代码就能使用。 @@ -33,9 +31,9 @@ JSSDK 版本可以在 github 的 [releases](https://github.com/baidu/amis/releas /> - - + +