Highly extensible React/Vue decorator you must have never seen

Highly extensible React/Vue decorator you must have never seen

  • This article has obtained the exclusive authorization of the original author, friends who want to reprint can contact me in the background to apply for openness!
  • PS: Welcome friends to contribute to me, and the adopted articles can also send you exquisite surroundings!

This article will introduce a highly scalable decorator under the React/Vue framework-sifoAppDecorator. The main capabilities of this decorator include:

  • a. Add rendering fragments at the head and tail of the target component (basic)
  • b. Embed rendering fragments inside the target component (advanced)
  • c. Monitoring and intervention of target component methods and events (advanced)
  • d. Communicate with target components, such as component state changes (advanced)
  • e. Non-contact expansion and secondary development capabilities (advanced)

In addition, some people may wonder: Why do you talk about it together under the two frameworks of React/Vue? The reason is simple: the usage under React and Vue is almost the same!

1. What is a decorator

Simply put, a decorator is essentially a function that modifies the target class (this is not nonsense), and is used to give the target a specific ability. The influence of the decorator on the class is determined at compile time, that is, the code layer. At runtime, the modified content can play a role in each class instance. A simple example:

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

3. sifoAppDecorator

That's right, this is the third section, because this article is about sifoAppDecorator, so I mentioned the decorator related to the front. sifoAppDecorator is an encapsulation of Sifo's high-extensibility development framework. Sifo is the "behind the scenes" of the modifier capabilities. The second section will introduce Sifo.

Readers can choose to learn the basic functions and usage of this modifier from this section first, and then go to learn more about the more powerful Sifo. (It’s like chasing the new drama, but you haven’t come to watch the previous season in a hurry)

The article will be described in the order of sifoAppDecorator's ability ae-basic, advanced and advanced.

Let’s take a look at a simple Demo effect (Vue). At the end of the article, there are links to the online experience of React and Vue.

3.1 Basics

3.1.1 sifoAppDecorator parameters

The sifoAppDecorator parameters are basically the same as the Sifo parameters (except for the fragments parameter). Here we still need to explain these parameters so that readers can understand what the sifoAppDecorator parameters correspond to. For other parameters not listed, please refer to Sifo's documentation.

  • namespace
    • Namespace, this is the first parameter of sifoAppDecorator, string type. Namespace is the globally unique identifier of the current sifo target. It is recommended to use a value with clear meaning.
  • fragments
    • Fragments, the above capabilities a and b mentioned rendering fragments, which are used by this parameter to specify which fragments are available. Type is generally fragment key (string type) of the array: [ 'fragmentKey1', 'fragmentKey2' ]. The head and tail fragments of the target component are built-in and do not need to be specified. The embedded fragment is explicitly specified when the modifier is installed. The target component will take the fragment according to the fragment key and render it to the desired position. Developers can perform logic control for fragments (specify rendering components, bind properties and events, etc.).
  • components
    • Component, why is there a component parameter? Because the fragment can be a json description (in Sifo schema, see the format below), here it is called a schema fragment for the time being, and ordinary fragments are called string fragments. Because the string fragment is just an identifier, it cannot limit the content of the fragment, and the schema fragment is the fragment that specifies the content structure. So also provide the component objects specified in the schema.
  • plugins
    • Plug-ins, the "binding properties and events" (corresponding capabilities c and d) in the logic control are written using plug-ins, which is an important feature of Sifo. There are three types of plug-ins in Sifo: model plug-ins, page plug-ins, and component plug-ins. All kinds of plug-ins can implement rich functions, which will not be expanded here.

The format of the schema is as follows:

{
  "id": "id001",//The unique identifier of the node, which cannot be repeated. The plug-ins are distinguished by this id.
  "component": "div",
  "children": [
    {
      "component": "InputComponent",
      "id": "id002",
      "attributes": {
        "value": "val01"
      },
      "children": []
    }
  ]
}

In addition, all the fragment keys declared in the modifier mode will be used as the id of the child node of the first-level children of the schema (the first-level node id is the namespace).

3.1.2 Basic usage

Basic usage under React

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;
const TestFnDecorator = props => (<>...</>);
const App = sifoAppDecorator('test-sifo-decorator', {
  fragments: ['$temp_panel']
})(TestFnDecorator);
export default App;
  • Functional components, using higher-order functional forms

Basic usage under Vue

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

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;

3.1.3 Add rendering fragments at the head and tail of the target component

In 3.1.2, I talked about the 被修饰方usage of the modified goal ( ), what about 修饰方(that is, the realization of the ability, which will also be used in the following text)? Remember the plugins parameter? Yes, we can write plug-ins to control fragments.

