This article introduces sifoAppDecorator, a highly extensible decorator in the React/Vue framework. The main capabilities of the decorator include:

  • A. Add render snippets to the target component header and tail (basic)
  • B. Embed render fragments inside the target component (advanced)
  • C. Listening and intervening target component methods and events (advanced)
  • D. Communicate with target components, such as component state changes (advanced)
  • E. Contactless expansion and secondary development capability (advanced)

Also, some might wonder: why React/Vue? The reason is simple: React is almost the same as Vue!

What is a decorator

To put it simply, a decorator is essentially a function that decorates a target class to give the target specific capabilities. The effect of modifiers on classes is determined at compile time, at the code level. At run time, embellished content can be applied to individual class instances. A simple example:

@testable
class MyTestableClass {
  // ...
}
function testable(target) {
  target.isTestable = true;
}
MyTestableClass.isTestable // true
Copy the code

Third, sifoAppDecorator

That’s right, this is section 3, and since this article is about sifoappdecorators, I’ll leave the decorators up front. SifoAppDecorator is a wrapper around Sifo, alibaba’s open source, highly extensible, divisible plug-in front-end development framework. Sifo is behind the decorator’s capabilities, and section 2 gives you a basic introduction.

You can learn about the basic functionality and usage of this modifier in this section before delving into the more powerful Sifo. (Much like you, who are binge-watching new episodes but haven’t yet arrived to watch the previous one)

The article will be presented in the order of the A-E hierarchy of sifoAppDecorator capabilities — basic, advanced, and advanced.

Take a look at a simple Demo (Vue) at the end of the article with links to experience React and Vue online

Basis of article 3.1

3.1.1 sifoAppDecorator parameters

The sifoAppDecorator parameter is basically the same as the Sifo parameter (except for the fragments parameter), but these parameters are explained here so that the reader can understand what the Sifoappator parameter corresponds to. Other parameters that are not listed can be referenced in the Sifo documentation.

  • namespace
    • Namespace, the first argument to the sifoAppDecorator, is of type string. The namespace is a globally unique identifier for the current SIFO target.
  • fragments
    • Fragments. Capabilities A and B above refer to render fragments, which are used to specify which fragments are present. The type is typically an array of fragment keys (string) :[ 'fragmentKey1', 'fragmentKey2' ]. The header and tail segments of the target component are built-in and do not need to be specified. The embedded fragment is explicitly specified when a decorator is added, and the target component will pick up the fragment by the fragment key and render it to the desired location. Developers have logical control over fragments (specifying rendering components, binding properties, events, and so on).
  • components
    • Component, why is there a component parameter? Because fragments can be a JSON description (in Sifo)schemaWe’ll call them schema fragments for now. We’ll call them string fragments instead. Because a String fragment is just an identifier, you cannot qualify the content of the fragment, whereas a Schema fragment is a fragment that specifies the structure of the content. Therefore, provide the component objects specified in the schema as well.
  • plugins
    • Plug-ins, the logical control “binding properties and events” (corresponding capabilities C and D) are written in plug-ins, which is an important feature of Sifo. There are three types of plug-ins in Sifo: model plug-in, page plug-in and component plug-in. Each type of plug-in can realize rich functions, which will not be expanded here.

The schema format is as follows:

{
  "id": "id001".// The node id is unique and cannot be repeated. Plug-ins use this ID to distinguish nodes.
  "component": "div"."children": [{"component": "InputComponent"."id": "id002"."attributes": {
        "value": "val01"
      },
      "children": []}]}Copy the code

In addition, all declared fragment keys in the decorator schema will be the IDS of the children node of the schema’s first tier (the first tier node ID is the namespace).

3.1.2 Basic Usage

  • React basic usage

    • Class components
    import { sifoAppDecorator } from "@schema-plugin-flow/sifo-react";
    @sifoAppDecorator('test-sifo-decorator', {
      components,
      plugins,
      fragments: ['$header', innerSchema, '$temp_panel'.'$body'],})class TestDecorator extends React.Component {}export default TestDecorator;
    Copy the code
    • Functional components, using higher-order functional form
    const TestFnDecorator = props= > (<>.</>);
    const App = sifoAppDecorator('test-sifo-decorator', {
      fragments: ['$temp_panel']
    })(TestFnDecorator);
    export default App;
    Copy the code
  • Basic usage under Vue

    Since normal Vue components do not use class syntax, but component objects, they also use higher-order functional form.

    import { sifoAppDecorator } from "@schema-plugin-flow/sifo-vue";
    import TestDecorator from './test-decorator-target.vue';
    const App = sifoAppDecorator('test-sifo-decorator', {
      components,
      plugins,
      fragments: ['$dynamic_panel'.'$static_panel']
    })(TestDecorator);
    export default App;
    Copy the code

