This should be the largest react component, right!?

Students, web projects should be componentized

People who came from jquery in that era have a great feeling of componentization. As we all know, react project is composed of many small components. Small components are combined into large components, and large components are combined into project level components. That component also has size and life cycle, so the following < app / > is also a component.

render(<App />, document.getElementById('App'))

Here we refer to the components: reusable components, which are installed and used through npm install xxx.

Accidentally made a super big component

The shared project is h5ds, a super large react component, which can be installed and used through npm install h5ds. At present, this should be the largest react component.

1. What is H5DS?

H5DS (HTML5 design software) can be understood as an online tool for H5. H5 is the page that slides on the mobile phone, such as the online tools like e-business show, baidu H5, wps show hall and Maka.

At first, I just want to make a canoe, do it, feel it's better to add a sail, and then I can't stop adding new functions. The canoe becomes a big ship, and finally a warship.

As soon as any project enters the iteration cycle. Even a small project can grow into a huge one, so don't underestimate your ability to deal with problems. If you let go, there will be surprises.

Example of tool screenshot:

Example of H5 made:

Next, I will explain the whole process and technical scheme of developing this project.

2. Technical selection

Principle of technology selection: we try to save the development amount and do not make wheels repeatedly. We want to produce a finished product, not a raw material processing plant. We can use it when there are ready-made components and a framework to save the development workload.

[front end:]

React: modular development is necessary. Choose one from angular, react and vue. React is selected here.

JQuery: Although many students say that this has been eliminated, for small team development, development resources are very valuable. It is a waste of R & D resources to use two sentences of code to solve a problem that can be solved by one sentence of code. Of course, there is another more important reason to use this. In order to support the massive jQuery plug-ins, we sacrifice part of the performance.

Mobx: from a technical point of view, the best solution after our analysis is not mobx, but redux. Redux is more suitable for such a tool project. However, considering the amount of code and the cost of secondary development, I finally chose mobx. For users who have used vuex, mobx is also convenient to handle and easy to transfer from vue to react project.

Less: the main reason for not choosing sass is that the node sass package is not very stable and often makes mistakes. Of course, less can meet our needs.

antd: we don't build wheels. Such a tool project has a very large demand for components. There is a ready-made excellent react component library to use. If you don't have one, you can encapsulate it.

Loadsh: there are many tools to process data. Here we use some methods in loadsh to process data.

[back end:]

Koa: nodejs is used as the back-end language, and there are many koa documents and learning materials. express was built by the original team, which is just right.

mysql: it's a free relational database, which is quite conventional.

Sequelize: sequelize is a powerful asynchronous ORM framework based on Nodejs. Since we have it out of the box, we won't encapsulate it ourselves. Save the amount of work. Focus on business.

3. System architecture scheme

The requirements are summarized in one sentence: the editor generates H5 page, which can be opened on the mobile terminal.

The editor and H5 page should be separate projects. There are many modules (plugins plug-ins) in H5, such as picture, text, shape, video, music, etc. other plug-ins may be added later, so this business must be extensible. Because we use mobx to manage data and json to manage data, we have at least three solutions:

  1. The editor generates JSON data, and the server generates HTML code based on JSON data to provide H5 preview page.

Advantages: the server directly returns to the HTML page and can do SEO

Disadvantages: the plug-in needs to be loaded and used on the server side. It will not be able to run without the server side. If the access volume is large, because of the server pressure.

  1. The editor generates HTML code directly. The server saves the HTML code as an HTML file and returns the URL for access.

Advantages: the server directly returns the HTML page and can do SEO. The server has low access pressure and can run independently from the server

Disadvantages: the data is not flexible enough. It can only be re published after the editor is modified. The code of subsequent upgrade preview page cannot be upgraded synchronously.

  1. The editor generates JSON data directly. The server is only responsible for accessing JSON data and rendering to the front end for processing.

Advantages: small pressure on the server, independent operation from the server, flexible data, synchronous template upgrade

Disadvantage: SEO is not supported

In the comprehensive evaluation, we chose the third scheme, the preview page directly depends on the plug-in, with obvious advantages and large space for subsequent upgrade. If you want to do SEO, you can also do SSR, but at present, the demand for SEO is not great, because the main thing is wechat H5.

4. Data structure

After the architecture scheme is determined, the data structure is also very important. According to the requirements of H5 page, the data structure is roughly as follows:

{
    ...infos, // Record H5 information, name, main diagram, description
    ...options, // Record H5 configuration information, sliding effect, type, etc
    pages: [ // Record page data
        {
            ...infos, // Page information
            ...options, // Page configuration
            layers: [...] // Record layer information for pages
        }
    ]
}

Overall, the data structure is very clear. The concept of layer and page is also the core of H5.

A page is made up of multiple layers, and there are multiple kinds of layers (pictures, text, shapes, videos, music), which is called plugins

In fact, the data structure is very complex. Our data are as follows:

Version v1.0 of h5ds json data structure

appJSON data structure:

