Welcome to @medux. I suggest you read the following 4 articles in turn, which will take you about 30 minutes.
- Why do you need @medux
- @medux Basic concept overview
- @ medux routing
- @ medux data flow
Why do you need @medux
— Github address —
One-stop solution
Typically, a front-end project includes the following functions:
- UI Rendering framework
- State management
- Routing management
- Modular management (including module definition, loading and maintenance)
- Structured management (how to organize documents and resources)
The UI framework is closely related to the host platform, is independent and complex, and there are often many different styles of solutions to choose from. Other functions are relatively simple and can be abstracted into a universal cross-platform JS runtime.
So in simple terms, @ medux want to create a different third party can be docking UI framework of general front-end framework, it contains a unified state management, routing management, modular, structured management function, can run in support of JS runtime platform, which is popular nowadays, cross-platform across the front-end engineering solutions.
State to drive
You may be wondering if you need a separate state management layer, since writing state management in the UI rendering layer seems to work just fine. But according to @medux, not only do you need to separate them from the UI, but you should also use state to drive the entire application because:
- The state layer tends to be more abstract and stable, the UI layer more complex and changeable, and stripping out stable things can reduce changes
- UI is purer after stripping State management: UI=Render(State)
- Regardless of the lifecycle of UI components and various hooks, state management is easier and more intuitive
- Not associated with a specific UI, facilitating reuse and multi-platform
Another Flux framework based on Redux that also supports Mutable Data
Fans of Vue or Mobx may ask, does Medux require mutable or immutable data?
Although Medux is based on Redux, the principle of pragmatism does not require strict adherence to the Redux model, which is another Flux framework.
The Medux framework uses ImmutableData internally to automatically generate and manage state and its level 1 nodes, and you usually don’t have to intervene with this built-in data structure. For the secondary moduleState, you can define it as a MutableData, and then directly modify the state in the Reducer and return it. Although this is against the reducer’s intent, it is the simplest and most flexible way to communicate with MutableData.
Looser collaboration across Modules
Cross-module invocation and collaboration are essential in complex, long business processes. Many frameworks support modularity and cross-module Dispatch actions, but they tend to only support active invocation, such as:
login(){
...
if(response.success){
dispatch({type: 'moduleB/someType'});
dispatch({type: 'moduleC/someType'}); }}Copy the code
Medux introduces a unique actionHandler mechanism that allows actions to have the Event feature, so you can use subscription listening mode in moduleB and moduleC:
{
@reducer
['moduleA.login'] () {/ /... doSomethings}}Copy the code
Armed to the teeth type inference
Medux claims to be a one-stop-shop front-end framework, but it’s more than just a patchwork of wheels and doesn’t want to be a loose hodgepodge, so it’s written in Typescript from the start and tightly integrates UI management, state management, modularity management, and type inference.
Route stateful
Remember that the widely popular routing framework React-Router has a concept called route componentization, which states that there is no need to distinguish between routing components and normal components, they are all the same. What this framework proposes is that routing stateful is not the domain of components at all.
To express in abstract terms:
- State = Combine(Route)
- UI = Render(State)
Medux views routing as a Store similar to Redux, which, like Redux, keeps track of the real-time state of the application. Whereas Redux is recorded in memory and maintained automatically by the program, Route is recorded in the browser address bar and maintained manually by the user. In Component you don’t have to distinguish between ReduxStore and RouteStore that cause UI changes; they’re all the same.
So some common routing components medux is not recommended, for example
<Switch>
<Route exact path="/admin/home" component="{AdminHome}" />
<Route exact path="/admin/role/:listView" component="{AdminRole}" />
<Route path="/admin/member/:listView" component="{AdminMember}" />
</Switch>
Copy the code
Their main problems are as follows:
- By binding routes to components, render is no longer pure and contains side effects of the external environment
- Hard-coding the path into the component is not conducive to late modification
- As a string, path loses type inference and checking
- Unable to cross platform and UI framework
So in @medux you can rewrite it as a normal component:
<Switch>{routeViews.adminHome? .Main &&<AdminHome />} {routeViews.adminRole? .List &&<AdminRole />} {routeViews.adminMember? .List &&<AdminMember />}
</Switch>
Copy the code
Elegant support for server isomorphic rendering SSR
Server Rendering is not a complex technology, and Server Rendering and Server isomorphism are two different concepts. The key point is: isomorphism. It’s not easy to make a set of code run perfectly in browsers and servers.
Main difficulty is that the Client side render time component lifecycle hooks bearing too many functions and side effects, such as: to get the data, routing, on-demand loaded, modularization, etc, these logic are scattered in various components with the rendering dynamic execution of components, and their execution cause components to render again, is simply:
Render -> Hooks -> Effects -> ReRender -> Hooks -> Effects…
This rendering process is not possible on the Server side because Sever will not ReRender normally, so all side effects must be performed in advance and then Render in one time.
Effects -> State -> Render
The solution is to take these side effects as far as possible out of the component lifecycle hooks and introduce a separate state management mechanism to manage them, making UI rendering pure PrueRender, which is the state-driven concept advocated by @medux.
More thorough modularization
A typical engineering structure using @medux:
SRC ├─ assets // Public Static Resources ├─ Entity // Store Business Entity Type Definition ├─ common // Store public code ├─ Components // Store UI Public Component ├─ modules │ ├─ App │ │ ├─ Exercises for Module │ │ ├─ Elements │ │ ├─ views │ │ ├─ TopNav │ │ │ ├ ─ ─ BottomNav │ │ │ └ ─ ─... │ │ ├ ─ ─ model. The model ts / / define the module │ │ └ ─ ─ index. The ts / / export this module │ ├ ─ ─ photos / / another module called photos │ │ └ ─ ─... │ ├ ─ ├ ─ imp // module setup &managementCopy the code
Other engineering structures commonly used on the Web:
SRC ├─ Assets // Public Static Resources // Common // Public code ├─ Components // UI Public Component ├─ // routers // Heavy // Store all kinds of layout version ├ ─ ─ pages / / store various pages ├ ─ ─ views / / store various views ├ ─ ─ store / / storage modular state management │ ├ ─ ─ modules │ │ ├ ─ ─ modelA │ │ ├ ─ ─ modelB │ │ └ ─ ─... └─ index. TsCopy the code
The comparison is as follows:
- Medux uses Module as a level of classification, which is divided into Model, Components, View and assets. Other common frameworks typically only use modularity for the Store (Model) portion, while Components, Views, and Assets are not well modularized
- Medux modules are based on high cohesion and low coupling business logic. Other common frameworks are often modular based on UI vision
- Medux bundles a module as a whole into a bundle that can be pluggable and loaded on demand. Other common frameworks pack a view as a bundle, and for a real business scenario, you usually need to plug and unplug the entire business function module, not just a view
- Medux has a clear positioning and boundary between View and Component: Component is a UI interactive control that can only pass values through props and cannot directly use ReduxStore, while View is a business view that can directly use ReduxStore. Other common frameworks don’t have a clear orientation for Components and Views, and are often visually subjective
- Medux only enforces a distinction between View and Component, because you don’t want to confuse users without a clear distinction. Other common frames include layouts, routers, and Pages. So here’s the question:
- In single page applications, the concept of page has become very vague. What is a page?
- UI components support nesting or slot slots, and the concept of layout has become vague
- Routing changes can cause UI loading and unloading, as can State changes. Why distinguish routing components from common components
Static and dynamic module loading mechanism
Modules can be loaded synchronously or asynchronously on demand. However, we should not mix the loading logic of modules with the business logic during development, which would make the problem more complicated. Medux views module loading as a configuration strategy that makes it easy to switch between synchronous and asynchronous module loading.
This is also the golden key to server homogeneous rendering (SSR), as Client rendering often involves chunk subcontracting of code to improve load speed and use asynchronous load on demand to optimize the user experience. On the Server side this becomes unnecessary and slows down the load.
@ medux overview
This framework is a precursor to another framework I wrote a few years ago, React-Coat, which is not pure because it is bundled with the React UI framework.
Now @medux is packaged into a series of NPM packages, from the abstract to the concrete, which you can either pick and re-develop, or use the platform UI integration package straight out of the box
Contains the following Packages
- Medux /core: Core base package
- @medux/web: Adapted to Web development, mainly embodied in History management
- Medux /route-plan-a: Implement a web style cross-platform routing scheme
- @medux/react: Adapt react development
- @medux/react-web-router: encapsulates @medux/core, @medux/web, @medux/route-plan-a, and @medux/react
- Medux /mini-program: Basic package for various applets
- @medux/wechat: integrated encapsulation of @medux/core, @medux/ Route-plan -A, @medux/mini-program, used to develop native wechat applets
- @medux/ Wechat – Redux-devTools: Use redux-devTools in the wechat applet environment
- • @medux/ React-Taro: integrates @medux/core, @medux/ Route-plan-a, and @medux/ Mini-Program into the Taro development framework
The following Packages are under development and not yet complete:
- @medux/ VUE-web-router: @medux/core combined with VUE, the idea is simple, directly modify the ModuleState in Reducer and return it
- @medux/react-native router: @medux/core combined with ReactNative
compatibility
Supports MODERN browsers IE8 and above. For Internet Explorer 11 and below, add polyfill and recompile the TS source in the SRC directory.
See details
Model code Style
Here’s a model using @medux to get a sense of its style:
// Just one class, action, Dispatch, Reducer, effect, loading
export class ModelHandlers extends BaseModelHandlers<State, RootState> {
@reducer
protected putCurUser(curUser: CurUser): State {
return{... this.state, curUser}; }@reducer
public putShowLoginPop(showLoginPop: boolean): State {
return{... this.state, showLoginPop}; }@effect('login') // Inject the loading state into the state whose key is login
public async login(payload: {username: string; password: string{})const loginResult = await sessionService.api.login(payload);
if(! loginResult.error) {this.dispatch(this.actions.putCurUser({curUser: loginResult.data}));
Toast.success('Welcome back! ');
} else{ Toast.fail(loginResult.error.message); }}// An ERROR in the model will trigger the medux.ERROR action, which will listen and send to the background
@effect(null) // Set to null to indicate that no trace loading is required
protected async ['medux.ERROR'](error: CustomError) {
if (error.code === '401') {
this.dispatch(this.actions.putShowLoginPop(true));
} else if (error.code === '301' || error.code === '302') {
// Route jump
historyActions.replace(error.detail);
} else {
Toast.fail(error.message);
awaitsettingsService.api.reportError(error); }}// Listen for your INIT Action to do some asynchronous data requests
@effect(a)protected async ['this.INIT'] () {const [projectConfig, curUser] = await Promise.all([settingsService.api.getSettings(), sessionService.api.getCurUser()]);
this.dispatch(
this.actions.updateState({ projectConfig, curUser, }) ); }}Copy the code
DispatchAction in view
Rich typescript type inference and reflection is one of medux’s strengths:
CoreAPI
Check out the CoreAPI documentation
The Demo with the case
- medux-react-adminBased on:
@medux/react-web-router
And the latestANTD 4.x
In addition to demonstrating how medux works, it also creates many unique ideas - medux-react-ssr: Fork from medux-react-adminServer isomorphic rendering, you can see how a SinglePage(
A single page application
) quickly convert to seO-enabled multi-page applications. - Medux-video-native: based on @medux/wechat, develop wechat applets in native language
- Medux-taro-taro: use taro3.0+react to develop small applications based on @medux/react-taro
Read on to the next article
A quick overview of basic medux concepts