@schema-plugin-flow/sifo-react
TypeScript icon, indicating that this package has built-in type declarations

1.6.0 • Public • Published

sifo-react

sifo-react 是组装了sifo-modelsifo-singleton 的一个React组件。

sifo-react 以 sifo-model 为内核,使用插件式的开发模式,为页面提供了多级定制与扩展能力。包含但不限于:页面结构的修改、渲染组件的替换、组件属性的变更、组件事件的监听与阻断等。结合不同的模型插件,可以实现更加丰富的特定功能。

  • 插件示例

codesandbox.io

SifoApp (sifo-react) Props

参数 说明 类型 是否必传 默认值
namespace 命名空间,这是一个功能集合的主要标识,第三方将根据命名空间来进行功能扩展 string -
schema schema,描述了页面结构 object -
components 组件 object {}
plugins 插件,分为模型插件、页面插件和组件插件 array:[{ componentPlugin, pagePlugin, modelPlugin }, { modelPlugin: otherModelPlugin }] []
externals 任意其它信息 object {}
sifoExtProps 任意对象,mApi.getSifoExtProps 可以取到即时的值,这点与 externals 相区别 object {}
modelApiRef 模型接口外传方法,调用参数为 mApi(接口构建完成时) 或 null(模型销毁时) function
openLogger 是否在控制台打印出执行日志,不建议在生产环境使用 bool false
getModelPluginArgs 获取模型插件实例化时的构造函数参数 function:(modelPluginId, info) => ([arg1, arg2, ...])
className 样式类 string

扩展的 mApi 模型接口

mApi说明

方法名 参数/类型 返回值类型 描述
getSifoExtProps 任意对象 获取 SifoApp.sifoExtProps 即时的值

如何使用

  • 项目

    • extend.js

      import SifoSingleton from '@schema-plugin-flow/sifo-singleton';
      const singleton = new SifoSingleton('quick-start'); // target namespace
      singleton.registerItem('testExtendId', () => {
        return {
          plugins,
          components
        }
      });
    • app.js

      import React from 'react';
      import ReactDOM from "react-dom";
      import SifoApp from '@schema-plugin-flow/sifo-react';
      class App extends React.Component {
        render() {
          return (
            <SifoApp
              namespace='quick-start'
              components={components}
              schema={schema}
              plugins={plugins}
            />
          );
        }
      }
      ReactDOM.render(
        <App />
        rootElement
      );
  • runtime

    • load extend js

    • load app js

      你应该在 sifoApp 渲染前加载扩展 js 资源

      <script src="extend.js"></script>
      <script src="app.js"></script>

QuickStart

下面的例子演示了如何监听一个按钮组件的点击事件,并在点击事件中修改其它组件的属性,同时也演示了多个插件的情形。想了解更多的功能请参考sifo-model

import React from 'react';
import SifoApp from '@schema-plugin-flow/sifo-react';
// 一些组件
const Container = props => <div {...props} />;
const Slogan = ({ content, ...other }) => <h2 {...other}>{content}</h2>;
const Button = props => <button {...props}>click to change</button>;
// schema 定义了初始的页面结构
const schema = {
  component: "Container",
  id: 'mainId',
  attributes: {},
  children: [
    {
      component: "Slogan",
      id: 'slogan_id',
      attributes: {
        content: 'hello world'
      }
    },
    {
      component: "Button",
      id: 'test_btn_id',
      attributes: {}
    }
  ]
};
// 组件插件可以实现与组件相关的功能
const componentPlugin1 = {
  test_btn_id: {
    onComponentInitial: params => {
      const { event, mApi } = params;
      mApi.addEventListener(event.key, 'onClick', (context, e) => {
        mApi.setAttributes('slogan_id', {
          content: 'hello sifo'
        });
      })
    }
  }
};
// 第二个插件
const componentPlugin2 = {
  test_btn_id: {
    onComponentInitial: params => {
      const { event, mApi } = params;
      mApi.addEventListener(event.key, 'onClick', () => {
        console.log('test_btn_id clicked!')
      })
    }
  }
};
const components = { Container, Slogan, Button };
const plugins = [
  { componentPlugin: componentPlugin1 },
  { componentPlugin: componentPlugin2 }
];
class App extends React.Component {
  render() {
    return (
      <SifoApp
        className='quick-start'
        namespace='quick-start'
        components={components}
        schema={schema}
        plugins={plugins}
        openLogger={false}
      />
    );
  }
}
export default App;

