@vmojs/decorator – a library of tools that helps you create front-end data models faster and better, making your data processing process simpler and more flexible.
I’ve written many times on the topic of using data models in the front end, and I’ve done a lot of success with our business projects. After a lot of thought and polish from the @vmojs/ Core inherited data model to the @Vmojs /decorator pure decorator pattern, I’d like to revisit the topic of front-end data model applications and front-end object-oriented programming.
First, object-oriented design patterns are the proven success of any complex system design, but do the front-end years of Hooks mean we no longer need object-oriented design patterns? Does it mean that the once popular Class is no longer valuable?
Why to refuseClass
What are the issues that React Hooks and Vue 3’s Composition API currently addressing? Or, why are they so popular?
Easy to write
First of all, it is undeniable that Function does define a Function more senselessly than Class. Compared to the heavy definition inheritance, rendering, life cycle, state, it is really very light to complete the development of a component.
It’s also clear from the TypeScript definition that those of you who have developed relatively robust components using Class know the following component properties that React defines for contextual communication and validation:
React.Component<IProps, IState>
Component.defaultProps
Component.propTypes
Component.contextType
Component.childContextTypes
Component.getChildContext
These methods are excruciating in the derivation of TypeScript type definitions, often requiring various type assertions to continue. When it comes to type validation, writing propTypes after ts is more like writing a tasteless essay.
The writing rules of Function make full use of the Function derivation process of TS, so that its type only needs to be defined once, and the component input parameter declaration is carried out purely by means of the type inference of TS, which greatly reduces the development and maintenance work of developers on such repeated definitions.
Easy to use
Hooks provide reuse capabilities that decouple logic from state independently.
Previously this capability was mostly used in various rendering engines or system designs in the form of Mixin, HOC, etc. From the point of view of usage, this design can achieve the same effect of partial logic reuse and partial component interaction. However, it also brings great difficulty to the debugging process. I once tried to live and die in a rendering engine based on HOC design, because there are nearly 8 layers of HOC to handle the non-presentation logic required by a component rendering, which seems to be reasonably layered and logical at first sight. However, the following problems may arise when using the reverse:
- Consume black boxes, which higher-order functions tend to consume during component rendering
Props
And even secretly eat someProps
, developers encounter someProps
It often takes a long time to figure out which layer the problem is at after you have passed down the exception - The combination is poor. In the process of designing higher-order functions at the beginning, people often feel that their flexibility and reusability should be very high. They only need to wrap the components to reuse this part of logic ability. However, this iterator pattern is designed if multiple
HOC
Combined use, there will often be a dependency, which brings great hidden trouble in the process of use - React Devtools makes it very difficult to view and debug components using Wrapper Hell
With the help of a state encapsulation and composition, it is possible to abstract the logic that needs to be handled by the React Hooks component. The composition mode is more flexible, requiring developers to understand the concepts of the render cycle of the React Hooks component to combine the logic of different states without side effects.
Easy to understand
Hooks and Component packaging is with the Function call, the developer can see understand it into and out of the cords, the distinction between Component and Hooks are relatively clear, with useXXXX Cxxxxx distinction.
In general, the Function approach is cleaner and easier to understand than the Class approach of reusing logical abstractions.
Why leaveClass
I’ve just described why Class is being phased out of mainstream front-end frameworks, but what I want to focus on here is why Class is needed in current front-end projects and what problems it solves.
First, let’s take a look at why major server-side frameworks use a class-based approach to system design:
Data materialization
Using Class definition, we can transform a vaguely defined data content in the transmission process into a Class with clear field and type, so as to complete the safe call and clearly deal with the data flow relationship.
How to understand the above sentence, LET me give a few simple examples:
Let’s say we need to render an order form and get the following data:
[{"buyerAvatar": "https://xxxxxx.cdn.com/avatar.png"."buyerId": 1000001."createTime": 1637230483."orderNumber": "60000000000001"."orderType": "Normal"."preOrder": false."shippingFee": 1.49."deliveryType": "STANDARD"."buyerName": "today"."freeGift": false."skus": [{"createTime": 1637230483."createDate": "10 Sep 2021"."image": "https://xxxxxx.cdn.com/product.png"."itemStatus": "repacked"."orderItemId": "60676801622031"."orderType": 0."skuName": "Trade Name - Black"."productTitle": "Trade Name"."quantity": 1."skuId": "test0000001-black"."skuInfo": "1:black"."unitPrice": "200.00"}]."status": "topack"."totalQuantity": 1."totalRetailPrice": "0.00"."totalUnitPrice": "200.00"}]Copy the code
When such a data is obtained by the front end, there will be the following problems:
- It is not very intuitive to see the data content described, and each field needs to be proofread and understood before it can be used
- The relationship between the data described cannot be clearly seen, and the information cannot be clearly understood by the type
- When there is some interaction in the joint judgment interface of multiple data (e.g., tabStatus is unpacked, showing an action), this kind of judgment logic becomes bloated in the rendering process and difficult to trace back to during debugging inspections
Of course, in the process of front-end data analysis and use, there are still very, very many front-end developers helpless and ridicule.
In fact, these problems are really data processing problems. And in the face of these problems, perennial and data dealing with the server-side students how to do?
Class, the data model, and so on. Let’s see how the data model works:
First of all, the fuzzy data above will be transformed into a clearer data structure like the above after front-end instantiation transformation. It is suggested to compare the data and see the figure on both sides
Then it becomes very transparent in terms of data. For example, the data required for an order alphabet rendering is:
interface List {
list: Order[];
pageSize: number;
total: number;
current: number;
}
Copy the code
Because it is an entity class, it becomes extremely clear during data consumption.
When data processing needs to be extended, it becomes logical to extend the method under the corresponding model.
A decorator
As we all know, after the adoption of ES7’s JS decorator proposal, @xxxx decorator has been actively adopted and utilized by major server frameworks, such as Midway and Nex.js
With the help of decorator and class metadata, dependency injection and other design patterns derived from reference, the server data, service and consumption have been flexibly combined to play incisively and vividly.
In the past, it was necessary to use a variety of directory definitions, or even special naming rules to complete the mutual call and combination, but now only need to modify according to different decorators can be freely combined in any position of the project, it has to say that in this server-side data transmission, service call has a very epochal significance.
TypeScript
TypeScript, Microsoft’s compiled type language, is popular in major frameworks and front-end communities.
We all know that the Class model is highly respected in Microsoft projects, such as Vscode and Monico Editor. Those of you who have developed Vscode plug-ins will obviously feel that the concept of Class is in the blood within Microsoft.
Even in a sense, TypeScript is a syntactic superset that Microsoft has expanded beyond JavaScript’s current support for classes and types. With this superset, Microsoft can leverage JS capabilities to complete complex and stable projects like Vscode.
As a result, TypeScript’s support for classes is like a son of your own.
Using class-defined data models in TypeScript makes you feel like you already know what editor you’re writing.
Recently combined with Github Copilot used, but also feel as long as the definition of a good data model, write what function to a line of annotations is enough 😂
Why doVmo
I have already talked a lot about the topic of Class and the two trends in community development. Now I want to talk about how I implemented the combination in the project.
I always believe that these are the advantages of the two fields. The problems they solve and the content they deal with are different.
There are better, simpler invocation methods for components, for interface rendering, and more flexible abstraction methods for state logic. There is no reason not to use them. So for component rendering, I use React Hooks.
For data processing, data transformation and data consumption that need long-term maintenance, I choose data model.
They play their respective roles and complement each other, which can be verified in the project. Context + Model mode can significantly improve the overall stability and maintenance of the project.
This sounds like a design pattern, a programming habit, why @vmojs/decorator?
Decorator @vmojs/decorator is primarily used to solve the problem of repetitive, tasteless assignment during data initialization and reduce the amount of glue code.
Let’s say I need to instantiate an Order class:
class Order {
id: string;
price: number;
status: EStatus;
skus: Sku[];
createTime: Date;
constructor(data: IRemoteOrderDTO) {
this.id = data.orderNumber;
this.price = data.totalUnitPrice;
this.status = data.status;
this.skus = data.skus.map((sku) = > new Sku(sku));
this.createTime = new Date(data.createTime); }}// new Order(data) => Order;
Copy the code
This assignment process can be done quickly with Vmo, and the field assignment conversion logic is all in one place when fields are added in the future. As the model size increases, you don’t want to repeat the transformation process by scrolling up and down:
import { Vmo } from "@vmojs/decorator";
@Vmo(a)class Order {
// This is defined for ts code hints
constructor(data: IRemoteOrderDTO) {}
@Vmo("orderNumber")
id: string;
@Vmo("totalUnitPrice")
price: number;
@Vmo(a)status: EStatus;
@Vmo(({ skus }) = > skus.map((sku) = > new Sku(sku)))
skus: Sku[];
@Vmo(({ createTime }) = > new Date(createTime))
createTime: Date;
}
Copy the code
conclusion
In a sense, we are all in a river’s lake, so is the development of the front-end community, a framework, a design model, a hot open source project are countless behind the crystallization of thinking.
Is not a framework is authority, a practice is perfect, just the author scenarios of best practice at the time, so the more skilled developers, the more want to know the development of a technology stack, it is more beneficial to understand the thinking behind it represent, behind which represented by the trend, represented by the river’s lake behind it.
Anyway, this article is about a library of tools, but most of it is about sharing my personal best practices for front-end developers handling data in the current era. Hope to help you! 😄