在我看来,是时候分享编写ReactJS App的方法了,但
我并没有声称它是独一无二的。可以跳过第一段 。 我从事网络开发已经很长时间了,但是在过去的四年里,我一直坐在ReactJS上,一切适合我,我一生中都有过redux,但是大约两年前我遇到了MobX,就在几个月前,我试图重做redux,但是我没有我可以,感觉到我在做多余的事情,也许根本就不对,服务器上的许多字节已经针对该主题进行了翻译,这篇文章不是关于一个字节一个字节的酷,这只是试图分享我的最佳实践,也许有人真的这种方法将会成功,如此地步。
我们将解决的任务:可以在
Github上查看该项目的结构。 因此,我将跳过如何编写原始应用程序,而本文仅重点介绍
我们介绍诸如数据模型,服务,方面之类的概念。
让我们得到一个简单的模型
TodoModel.tsimport { observable, action } from 'mobx'; export class TodoModel { @observable public id: number; @observable public text: string = ''; @observable public isCompleted: boolean = false; @action public set = (key: 'text' | 'isCompleted', value: any): void => { this[key] = value; }; }
您所看到的是设定的动作,在模型中,它不是一个好声音,而是一个好的基调,通常在项目中,有一个带有原始辅助函数的基本模型,并且它是从它那里继承而来的,在模型中,不应有任何好的动作。
现在,我们需要学习如何使用此模型,并启动服务:
TodoService.ts import { Service, Inject } from 'typedi'; import { plainToClass, classToClass } from 'class-transformer'; import { DataStorage } from '../storage/DataStorage'; import { action } from 'mobx'; import { TodoModel } from '../models/TodoModel'; const responseMock = { items: [ { id: 1, isCompleted: false, text: 'Item 1' }, { id: 2, isCompleted: true, text: 'Item 2' } ] }; @Service('TodoService') export class TodoService { @Inject('DataStorage') public dataStorage: DataStorage; @action public load = async () => { await new Promise(resolve => setTimeout(resolve, 300)); this.dataStorage.todos = plainToClass(TodoModel, responseMock.items); }; @action public save(todo: TodoModel): void { if (todo.id) { const idx = this.dataStorage.todos.findIndex(item => todo.id === item.id); this.dataStorage.todos[idx] = classToClass(todo); } else { const todos = this.dataStorage.todos.slice(); todo.id = Math.floor(Math.random() * Math.floor(100000)); todos.push(todo); this.dataStorage.todos = todos; } this.clearTodo(); } @action public edit(todo: TodoModel): void { this.dataStorage.todo = classToClass(todo); } @action public clearTodo(): void { this.dataStorage.todo = new TodoModel(); } }
我们的服务有一个链接到
数据存储 import { Service } from 'typedi'; import { observable } from 'mobx'; import { TodoModel } from '../models/TodoModel'; @Service('DataStorage') export class DataStorage { @observable public todos: TodoModel[] = []; @observable public todo: TodoModel = new TodoModel(); }
在此存储中,我们将存储应用程序的状态,可以有很多这样的存储,但是如实践所示,进入许多小型存储没有意义。 在商店以及模型中,不应执行任何操作。
我们几乎准备就绪,仍然可以将所有内容连接到我们的应用程序,为此,我们将稍微拧紧注射器使其免受mobx反应的影响:
DI import { inject } from 'mobx-react'; export function DI(...classNames: string[]) { return (target: any) => { return inject((props: any) => { const data: any = {}; classNames.forEach(className => { const name = className.charAt(0).toLowerCase() + className.slice(1); data[name] = props.container.get(className); }); data.container = props.container; return data; })(target); }; }
并为我们的DI获得一个容器
browser.tsx import 'reflect-metadata'; import * as React from 'react'; import { hydrate } from 'react-dom'; import { renderRoutes } from 'react-router-config'; import { Provider } from 'mobx-react'; import { BrowserRouter } from 'react-router-dom'; import { Container } from 'typedi'; import '../application'; import { routes } from '../application/route'; hydrate( <Provider container={Container}> <BrowserRouter>{renderRoutes(routes)}</BrowserRouter> </Provider>, document.getElementById('root') );
对于浏览器,我们始终只有一个容器,但是对于您需要查看的服务器渲染,最好为每个请求组织容器:
server.tsx import * as express from 'express'; import * as React from 'react'; import { Container } from 'typedi'; import '../application';
服务器渲染实际上是一件微妙的事情,一方面,我想让所有内容通过它,
但是它只有一个业务任务, 即向机器人提供内容 ,因此最好检查一下“用户是否至少在网站上登录一次”之类的内容。 ,并跳过在服务器上创建容器的服务器渲染。
好了,现在我们的组件:
MainRoute.tsx import * as React from 'react'; import { TodoService } from '../service/TodoService'; import { observer } from 'mobx-react'; import { DI } from '../annotation/DI'; import { DataStorage } from '../storage/DataStorage'; import { Todo } from '../component/todo'; import { Form } from '../component/form/Form'; import { ContainerInstance } from 'typedi'; interface IProps { todoService?: TodoService; dataStorage?: DataStorage; } @DI('TodoService', 'DataStorage') @observer export class MainRoute extends React.Component<IProps> { public static async loadData(container: ContainerInstance) { const todoService: TodoService = container.get('TodoService'); await todoService.load(); } public componentDidMount() { this.props.todoService.load(); } public render() { return ( <div> <Form /> <ul> {this.props.dataStorage.items.map(item => ( <li key={item.id} ><Todo model={item} /></li> ))} </ul> </div> ); } }
一切都变得十分逻辑和美观,我们用于绘制的“渲染”视图从我们的商店中获取数据,组件挂钩表示应该在什么时间加载数据。
待办事项 import * as React from 'react'; import { TodoModel } from '../../models/TodoModel'; import { TodoService } from '../../service/TodoService'; import { DI } from '../../annotation/DI'; import { observer } from 'mobx-react'; interface IProps { model: TodoModel; todoService?: TodoService; } @DI('TodoService') @observer export class Todo extends React.Component<IProps> { public render() { const { model, todoService } = this.props; return ( <> <input type='checkbox' checked={model.isCompleted} onChange={e => model.set('isCompleted', e.target.checked)} /> <h4>{model.text}</h4> <button type='button' onClick={() => todoService.edit(model)}>Edit</button> </> ); } }
Form.tsx import * as React from 'react'; import { observer } from 'mobx-react'; import { DI } from '../../annotation/DI'; import { TodoService } from '../../service'; import { DataStorage } from '../../storage'; import { TextField } from '../text-field'; interface IProps { todoService?: TodoService; dataStorage?: DataStorage; } @DI('TodoService', 'DataStorage') @observer export class Form extends React.Component<IProps> { public handleSave = (e: any) => { e.preventDefault(); this.props.todoService.save(this.props.dataStorage.todo); }; public handleClear = () => { this.props.todoService.clearTodo(); }; public render() { const { dataStorage } = this.props; return ( <form onSubmit={this.handleSave}> <TextField name='text' model={dataStorage.todo} /> <button>{dataStorage.todo.id ? 'Save' : 'Create'}</button> <button type='button' onClick={this.handleClear}> Clear </button> </form> ); } }
我认为,通过模型/ dto使用表单更方便,您可以使用通常的本机表单,并更新数据模型,所有监听该模型的人都将立即更新。
像这样的东西我使用了这堆库:react,class-transformer,mobx,typedi
我们现在在产品中使用这种方法,这些都是具有共同的通用组件和服务的大型项目。
如果这种方法很有趣,我将告诉您同样的方法,我们如何在将模型发送到服务器之前验证模型,如何处理服务器错误以及如何在浏览器标签之间同步状态。
实际上,所有东西都是非常好的:“类验证器”,“ localStorage + window.addEventListener('storage')”
感谢您的阅读:-)
例子