In the idle fish deep use of fluent development process, we encountered business code coupling is serious, code maintainability is poor, such as into the mud. For responsible business scenarios like idle fish, we need a unified application framework to get rid of the current development dilemma, and this is also a virgin land in the field of flutter.
Fish Redux is an upper application framework to solve the above problems. It is a assembled flutter application framework based on Redux data management, which is especially suitable for building large and medium-sized complex applications.
On the one hand, a large page is disassembled into independent component | adapter for view and data. The upper layer is responsible for the assembly and the lower level is responsible for implementation. On the other hand, the component | adapter is divided into independent context free functions such as view, reducer and effect. So it will be very clean, easy to write, easy to maintain, easy to collaborate.
The inspiration of fish Redux is mainly from excellent frameworks such as Redux, react, elm and DVA. While fish Redux stands on the shoulders of giants, it will further focus, divide, reuse and isolate.
The main body is divided into three layers from bottom to top. Each layer is used to solve problems and contradictions at different levels. The following is the sequence.
- Redux is a data management framework from the front-end community. It may be a little strange to native developers. Let’s give a brief introduction.
What does Redux do?
- Redux is a predictable and easy to debug data management framework. Redux is responsible for all data addition, deletion, modification and query.
How is Redux designed and implemented?
Redux is a functional data management framework.
Traditional OOP data management usually defines some beans, and each bean exposes some public API to operate internal data (congestion model). The functional approach is a more abstract dimension. The definition of data is some structure (anemia model), and the methods of operating data are unified into reducers with the same function signature (T, action) = > t. FP: structure + reducer= OOP:Bean (hyperemia model) At the same time, Redux adds middleware (AOP) mode and subscribe mechanism which are commonly used in FP, which brings high flexibility and expansibility to the framework. Reference of anemia model and hyperemia model [https://en.wikipedia.org/wiki/Plain_old_Java_object](https://en.wikipedia.org/wiki/Plain_old_Java_object)
Disadvantages of Redux
- Redux core only cares about data management, and doesn’t care about the specific scenarios to use it. This is its advantages and disadvantages.
In our actual use of Redux, we face two specific problems
- The contradiction between the centralization of Redux and the divide and conquer of component.
- The reducer of Redux needs to be manually assembled layer by layer, which is cumbersome and error prone.
Improvement of fish Redux
Fish Redux does centralized and observable data management through redux. However, not only that, for the shortcomings of traditional Redux in the use level, we have improved it through better and higher abstraction in the development scenario of end-to-side fluent page latitude.
A component needs to define a structure and a reducer. At the same time, there is a parent dependent relationship between components. Through this layer of dependency,
We have solved the contradiction between centralization and divide and conquer. At the same time, the manual level combination of reducer is automatically completed by the framework, which greatly simplifies the difficulty of using redux.
We get the ideal centralized effect and divide and conquer code.
Follow up on community standards
- The concepts of state, action, reducer, store and middleware are completely consistent with the reduxjs of the community. We will keep all the advantages of Redux as it is.
- For a closer understanding of Redux, please refer to https://github.com/reduxjs/redux
Component is the encapsulation of local display and function. Based on the principle of Redux, we subdivide the function into the function of modifying data (reducer) and the function of non modifying data (effect).
So we get three parts: view, effect and reducer, which are called the three elements of components, which are responsible for the display of components, the behavior of non modifying data, and the behavior of modifying data.
This is a kind of splitting for the present and for the future. In Redux’s view, it’s data management and others. In the future oriented UI automation, it’s UI representation and others.
For programmers, UI expression is about to enter the black box era. R & D engineers will focus more on the behavior of non modifying data and modifying data.
Components divide and conquer views and data. Through layer by layer divide and conquer, we divide the complex page and data into independent small modules. This will facilitate collaborative development within the team.
View is just a function signature: (T, dispatch, viewservice) = > widget
It mainly contains three aspects of information
- Views are completely data driven.
- The event / callback generated by the view sends out the “intention” through the dispatch without any concrete implementation.
The required component dependencies are called through viewservice standardization.
For example, a typical function that conforms to the view signature[ image.png ] https://img.alicdn.com/tfs/TB1ymUNCgHqK1RjSZFPXXcwapXa-1198-1098.png )
Effect is a standard definition of non modified data behavior. It is a function signature: (context, action) = > object
It mainly contains four aspects of information
- Receive the “intention” from the view, including the callback of the corresponding lifecycle, and then make the specific execution.
- Its processing may be an asynchronous function, and the data may be modified in the process, so we don’t advocate holding the data, but get the latest data through the context.
- It does not modify the data. If necessary, it should send an action to the reducer for processing.
Its return value is only limited to bool or future, corresponding to the processing flow supporting synchronous functions and coroutines.
For example: the support of good cooperative process[ image.png ] https://img.alicdn.com/tfs/TB1bTgVChYaK1RjSZFnXXa80pXa-1256-944.png )
Reducer is a function signature that fully conforms to the Redux specification: (T, action) = > t
Some reducers that match the signature
At the same time, we use explicit configuration to complete the registration of small components and adapters that large components depend on. This dependency configuration is called dependencies.
So there is the formula component = view + effect (optional) + reducer (optional) + dependencies (optional).
A typical assembly
Through the abstraction of component, we get complete divide and conquer, multi latitude reuse and better decoupling.
The adapter also encapsulates the local display and function. It was born for the listview high performance scenario and is a change in component implementation.
Its goal is to solve three problems of component model in the context of fluent listview
- 1) If you put a “big cell” in component, you can’t enjoy the performance optimization of listview code.
- 2) Component cannot distinguish between appear | discover and init | dispose.
3) The life cycle of effect and the coupling of view do not meet the intuitive expectation in the scenario of listview.
In summary, we want a logical Scrollview, a performance listview, such an abstraction of partial presentation and functional encapsulation. To make such a separate level of abstraction is, Let's look at the actual effect. We don't use the framework for the page, use the framework component, and compare the performance baseline of the framework component + adapter
- Reducer is long-lived, Effect is medium-lived, View is short-lived.
Through continuous testing, we take an Android machine as an example
- Use the FPS of our detail page before using the framework, and the baseline is at 52fps.
- Using the framework and only using the component abstraction, FPS drops to 40 and encounters the “big cell” trap.
- After using the framework and adapter abstraction, FPS is upgraded to 53, which is higher than the baseline, with a small improvement.
The recommended directory structure would look like this
sample_page -- action.dart -- page.dart -- view.dart -- effect.dart -- reducer.dart -- state.dart components sample_component -- action.dart -- component.dart -- view.dart -- effect.dart -- reducer.dart -- state.dart
The upper layer is responsible for assembly, and the lower layer is responsible for implementation. At the same time, there will be a plug-in provided for us to fill in quickly.
Take the detail scene of idle fish as an example
Components and components, components and containers are completely independent.
- Communication within component | adapter
Communication between components and adapters
![image.png](https://img.alicdn.com/tfs/TB1GrISCkzoK1RjSZFlXXai4VXa-1846-986.png) Simple description: self first broadcast is adopted, which has a priority processing. The sent action should be handled by itself first, otherwise it will be broadcast to other components and Redux for processing. Finally, through a simple and intuitive dispatch, we complete all communication demands within and between components (parent to child, child to parent, brothers, etc.).
- Local data modification automatically triggers shallow copy of upper layer data layer by layer, which is transparent to upper layer business code.
Layers of data copies
- On the one hand, it strictly follows the modification of Redux data.
On the other hand, it is also a strict follow-up of data-driven presentation.
All components are informed of flattening, and the components determine whether they need to be refreshed through shouldupdate
Centralized management of data
- Centralized and observable data management through redux. We will keep all the advantages of Redux, and at the same time, on the merger of reducers, it will be automatically completed by the framework agent, which greatly simplifies the complexity of using redux.
Divide and conquer management of components
- Components divide and conquer both views and data. Through layer by layer divide and conquer, we divide the complex page and data into independent small modules. This will facilitate collaborative development within the team.
View, reducer, effect isolation
- The component is divided into three stateless and independent functions. Because it is a stateless function, it is easier to write, debug, test and maintain. At the same time, it brings more possibilities of composition, reuse and innovation.
Declarative configuration assembly
- Components and adapters are completed through free declarative configuration assembly. It includes its view, reducer, effect and its dependent subitems.
The core framework maintains its own three-tier core concerns, and does not do anything beyond the core concerns. At the same time, it maintains flexible scalability for the upper layer.
- The framework doesn’t even have a single line of printed code, but we can observe the flow of data and the changes of components through the standard middleware.
- In addition to the core three layers of the framework, we can also add mixins to component or adapter through DART’s language features, so as to enhance the customization and ability of their upper layer usage flexibly.
- The framework and other middleware, such as automatic exposure, high availability, are transparent and freely assembled by the upper layer.
Small, simple and complete
- It’s very small, just over 1000 lines of code.
- It is easy to use, complete a few small functions, complete the assembly, can run.
- It is complete.
At present, fish Redux has been applied in many scenes within Alibaba’s idle fish technology team.
The author of this paper is Jifeng, an idle FISH technique
Read the original
This article is the original content of yunqi community, which can not be reproduced without permission.