This part only talks about the most basic addition of rendering fragments at the beginning and end of the target component. This requires a page plugin, which 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 }];

Among them, the schema node id is

sifo-footer is the built-in head and tail fragment keys respectively. In the above example, the page plug-in sets the head fragment content as: a div and "this is a header fragment" copy.

It can also be specified as any other component. 1. register the required components in the components parameter:

const components = {
  AnyComp: HelloWorld,
}

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

{
  component:'AnyComp',
  attributes: {
    anyProps:'any'
  }
}

Of course, you can also control the props attribute of the specified component, etc., which will be discussed together in the advanced part.

3.2 Advanced

The advanced part should talk about the following abilities:

  • b. Embed rendering fragments inside the target component (advanced)
  • c. Monitoring and intervention of target component methods and events (advanced)
  • d. Communicate with target components, such as component state changes (advanced)

3.2.1 sifoApp properties

Here we need to talk about the sifoApp property first, which is an object injected by sifoAppDecorator into the props of the target component. React in the assembly, by this.props.sifoApptaking that in Vue component, props need to declare sifoApp, then this.sifoApptaken to.

A series of methods are provided in sifoApp, such as:

  • getFragment
    • Get the rendering fragment, basic usage:sifoApp.getFragment("$static_panel")
  • addEventListener
    • Registration method, event monitoring, basic usage:sifoApp.addEventListener("click", handler)
  • watch
    • Observation, you can customize the observation, and the observation can be triggered externally. Basic usage: sifoApp.watch("updateData", watchHandler)sifoApp also contains mApiobjects, several methods listed above ultimately called mApi, mApi is the main interface object Sifo, and offers many other capabilities, they will not expand here.

3.2.2 Embed rendering fragments inside the target component

It is to call sifoApp.getFragment to get the rendered fragment and place it in the specified position.

Modified formula

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>
     );
  }
}
<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>
  • Vue

Modified formula

The modifier is to implement the corresponding fragment function, the simplest is as the previous pagePlugin did:

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

MyInput can be a component that does not pay attention to props, and if you need to manage the props of the component, for example, to add 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 of event names in Vue is lowercase, and it may be onChange in React, according to component props
        mApi.addEventListener(event.key,'change', (ctx, e)=>{
          mApi.setAttributes(event.key, { 
            value: e.target.value +'extVal' 
          });
        });
      }
    },
}
const plugins = [{ pagePlugin, componentPlugin }];

3.2.3 Monitoring and intervention target component methods and events

The modifier uses sifoApp.addEventListener to register the event or method to be monitored. The modifier can also monitor and intervene, such as adding response actions, modifying the return value, blocking events, etc.

Modified party

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

Modifier

On the component plug-in whose key is the namespace, you can monitor the method with the same name

const componentPlugin = {
 //The event is monitored on the id of the namespace
  '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}`
        });
      });
    }
  }
}

3.2.4 Communicate with target components, such as component state changes, etc.

Use sifoApp.watch to customize an observation event. When mApi.dispatchWatch is called externally, the observation event will be triggered.

Modified party

sifoApp.watch("updateData", (ctx, data) => {
  console.log('data', data);
});

Modifier

mApi.dispatchWatch('updateData', data);

3.3 Advanced

Finally came to the advanced part, remember the plugins and components parameters that you said to pass to sifoAppDecorator?

In fact, they can be passed in without adding modifiers.

That's right, they can be completely placed in a separate file, and js packaging and loading can be performed independently! This is: contactless expansion and secondary development.

3.3.1 Contactless expansion and secondary development

If readers read the official sample code, they will find that the decorator implementation of the decorator in the example is not passed in the parameters of sifoAppDecorator, but a separate ext.js file.

This mainly depends on the ability of sifo-singleton:

If a page is developed with sifo, the developer can extend the page without touching the original code. This uses sifo-singletonthe global expansion vessel, loaded extensions, as long as the assembly before the target page rendering extension will take effect on the target page.

The method of use is as follows:

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

The advanced chapter is over!

2. Sifo basics

Sifo is a highly extensible, two-open plug-in front-end development framework. This article has enough space to introduce modifiers, so I will leave it empty here (see reader feedback before talking). For more features and capabilities of Sifo, you can read the content in the relevant links at the end of the article.

4. other

sifo github address

Decorator online experience: react-decorator, vue-decorator

Related reading: Highly scalable, two-open front-end development framework-sifo Ali open source Sifo: configurable and highly scalable forms

Reference: https://cloud.tencent.com/developer/article/1780998 The highly scalable React/Vue decorator you must have never seen-Cloud + Community-Tencent Cloud