{
    version: 5.0.0, // Current H5DS version
    img: 'http://CDN. H5ds. CN / static / images / img null. PNG ', / / main diagram
    desc: 'Point stone H5´╝îOfficial website h5ds.cn', // Descriptive information
    name: 'Point stone H5', // apply name
    type: 'phone', // h5 type phone or pc
    slider: { // Global paging settings
      speed: 0.5, // Switching speed
      effect: 'slide', // Flip animation slide, fade, coverflow
      autoplay: false, // Auto page
      time: 5 // Auto page turning time
    },
    style: { // The style of app
      width,
      height
    },
    fixeds: [ // There are two floating layers, which cannot be deleted or added. Upper float and lower float
      {
        id: null, // div.id
        className: null, // div.class
        keyid: util.randomID(), // keyid is a random number that does not repeat, which is equivalent to id
        name: 'Floating layer', // Name
        desc: 'Page description',
        style: { // Style of floating layer
          height,
          width
        },
        layers: [ layerJSON ] // Layer set in floating layer
      },
      {
        id: null,
        className: null,
        keyid: util.randomID(),
        name: 'Under the floating layer',
        desc: 'Page description',
        style: {
          height,
          width
        },
        layers: []
      }
    ],
    popups: [ pageJSON ], // Pop up data set,
    pages: [ // Page data collection
      {
        id: null,
        className: null,
        keyid: util.randomID(),
        name: 'page name',
        desc: 'Page description',
        style: { height, width }, // Page style, background color style will render separately in the outer div
        layers: [ layerJSON ], // The layer of the current page
        slider: { // Page turning settings of the current page
          autoplay: false,
          lock: false,
          time: 5
        }
      }
    ]
  }

pageJSON Data Description:


{
    id: null, // Page id
    className: null, // Page class
    keyid: util.randomID(), // Unique identification
    name: 'page name',
    desc: 'Page description',
    style: { height, width }, // Page style, background color style will render separately in the outer div
    layers: [ layerJSON ], // The layer of the current page
    slider: { // Page turning settings of the current page
      autoplay: false,
      lock: false,
      time: 5
    }
}

layerJSON Data Description:


// Each layer JSON data is different. They all follow certain rules and data parameters are different

{
    version: '1.0.0', // Plug in version number
    name: 'Map plug-in', // Plug-in name
    pid: 'h5ds_map', // id of plug-ins
    id: null, // Layer id
    className: null, // class of layer
    set: { hide, lock, lockWideHigh, noEvent }, // Lock, hide, lockWideHigh lock aspect ratio and other settings. noEvent indicates that the event can be penetrated
    animate: [ animateJSON ], // Layer animation, can support multiple animations
    data: {...}, // Data storage location related to component differentiation
    style: { width: 100, height: 100, top: 0, left: 0 }, // Outer layer default style for layer components
    estyle: {}, // The style of element div has only four parameters. Other styles are written to estyle, such as background. Because the animation parameters are set on element div, the transform style cannot be set here
    events: [ eventJSON ] // Event configuration data, each layer can add multiple events
}

5. Technical difficulties

  1. Data management:

Our data is lost to mobx for management, data changes, and view updates directly. This is very similar to the data management of vue. There will be many data issues involved. In this case, we need to define some global methods. I have defined an h5ds store, in which two data (edata, data) are saved. Edata is the abbreviation of editor data, which is used to record the interaction data of users in the operation editor. For example, which page and layer are currently selected, and some global configuration parameters. Data is used to save the json data of H5. In order to ensure that the browser suddenly crashes, resulting in the user data being cleared, I have done historical operation data, and will save the edited data to localstorage.

  1. Revocation rollback

The operation record is to save the current data of edata and data to memory. In order to save memory, only 20 latest operations are saved, and you can undo to back to the previous operation at any time. If redux is used, natural data rollback is very convenient.

  1. Data updating

The data update in the editor is very frequent. Because the data nesting is too deep, mobx's proxy will not listen to the data inside the object or array, so it is necessary to trigger the view update manually. Here, a global method, updateCanvas(), is written to force the update of the entire view. For performance reasons, when dragging the position or modifying the size , to modify only the view of a certain layer. Each layer has a key, which can be modified to update a single component.

  1. Packaging layer plug-ins

Layer plug-ins are used in the editor and preview page, and reuse is involved here. We divide the plug-ins into layer.js and Editor.js. Layer.js is used in both editor and H5 preview page, and Editor.js is only used in editor. In order to reduce the code of preview page, we packed two UMD packages separately and used them in different places. At first, we used requirejs to manage UMD packages, but later we developed requirejs with various problems, which may conflict with some third-party packages. Therefore, we directly mounted the plug-in under the window object and stored it with h5ds_globalobject. Although it is very violent, but This method is really practical.

  1. Drag scale rotation

It's very interesting to drag, zoom and rotate. If you use react, you will wrap a layer of drag components outside each selected element. If you use judgment to dynamically load the drag components, the layer will be updated. So we use a very clever method, using jquery to encapsulate a drag and drop plug-in, and binding things in component didmount It is not recommended to initialize this plug-in. It can be described as a magic trick. But it has greatly reduced our maintenance costs. It is also very convenient to use.

6. Performance optimization

We can combine the anti shake function to optimize the performance, control or selectively update the view. Here is an example:

import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import debounce from 'lodash/debounce';

@inject('h5ds')
@observer
class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
        count: props.h5ds.count  // The default is 1.
    }
  }
  
  // Control performance of anti shake function
  updateOtherRender = debounce(() => {
      const { count } = this.state;
      // If it is greater than 10, the view in other places will be updated
      if(count > 10) {
        this.props.h5ds.count = this.state.count
      }
  }, 500)
  
  changeValue = e => {
      this.setState({count: e.target.value}, this.updateOtherRender)
  }
  
  render() {
      return <input type="number" value={this.state.count} onChange={this.changeValue}/>
  }
}

We use the above writing method in many places. react advocates the minimum modularity. We also hope that the impact between modules will be minimal. If a parameter is used in multiple modules, it must be very slow when it is input quickly.

7. All terminal adaptation scheme

The resolution of the mobile phone is too much. To be compatible with all models, one compatibility scheme is far from enough. Here I provide a variety of compatibility schemes.

First: scale proportionally

Our default width height is 320px * 514px, and then zoom to automatically fill the height or width, as shown in the figure. Of course, a part of the border may be reserved up and down or left and right without any display.

Second: full screen background

Because there will be up and down or left and right gaps, at this time we will do full screen processing for the background color, for example, red is the background color, if the height exceeds, our H5 page will automatically appear scroll bar.

Third: adsorption location

The term "adsorption location" is my own. Sometimes it is very troublesome to be compatible with iPhone X. adsorption location means that it can be adsorbed to the top or other relative positions. Our adsorption location provides 8 positions for adsorption. After adsorption, the location is relative to the window, not the shadow. For example, for little hearts:

Before starting adsorption:

After the adsorption at the upper left corner is turned on:

8. How to use H5DS

  1. npm install h5ds --save installation dependency package.
  2. webpack configuration:
externals: ['React', 'ReactDOM', 'ReactRouter', 'ReactRouterDOM', 'mobx', '_', 'antd', 'PubSub', 'moment']
  1. Using h5ds

html template:

<!DOCTYPE html>
<html>

<head lang="zh-cn">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="keywords" content="">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>H5DS5.0</title>
    <meta name="renderer" content="webkit">
    <!-- No Baidu Siteapp-->
    <meta http-equiv="Cache-Control" content="no-siteapp" />
    <meta name="apple-mobile-web-app-title" content="yes" />
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta http-equiv="Cache-Control" content="no-siteapp" />
    <link rel="shortcut icon" href="/assets/images/favicon.ico">
    <link rel="stylesheet" href="https://at.alicdn.com/t/font_1160472_ybl2xl0ao8.css">
    <link rel="stylesheet" href="https://at.alicdn.com/t/font_157397_ujac0trx9i.css">
    <link href="https://cdn.bootcss.com/antd/3.23.0-beta.0/antd.min.css" rel="stylesheet">
    <link href="https://cdn.h5ds.com/lib/plugins/swiper.min.css" rel="stylesheet">
    <script src="https://cdn.h5ds.com/lib/plugins/swiper.min.js"></script>
    <script src="https://cdn.h5ds.com/lib/plugins/jquery.min.js"></script>
    <script src="https://cdn.h5ds.com/lib/plugins/h5ds.vendor.min.js"></script>
    <script src="https://cdn.bootcss.com/moment.js/2.24.0/moment.min.js"></script>
    <script src="https://cdn.bootcss.com/antd/3.23.0-beta.0/antd.min.js"></script>
  </head>

  <body>
    <div id="App"></div>
  </body>
</html>

react Code:

import 'h5ds/editor/style.css';
import { render } from 'react-dom';
import React, { Component } from 'react';
import H5dsEditor from 'h5ds/editor';

class Editor extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null
    };
  }

  /**
   * Save APP
   */
  saveApp = async data => {
    console.log('saveApp ->', data);
  };

  /**
   * Release app
   */
  publishApp = async data => {
    console.log('publishApp ->', data);
  };

  componentDidMount() {
    // Simulate the number of asynchronous loads. Setting defaultData will load an initialization data by default
    setTimeout(() => {
      this.setState({ data: 'defaultData' });
    }, 100);
  }

  /**
   * Using the Editor section
   */
  render() {
    const { data } = this.state;
    return (
      <H5dsEditor
        plugins={[]} // Third party plug-in package
        data={data}
        options={{
          publishApp: this.publishApp,
          saveApp: this.saveApp, // Save application
          appId: 'test_app_id' // Current appId
        }}
      />
    );
  }
}

// render
render(
  <Editor />,
  document.getElementById('App')
);

End

Thank you for reading!

Our official website is: http://www.h5ds.com

Our git address is https://github.com/h5ds/h5ds

At present, the release of tool editor is relatively mature, and it is still in the process of iteration. We hope that more developers can participate in the development of plug-ins, so that the H5DS editor can bring convenience to various fields.

Technical exchange QQ group: 549856478

If you think there is something wrong, or have any good suggestions, welcome to issue us!

Tags: Javascript React JSON Mobile JQuery

Posted on Sun, 03 Nov 2019 20:15:51 -0800 by ekalath