Author: The Technique of Idle Fish by Rixi

Business background

At present, the development mode within the team is mostly component-oriented, and the UI layer and logic layer are strongly coupled together. Due to the difference of business, it is often difficult to fully reuse.

The front-end business of Xianyu is in the stage of rapid development and continuous trial. How to fulfill the demand more quickly and stably and better support the business development is definitely a problem worth exploring.

When taking over a complex old business code, the maintainability is often poor after more people modify it. Sometimes, it only wants to modify a small place but needs a large understanding cost. Therefore, it is particularly important to use a uniform set of component development specifications in long-term maintenance.

Xiyu technology system has undergone changes from WEEX and RAX0.x to the present RAX1.x, and some front-end assets have been accumulated in the process. However, since the cost of migration is no longer maintained in the later stage, how to make the business layer transition smoothly to the new technology system with less cost?

For the above problems, we hope to solve them together with the framework. The objectives of the framework mainly include:

• Improve code reusability • Standardize code to reduce long-term maintenance costs • Reduce the association between the business layer and technical architecture

Train of thought

In terms of efficiency improvement, it is more important not to write the same code repeatedly, to do finer differentiation and extraction, and to improve the reusable granularity. On the other hand, it is to solve the problems that affect the development efficiency under the existing development.

Component layering

So we divided the component-oriented development pattern into UI layer View and logic layer Store, separated and coupled by Interface.

Figure 1: The component component does not care about the flow of state at the UI layer. It is only responsible for displaying and calling interactive methods. Behavior logic such as DOM related animation interaction is also placed in this layer.Figure 2: Component division

After confirming the layered logic, Interface is naturally introduced, which is mainly divided into two parts: one is IProps, which declares the Props needed by the component and provides corresponding prompts and constraints when the user invokes the component; The other is responsible for connecting stores and Views, including state states and interaction methods. See the following Interface example:

export interface IMultiScrollerProps { tabs: string[]; onTabChange? (i: number): void; } export interface IMultiScroller extends IBase { readonly tabIndex: number; readonly tabSource: ITabItem[]; readonly children: any[]; onSwiperChange(i: number): void; }Copy the code

To summarize: All state and interaction methods are managed in the Store and consumed by the View; View is only responsible for the logic operations related to the DOM. The dividing line between View and Store is that when View and Store are used separately, their interactions and effects remain the same. This allows for more reuse of the View and store, respectively.

State management

Basically all requirements in the existing business development are based on hooks state management, which has the following problems:

• High maintenance costs for complex component hooks after multiple iterations;

• sometimes your useEffect depends on the immutability of a function, which in turn depends on the immutability of another function, thus forming a chain of dependencies. Once a node in the chain of dependencies is accidentally changed, the useEffect is accidentally triggered and the subsequent situation becomes uncontrollable.

• Asynchronous traps

• State changes are asynchronous. UseState returns an asynchronous change function that does not take effect directly, so reading this value gets the old value. You won’t get the new value until the next redraw. Do not try to get the state immediately after changing it.

const [value, setValue] = useState(0);
setValue(100);
console.log(value); // <- 0
Copy the code

• Timeout points to the old value, even if it has been reset externally, because all that is retrieved in setTimeout is the previous value.

const [value, setValue] = useState(0);
window.setTimeout(() => {
  console.log('setAnotherValue', value) // <- 0
}, 1000);
setValue(100);
Copy the code

• There is a threshold for beginners when to use useCallback/useMemo etc.

UseEffect, useMemo, useCallback all come with their own closures. That is, every time a component is rendered, it captures the state (props) in the context of the current component function. Therefore, each execution of the three hooks reflects the state at that time, and the latest state cannot be obtained. In this case, ref should be used for access.

The most popular react architectures for state management are Redux and MOBxFigure 3: Redux Flow

The characteristics of Redux can be summarized from the following three principles:

• Single data source • State is read-only • Pure functions are used to perform modifications

But the problem with Redux was obvious: developers had to write more additional boilerplate code, leaving more code for us to maintain.

Another state management solution similar to Redux is MobX:Figure 4: Mobx Flow

MobX is simpler and more flexible than Redux’s strong-rule convention, with the core principle of triggering state changes through action, which in turn triggers state-derived objects (Computed Value & Reactions). All you need to do is define data for Observe and Computed value or Reactions, and MobX will do the rest of the updating. In a word:

Anything derived from the application state should be acquired automatically.

Analyzing the business characteristics of Idle Fish, there is no super-large demand for more than 5 students to maintain a project at the same time. Strong contract redux has limited benefits for us, while MobX is indeed easier to get started than Redux and does not need to write a lot of boilerplate code, so it can provide a more efficient choice.

implementation

We named the framework Linke, from the Switch game Zelda, hoping it would light up temples like Link. Based on the above analysis ideas and combined with the technical system (Rax) in the actual business, we finally designed the following research and development system: THE UI part, namely View, still uses the original Rax, and the state used by UI is also directly managed in View. The business logic part, where the Store uses Mobx’s capabilities to solve the same problems that were previously mentioned in the hooks development, is not strongly related. Linke acts as an intermediate coupling layer to constrain and bridge them.Figure 5: Development system based on Linke

API

To ensure the lowest learning cost for developers, Linke designed with as few apis as possible, ending up with just one method and four built-in Store methods. See:

observer(baseComponent, Store)

Make sure the component responds to Observable changes in the store, i.e., store updates, and component views responsive updates

Store built-in methods

• Member method -set: All state changes must go through set: All state changes must be done through set: All state changes must be done through set, similar to setData() in wechat. • Member methods – setProps: Used to handle external component props, View initialization or changes • Member methods −setProps: • setProps: Calls when the View is initialized or props changes. • didMount: calls when the View is initialized or props changes. • Member method -unmount: Provides a View lifecycle, which is called when the View is removed from the DOM. When a View is removed from the DOM, it is called when the View is removed from the DOM. When a View is removed from the DOM, it is called when the View is removed from the DOM. SetProps −>setProps -> setProps−>didMount -> $$unMount

demo

Interface.ts

index.tsx

store.ts

A complete component demo is shown above.

contrast

The current module pattern for component development is shown in Figure 6 below, where all logic is coupled together on a component basis and there is no boundary between them, making it difficult to reuse even the same style. There are significant costs and instability risks in both code understanding and secondary development.Figure 6: Development pattern of the original component

The linke-based component development pattern is shown below:Figure 7: Linke-based component development pattern

View and Store are relatively independent without strong coupling, and the benefits are obvious: • Data logic and View logic are managed in Store and View respectively, realizing their respective functions and reducing maintenance costs. • The most important point is the separation of Store and View reuse, combining different stores/Views to generate different componentsFigure 8: Store and View combinations

Figure 9: Different Stores and the same View

application

At present, Linke has been applied in various new projects of Xianyu front-end, including 2 online projects and 3 projects under development. The benefits are obvious, and the location of the code of the function can be clearly seen. The annotation in Interface greatly reduces the understanding cost of the project to take over. General basic components and business components are being removed in an orderly manner. At the same time, with the continuous enrichment of View/Store library, reusable material resources increase, and more and more View/Store can be reused in different businesses and different scenarios of the same business, which greatly reduces development costs and improves efficiency to a certain extent.

The next step

At present, in addition to the existing H5 business in the new fiscal year, the biggest characteristic is to do some flow exploration for each small program, such as Tao department light application, wechat small program, Alipay light application, etc., these applications are similar to the H5 business in the end, but there will be slight differences. So we are also exploring linke-based improvements to such business scenarios.