Preface: before wrote 2 basic did not have dry goods, be afraid of reader scold my dog, since shared thoroughly point, it is stem code, have what cannot write of. I will send the specific code to Github for open source. The whole project is not open source, only the editing part. The React code does not always use the react framework. I don’t have time to go through the huge code, but you can read it for yourself. Just look at the thinking here.

Let’s talk about architecture. A lot of people ask what front-end architecture does, but I don’t really know. I was told by Winter that front-end architecture is about reusability. React, for example, is a logical separation of components to maximize reuse and scalability. Of course, many people like to use * -CLI and other tools to produce projects. I don’t worry about this. Anyway, I think the flexibility is not enough, so I always do it by myself.

My readme.md projects usually start with a diagram showing the document directory structure of the current project so that colleagues working on the project can understand the code. Preface nonsense finish, enter the topic, project catalog is not the key. I’m a React developer, so I use React as the main code, but the basic principles I share are common, not necessarily react.

According to the product form in the last article, we analyze two types of products, one is H5 page generator, the other is process definition +form generation.

Let’s start with the H5 page editor to repeat the basic product requirements

  • Display effect requirements, stability, no error, no white screen. It needs to be fast. We can’t keep our customers waiting too long.
  • Business needs, rich components, is the market we have to have, do not have the hope to have. For example, in order to show more products in an organized way, similar to the classification of components, for better control of page structure, similar to layout requirements
  • Routing demand, with the improvement of marketing operation ability, many activities are carried out in the form of theme activities, not a single page of publicity, often a conference concept, will set a lot of single pages to form a series of pages to conduct activities, so it needs to control the ability of routing
  • There will be a need for targeted placement on the activity page. For example, some activities will only be carried out in Hangzhou, and regions outside Hangzhou will not participate in the event. The page should have the ability to display according to the region, and some activities will be carried out in a certain channel, while other channels will not be carried out.
  • The need for data collection, the purpose of the campaign is still to retain customers, and hopefully there are portals for users to leave some information. So that the subsequent liquidation.
  • The need of data analysis, generally speaking, the page to access the data in the future will can improve effect of activity, after the user behavior data analysis, in order to provide policy support for the follow-up activities, simple uv, pv already cannot satisfy the needs of the present operation, but with 3 d data, such as commodity clicks, users in the retention time of the page, User access links throughout the activity.

Based on the above problems, we need to disassemble the requirements, form a basic front-end frame, frame these requirements, regardless of the UI, UI is very simple, there is no need for any architecture, according to the PRD to draw. The basic UI for editing is left center right structure, left component list, middle effect display, right property configuration. Basic is like this

We implement the react code as follows, with the entry editor.html, editor.js

<div className="page-container">
    <Suspense fallback="">
          <ThemeProvider theme={theme}>
                <Header savePage={this.savePage} publishPage={this.publishPage} />
                <Operation compCount={compCount} />
                <Preview pageConf={pageConf} compList={compList} />
                <Config
                  pageConf={pageConf}
                  compList={compList}
                  currentUuid={currentUuid}
                  onSort={this.handleSort}
                />
          </ThemeProvider>
    </Suspense>
</div>
Copy the code

This is the UI design, and the outer <div/> <Suspense/> <ThemeProvider/> is ignored

<Header/> – The Header contains the back button, preview, save, publish and other button functions

<Operation/> – List of components on the left

<Preview/> – Preview in the middle shows that, in order to isolate dom structure and event events, a Preview. HTML is loaded with iframe and iframe communication is performed using the Postmate component. There are a number of problems exposed, which will be explained in more detail later.

<Config/> – displays configuration items based on the selected components

Here we talk about the communication loop of the whole project, and I think we can easily understand how to do in the process of technical implementation. The entire UI is actually driven by a data structure. With react’s internal two-way data binding, every new change is actually changing the same data structure. Here’s an example:

env: "publish", pageId: "1632599993707810817", pageData: { pageConf:{ background: {a: 1, r: 255, b: 255, g: 255} pageDec: "" pageName:" hangzhou impression "pageURL:" https://cdn.izelas.run/publish/2194972e.html "}, compList: [{uuid: "C3f1ecdc71c1df833a4498fddd4040db compLabel:" shuffling ", compName: "Carousel" feature: false, setting: {carouselProps: {effect: "scrollx," the autoplay: true}, imageList: (5) [{...}, {...}, {...}, {...}, {...}], the outLine: false, place: "inline",}}},Copy the code

As you can see from this show, there is nothing mysterious about the technology as a whole, and it is consistent with most developers’ implementations. The above configuration is that a page contains a casting component. When the user clicks publish, the data and rendering will be loaded once, packed into a static file, pushed to the CDN, and a static page address will be returned. This is the whole logic. Because the backend is NodeJS, that piece of code is relatively simple, I will leave it later, mainly front-end rendering. Now you’re going to do a couple of things

  • The Schema for all components is clearly defined, that is, the data structure for each component in compList. This is closely tied to the business requirements of your specific component.
  • Solve data communication problems, that is, create components, modify component properties, delete components when events occur, the preview interface should change in real time
  • Data loading issues. Components are not completely static, and some components need to fetch background data to populate the page, such as a commodity component that displays items selected by the user. This commodity information needs to be fetched from ajax in the background, and the strategy for loading this data affects rendering

If you look at the UI code, you’ll see that the entry page holds all the data, and any child components don’t hold the data, which is passed through the component props property. Events are done in broadcast or listening mode. To clarify the pattern, react can be injected with props on events from parent components, while sibling components, or more distant horizontal components, need to pass events from a common parent component. It’s inefficient and confusing. Now, the model is, when I click a button, the button doesn’t actually have any logic to it, it just screams I’ve been clicked, and that’s it, and I broadcast that I’ve been clicked, so who handles that? Whoever listens will deal with it. For example, the teacher called the roll in class, Zhang SAN, this time the whole class heard, but only Zhang SAN answered “arrive”. That’s the idea.

PageData -> compList, pageConf, pageData -> pageData -> pageConf Broadcasting events emitters. AddListener ('_micro_page_createComp_', this.createcomp) // Monitoring data changes and notifying underlying components including component creation, deletion, and replication. Emitters. AddListener ('_micro_page_onSetCompList_', This.onsetcomplist) // Emitter. AddListener ('_micro_page_setCompProps_', This.setcompdata) // Emitter. AddListener ('_micro_page_setPageConfig_', This.setpageconfig) // Listen to the history of callback messages Emitters. AddListener ('_micro_page_posiCompList_', GoBackAction) // Emitter. AddListener ('_micro_page_compListeAction_', this.buildComplistData)Copy the code
// The react button will automatically diff when the top level of the data changes. All components that receive props // will update the props and then update the UI. The principle of Config is the same. In this way, the data and processing methods are all concentrated in the // entry file, logic is relatively clear, easy to troubleshoot problems. <div className="module-item" onClick={() => { emitter.emit('_micro_page_createComp_', _cloneDeep(defaultConfig)) }} > <div className="module-item-inner"> <SvgIcon component={renderComponent(compName)} viewBox="0 0 22 22"/> <div className="module-item-name">{compLabel}</div> </div> </div>Copy the code

This logic is clear, in fact, not complex, but more components, more complex UI interface. According to the PRD component design to develop good, no difficulty. There are a few remaining issues, namely data burial points and Ajax data communication.

Data burying point generally we will write a more general report function, also relatively simple. Very mature, not too much googled on Github. If not, look at the code in GA(Google Analysis). It is mainly the reporting of some button data, such as clicking on a commodity. Although the PV of the commodity page will be counted, it is not known that it is from the marketing page. There are many similar problems, of course, GA also provides a way to actively report data when clicking events occur, the problem is, the platform itself is low code, the page has not been released, we need to generate the corresponding page according to the results of the user to make the page, maybe we do not know what products users need to choose. There is also the problem of code intrusion, if the GA method is changed, then I will change. My solution is to hide attributes on sticky notes. Type jquery data-, and then put the required data into a custom attribute according to the needs of the data burying point. The data collection component listens for the events coming from pop and uses them by itself in the DOM.

There is also the problem of ajax request, which is not easy to solve with the front-end method, but nodeJS solution is more convenient, anyway, ACCORDING to the configuration, the page into a static file, upload to the CDN, so I just do this step. Nodejs makes the request, statically transfers the requested data to the configuration item, and then calls the CDN method to upload the HTML file.

const _putHTMLToOss = async (pageConf, compList, PageId) => {try {const _compList = await getAllData(compList) // Set static page URL const publishHtmlPath = pageConf.pageURL ? pageConf.pageURL.replace("https://cdn.izelas.run/", "") : `hd/publish/${getShortStr()}.html`; if (! pageConf.pageURL) pageConf.pageURL = `https://m.myweimai.com/${publishHtmlPath}`; // Injector injector = {jses: [ `${cdnPath}/micropage/${timestamp.micropage}/common.js`, `${cdnPath}/micropage/${timestamp.micropage}/micro-view.js`, ], css: [ venders.materialUI, `${cdnPath}/micropage/${timestamp.micropage}/common.css`, `${cdnPath}/micropage/${timestamp.micropage}/micro-view.css`, ], data: JSON.stringify({ env, pageId, pageData: {pageConf, compList: _compList,}, weimaiHosts,}), title: (pageConf && pageConf. PageName) | | "micro pulse - modular editor,"}; Const renderString = fs.readfilesync (path.join(view_path, "view.html"), {encoding: "utF-8 ",}); renderString = fs.readfilesync (path.join(view_path, "view.html"), {encoding:" utF-8 ",}); // Const compiled = _. Template (renderString); // Data and templates are combined into compiled strings const compiledStr = compiled(Injector); // Call oss authorized client const ossClient = new OSS(ossKey); // Upload file to oss await ossclient. put(publishHtmlPath, buffer. from(compiledStr)); return true; } catch (error) { console.error(error); return false; }};Copy the code

After all, each system has its own particularity, especially the requirements of the industry for the product. The source code of this code will be open source to Github. When I finish finishing, because nodeJS code has verification and some technical points are not convenient to open source, only put front-end code. Or that meaning, understand the train of thought, according to their own business thinking, can help you the best, there are good solutions welcome to provide. The system is also being upgraded. Also need to add some JS and image resource loading optimization, as well as template and skin some thinking. These await the joint efforts of all colleagues.