外部扩展

如果一个页面是用 sifo 开发的,开发者可以在不接触原始代码的情况下,对页面进行扩展。这里用到了 sifo-singleton 全局扩展容器,只要在目标页面渲染前载入了扩展插件、组件,扩展功能就会在目标页面上生效。

import SifoSingleton from '@schema-plugin-flow/sifo-singleton';
const singleton = new SifoSingleton('test_namespace');// 对目标命名空间进行扩展
// 插件的功能与使用跟前面的示例完全一致
const plugins = [{ pagePlugin, componentPlugin }];
const components = {};
singleton.registerItem('testExtendId', () => {
  return {
    plugins,
    components,
    openLogger: true, 
  }
});

sifoAppDecorator

为一个组件追加扩展能力时,可用修饰器方式。sifoAppDecorator 第一个参数是命名空间,第二个参数与上文的“SifoApp参数”一致(namespace 和 schema 除外)。此外还增加了如下参数:

参数 说明 类型 是否必传 默认值
fragments 片段列表。片段可以只定义一个id,通过 getFragment 方法获取片段来渲染;也可以传一个 schema,以 schema 的第一层 id 来标识 array -

被 sifoAppDecorator 修饰的组件,props 中将出现 sifoApp 对象,对象包含 addEventListener、 watch、getFragment 等方法和 mApi 接口。

sifoAppDecorator 示例

下面的示例包含:

  • View 组件标注命名空间为 main_namespace;
  • View 组件向外暴露 onSubmit、setState、onChange 事件,扩展件就可以监听与干预这些事件;
  • View 注册了 getState、setState 观测,扩展件可以发布相应观测消息来与 View 通信;
  • View 定义了 $header 片段,以使扩展件可以在页面指定位置渲染内容。 完整示例请参照这里
import React from 'react';
import { sifoAppDecorator } from '@schema-plugin-flow/sifo-react';
@sifoAppDecorator('main_namespace', {
  fragments: ['$testFragment', '$fragment2', innerSchema],
  components: {},
  plugins: [],
  openLogger: true
})
class View extends React.Component {
  constructor(props) {
    super(props);
    const { sifoApp } = props;
    this.state = { value: '' };
    // 加入事件监听,这些事件实际上是挂在了以当前命名空间为id的schema节点上
    this.onSubmit = sifoApp.addEventListener('onSubmit', this.onSubmit);
    this.setState = sifoApp.addEventListener('setState', this.setState.bind(this));
    // prepose传入true可使事件先于扩展件注册,在希望外部能够覆盖(扩展)内部方法时可使用
    this.onChange = sifoApp.addEventListener('onChange', this.onChange, true); 
    // 注册观测任务
    sifoApp.watch('getState', (context, getter) => {
      getter(this.state);
    });
    sifoApp.watch('setState', (context, state) => {
      this.setState({
        ...state
      });
    });
  }
  onSubmit = () => {
    //...
  }
  onChange = () => {
    //...
  }
  render() {
    // 将声明的片段放到指定的位置,扩展插件就可以在相应位置渲染自定义的内容了
    const testFragment = this.props.sifoApp.getFragment('$testFragment');
    // 动态传参
    const testFragment2 = this.props.sifoApp.getFragment('$fragment2', {
      stateValue: this.state.value, others: {}
    });
    return (
      <Comp
        onChange={this.onChange}
        onSubmit={this.onSubmit}
      >
        {testFragment}
        {testFragment2}
      </Comp>
    )
  }
}

sifoAppDecorator 下的外部扩展示例