3.1.3 Add render fragments to the header and tail of the target component

3.1.2 deals with the use of the modifier target (the modifier). What about the modifier (the enabler of the ability, as we will use this name later)? Remember the plugins parameter? Yes, we can write plug-ins to control fragments.

This section only covers the basics of adding render fragments to the header and tail of the target component. This requires a page plug-in that has a Schema preprocessing cycle.

const pagePlugin = {
  onNodePreprocess: (node) = > {
    if (node.id === '$sifo-header') {
      return {
      	...node,
        component: 'div'.children: ['This is a header fragment.']}}}}const plugins = [{ pagePlugin }];
Copy the code

$sifo-header and $sifo-footer are the built-in header fragment keys. In the example above, the page plug-in sets the header fragment contents: a div and a “this is a header fragment” copy.

This could be any other component, registering the required components in the components parameter:

const components = {
  AnyComp: HelloWorld,
}
Copy the code

Then change the div in pagePlugin to the corresponding component name:

{
  component: 'AnyComp'.attributes: {
    anyProps: 'any'}}Copy the code

Of course, you can also perform props property controls on a given component, as discussed in further sections.

3.2 advanced

The advanced part will cover the following abilities:

  • B. Embed render fragments inside the target component (advanced)
  • C. Listening and intervening target component methods and events (advanced)
  • D. Communicate with target components, such as component state changes (advanced)

3.2.1 sifoApp properties

We need to start with the sifoApp property, which is an object injected by the sifoAppDecorator into the props of the target component. In the React component, this.props. SifoApp is invoked. In the Vue component, props needs to declare sifoApp and this.

SifoApp provides a number of methods, such as:

  • getFragment
    • Get render snippets, basic usage:sifoApp.getFragment("$static_panel")
  • addEventListener
    • Register method, event listener, basic usage:sifoApp.addEventListener("click", handler)
  • watch
    • Observations, which can be customized, can be triggered externally. Basic usage:sifoApp.watch("updateData", watchHandler)

SifoApp also contains mApi objects, and the methods listed above also end up calling mApi. MApi is Sifo’s main interface object and provides many other capabilities that are not covered here.

3.2.2 Embed render fragments inside the target component

Sifoapp. getFragment is called to get the render fragment and place it in the specified location.

  • Written in the modified way

    • React
    class TestDecorator extends Component {
      render() {
        const headFragment = this.props.sifoApp.getFragment('$header');
        const dynamicPanel = this.props.sifoApp.getFragment('$temp_panel', { 
          stateValue: this.state.value
        });
         return (
           <div>{headFragment} <... /> {dynamicPanel}</div>); }}Copy the code
    • Vue
    <template>
      <div>
        <div>
          <component v-bind:is="staticFragment"></component>
        </div>
        <div>
          <component v-bind:is="getDynamicFragment()"></component>
        </div>
      </div>
    </template>
    <script>
      const TestDecorator = {
        name: "decorator-test".data: function () {
          return {
            count: 0.staticFragment: this.sifoApp.getFragment("$static_panel"),}; },methods: {
          getDynamicFragment: function () {
            return this.sifoApp.getFragment("$dynamic_panel", {
              value: this.count, }); }},props: ["sifoApp"]};export default TestDecorator;
      </script>
    Copy the code
  • Modifiers

    Modifiers implement the corresponding fragment function, the simplest of which is what the pagePlugin above does:

    const pagePlugin = {
      onNodePreprocess: (node) = > {
        if (node.id === '$dynamic_panel') {
          return {
            ...node,
            component: 'MyInput'}}}}const plugins = [{ pagePlugin }];
    const components = { MyInput };
    Copy the code

    MyInput can be a component that doesn’t care about props, and if you need to manage the component’s props, for example, by adding a property or a value change event, you can use the component plug-in:

    const componentPlugin = {
        // schema node ID, here is the fragment key
        $dynamic_panel: {
          onComponentInitial: params= > {
            const { event, mApi } = params;
            mApi.setAttributes(event.key, {
              placeholder: 'this is a input'
            });
            // The convention for event names is lowercase under Vue, and onChange under React, as the component props
            mApi.addEventListener(event.key, 'change'.(ctx, e) = >{
              mApi.setAttributes(event.key, { 
                value: e.target.value + 'extVal'}); }); }}},const plugins = [{ pagePlugin, componentPlugin }];
    Copy the code

3.2.3 Monitor and intervene target component methods and events

The decorator registers the listening event or method using sifoapp.addeventListener. The decorator can also listen and intervene, such as adding response actions, modifying return values, blocking events, and so on.

  • Party being modified

    • React
    class TestDecorator extends React.Component {
      constructor(props) {
        super(props);
        const { sifoApp } = props;
        this.onClick = sifoApp.addEventListener('onClick'.this.onClick);
      }
      onClick = () = > {
        console.log("target: clicked"); }}Copy the code
    • Vue
    {
      created: function () {
        this.clickFn = (. args) = > {
          console.log("target: clicked");
        };
        this.clickFn = this.sifoApp.addEventListener("click".this.clickFn);
      },
      methods: {
        click: function (. args) {
          this.clickFn(... args); }}}Copy the code
  • Modify the

    On a component plug-in with a key namespace, listen for methods with the same name

    const componentPlugin = {
      // The event listens on the namespace id
      'test-sifo-decorator': {
        onComponentInitial: params= > {
          const { event, mApi } = params;
          let fcount = 0;
          mApi.addEventListener(event.key, 'click'.(context, e) = > {
            mApi.setAttributes('$static_panel', {
              value: `ext click fired: ${++fcount}`}); }); }}}Copy the code

3.2.4 Communicate with the target component, for example, change the component state

With sifoapp. watch you can customize an observation event that is triggered when an external call to mapi.dispatchwatch is made.

  • Party being modified
    sifoApp.watch("updateData".(ctx, data) = > {
      console.log('data', data);
    });
    Copy the code
  • Modify the
    mApi.dispatchWatch('updateData', data);
    Copy the code

3.3 senior post

Here we go. Remember the plugins and components parameters passed to sifoAppDecorator?

They don’t have to be passed in where you put the modifier.

Yes, they can be placed completely in a separate file, js packaging and loading independently! That’s it: contactless expansion and secondary development.

3.3.1 Contactless expansion and secondary development

If you look at the official sample code, you’ll see that the decorator implementations in the sample are not passed in as arguments to sifoAppDecorator, but instead have a separate ext.js file.

This relies heavily on sifo-Singleton’s ability to:

If a page is developed using SIFO, developers can extend the page without touching the original code. The sifo-Singleton global extension container is used here, and the extension functionality will take effect on the target page as long as the extension plug-ins and components are loaded before the target page is rendered.

The usage method is as follows:

import SifoSingleton from '@schema-plugin-flow/sifo-singleton';
// Extend the target namespace
const singleton = new SifoSingleton('test-sifo-decorator');
// The functionality of the plug-in is exactly the same as using the previous example
const plugins = [{ pagePlugin, componentPlugin }];
const components = { AnyComp: HelloWorld, MyInput };
const extItemId = 'testExtendId';
// Multiple extensions can be registered to ensure that the extItemId is different
singleton.registerItem(extItemId, () = > {
  return {
    plugins,
    components,
    openLogger: true,}});Copy the code

End of advanced section!

Ii. Sifo foundation

Sifo is a highly extensible and divisible plug-in front-end development framework. This article is already full of modifiers, so leave them blank (see reader feedback). For more information about Sifo’s features and capabilities, see the links at the end of this article.

Four, other

Discussion of use scenarios for modifiers

The sifoAppDecorator decorator encapsulates Sifo, which is designed to address customer customization and support extended customization and secondary development. Such requirements are unpredictable, cannot be fully covered by the business, and are not suitable for coupling into the original business code or they will result in code clutter. This modifier is also useful for solving this problem — it provides extensibility on standard services, but it is up to the extender to decide whether and how to implement it.

Sifo making address

Sifo: Configurable and highly extensible forms siFO: Configurable and highly extensible forms