import React from 'react';
import SifoSingleton from '@schema-plugin-flow/sifo-singleton';
// 
const pagePlugin = {
  onNodePreprocess: (node, info) => {
    const { id, component } = node;
    // sifoAppDecorator 内置了 $sifo-header 和 $sifo-footer 两个节点
    if (id === '$sifo-header') {
      return {
        ...node,
        attributes: {},
        children: ['this is ext-header']
      }
    }
    if (id === '$sifo-footer') {
      return {
        ...node,
        children: ['this is ext-footer']
      }
    }
    if (id === '$fragment2') {
      // 将片段直接换成新的组件,这个组件就可以拿到getFragment的参数
      return {
        ...node,
        component: 'DynamicPropsCom'
      }
    }
    if (id === '$testFragment') {
      return {
        ...node,
        children: [
          {
            component: 'Input',
            id: 'custom'
          }
        ]
      }
    }
    return node;
  },
}
const componentPlugin = {
  // 命名空间认作根节点id
  main_namespace: {
    onComponentInitial: params => {
      const { event, mApi } = params;
      mApi.addEventListener(event.key, 'onSubmit', (context, e) => {
      });
      mApi.addEventListener(event.key, 'onChange', (context, e) => {
        mApi.dispatchWatch('setState', { testState: 'test' })
      });
      mApi.addEventListener(event.key, 'setState', (e, state) => {
        const nextState = {
          ...state,
          customState: new Date().getMilliseconds()
        };
        e.event.next(nextState);
      });
    }
  },
  custom: {
    onComponentInitial: params => { }
  }
};
const singleton = new SifoSingleton('main_namespace');
singleton.registerItem('this_ext_id', () => {
  return {
    plugins: [
      {
        pagePlugin,
        componentPlugin
      }
    ]
  }
});
//
export default {};

在函数式组件上使用

import React from 'react';
import { sifoAppDecorator } from "@schema-plugin-flow/sifo-react";
const TestFnDecorator = props => {
    const { sifoApp } = props;
    console.log('render ---- mApi instance:', sifoApp.mApi.instanceId);
    const dynamicPanel = sifoApp.getFragment('$temp_panel', { 
      stateValue: '函数式',
      others: { ok: true } 
    });
    return (
      <div>
      <h3>函数式组件</h3>
        { dynamicPanel }
      </div>
    );
}
const App = sifoAppDecorator('test-sifo-decorator', {
  externals: { aa: 1 },
  fragments: ['$temp_panel'],
  className: "decorator-fn-test",
  openLogger: true
})(TestFnDecorator);
export default App;

非标准的组件接入

const TestComponent = {
  name: "TestNonStandard", // 组件名
  useSifoRenderProxy: true, // 标注组件需要使用代理
  /**
   * 渲染方法
   * target: dom对象
   * props: 属性
   * update: 是否是更新
   * preInstance: 前一个render调用返回的对象(更新时)
   */
  render: (target, props, update, preInstance) => {
    // Svelte 组件
    // if (!update) {
    //   return renderTest(props, props.children, target); // 返回instance
    // } else {
    //   preInstance.update(props);
    //   return preInstance;
    // }
    ReactDOM.render(React.createElement(Demo, props), target); // react
  },
  /**
   * 卸载方法,如果不传,默认调用 ReactDOM.unmountComponentAtNode(target);
   */
  unmount: (target, instance) => {
    // instance.unmount(target);
    ReactDOM.unmountComponentAtNode(target); // react
  }
  /**
   * 渲染类型:normal 指在每次react节点渲染时调用render方法,rerenderKey 指在 props.rerenderKey 变化时才调用render方法,默认为 normal,也可以在 props.rerenderType 中指定渲染类型。
   */
  rerenderType: "normal",// "normal"|"rerenderKey"
}

模型插件推荐列表

Package Sidebar

Install

npm i @schema-plugin-flow/sifo-react

Weekly Downloads

2

Version

1.6.0

License

MIT

Unpacked Size

67.3 kB

Total Files

18

Last publish

Collaborators

  • fromin