We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
由于redux需要写很多繁琐的action和reducer,大部分项目也没有复杂到需要用到redux的程度,导致不少人对redux深恶痛绝。mobx是另一种状态管理方案,这里分享一下我最近使用mobx的经验。
我最喜欢mobx的地方就是和vue一样的数据监听,框架底层通过Object.defineProperty或Proxy来劫持数据,对组件可以进行更细粒度的渲染。在react中反而把更新组件的操作(setState)交给了使用者,由于setState的"异步"特性导致了没法立刻拿到更新后的state。
想像一下,在redux中,如果一个值A是由另外几个值B、C、D计算出来的,在store中该怎么实现?
如果要实现这么一个功能,最麻烦的做法是在所有B、C、D变化的地方重新计算得出A,最后存入store。
当然我也可以在组件渲染A的地方根据B、C、D计算出A,但是这样会把逻辑和组件耦合到一起,如果我需要在其他地方用到A怎么办?
我甚至还可以在所有connect的地方计算A,最后传入组件。但由于redux监听的是整个store的变化,所以无法准确的监听到B、C、D变化后才重新计算A。
但是mobx中提供了computed来解决这个问题。正如mobx官方介绍的一样,computed是基于现有状态或计算值衍生出的值,如下面todoList的例子,一旦已完成事项数量改变,那么completedCount会自动更新。
class TodoStore { @observable todos = [] @computed get completedCount() { return (this.todos.filter(todo => todo.isCompleted) || []).length } }
reaction则是和autorun功能类似,但是autorun会立即执行一次,而reaction不会,使用reaction可以在监听到指定数据变化的时候执行一些操作,有利于和副作用代码解耦。
// 当todos改变的时候将其存入缓存 reaction( () => toJS(this.todos), (todos) => localStorage.setItem('mobx-react-todomvc-todos', JSON.stringify({ todos })) )
mobx中的store的创建偏向于面向对象的形式,mobx官方给出的例子todomvc中的store更接近于mvc中的model。
但是这样也会带来一个问题,业务逻辑我们应该放到哪里?如果也放到store里面很容易造成不同store之间数据的耦合,因为业务代码必然会耦合不同的数据。
我参考了dobjs后,推荐将store拆分为action和dataModel两种。
action和dataModel一起组合成了页面的总store,dataModel只存放UI数据以及只涉及自身数据变化的action操作(在mobx严格模式中,修改数据一定要用action或flow)。
action store则是负责存放一些需要使用来自不同store数据的action操作。
我个人理解,dataModel更像MVC中的model,action store是controller,react components则是view,三者构成了mvc的结构。
- stores - actions - hotelListAction.js - dataModel - globalStatus.js - hotelList.js - index.js // globalStatus class GlobalStatus { @observable isShowLoading = false; @action showLoading = () => { this.isShowLoading = true } @action hideLoading = () => { this.isShowLoading = false } } // hotelList class HotelList { @observable hotels = [] @action addHotels = (hotels) => { this.hotels = [...toJS(this.hotels), ...hotels]; } } // hotelListAction class HotelListAction { fetchHotelList = flow(function *() { const { globalStatus, hotelList } = this.rootStore globalStatus.showLoading(); try { const res = yield fetch('/hoteList', params); hotelList.addHotels(res.hotels); } catch (err) { } finally { globalStatus.hideLoading(); } }).bind(this) }
observer可以给组件增加订阅功能,一旦收到数据变化的通知就会将组件重新渲染,从而做到更细粒度的更新,这是redux和react很难做到的,因为react中组件重新渲染基本是依赖于setState和接收到新的props,子组件的渲染几乎一定会伴随着父组件的渲染。 也许很多人没有注意到,mobx-react中还提供了一个Observer组件,这个组件接收一个render方法或者render props。
const App = () => <h1>hello, world</h1>; <Observer>{() => <App />}</Observer> <Observer render={() => <App />} />
也许你要问这个和observer有什么区别?还写的更加复杂了,下面这个例子对比起来会比较明显。
import { observer, Observer, observable } from 'mobx-react' const App = observer( (props) => <h1>hello, {props.name}</h1> ) const Header = (props) => <h1>this is header</h1> const Footer = (props) => <h1>this is footer</h1> const Container = observer( (props) => { return ( <> <Header /> <App name={props.person.name} /> <Footer /> </> ) } ) const person = observable({name: "gyyin"}); render(<Container person={person} />, document.getElementById("app")); person.name = "world";
上面这个代码,Container组件监听到person.name改变的时候会重新渲染,这样就导致了原本不需要重新渲染的Header和Footer也跟着渲染了,如果使用Observer就可以做到更细粒度的渲染。
const App = (props) => <h1>hello, {props.name}</h1> const Header = (props) => <h1>this is header</h1> const Footer = (props) => <h1>this is footer</h1> const Container = (props) => { return ( <> <Header /> <Observer render={ () => <App name={props.person.name} /> }> <Footer /> </> ) } const person = observable({name: "gyyin"}); render(<Container person={person} />, document.getElementById("app")); person.name = "world";
如果在Header和Footer里面做console.log,你会发现只有被Observer包裹的App组件进行了重新渲染,由于Container没有订阅数据变化,所以也不会重新渲染。
但如果不是对性能有极致的追求,observer已经足够了,大量的Observer会花费你很多精力来管理渲染问题。
参考链接:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
由于redux需要写很多繁琐的action和reducer,大部分项目也没有复杂到需要用到redux的程度,导致不少人对redux深恶痛绝。mobx是另一种状态管理方案,这里分享一下我最近使用mobx的经验。
更响应式
我最喜欢mobx的地方就是和vue一样的数据监听,框架底层通过Object.defineProperty或Proxy来劫持数据,对组件可以进行更细粒度的渲染。在react中反而把更新组件的操作(setState)交给了使用者,由于setState的"异步"特性导致了没法立刻拿到更新后的state。
computed
想像一下,在redux中,如果一个值A是由另外几个值B、C、D计算出来的,在store中该怎么实现?
如果要实现这么一个功能,最麻烦的做法是在所有B、C、D变化的地方重新计算得出A,最后存入store。
当然我也可以在组件渲染A的地方根据B、C、D计算出A,但是这样会把逻辑和组件耦合到一起,如果我需要在其他地方用到A怎么办?
我甚至还可以在所有connect的地方计算A,最后传入组件。但由于redux监听的是整个store的变化,所以无法准确的监听到B、C、D变化后才重新计算A。
但是mobx中提供了computed来解决这个问题。正如mobx官方介绍的一样,computed是基于现有状态或计算值衍生出的值,如下面todoList的例子,一旦已完成事项数量改变,那么completedCount会自动更新。
reaction
reaction则是和autorun功能类似,但是autorun会立即执行一次,而reaction不会,使用reaction可以在监听到指定数据变化的时候执行一些操作,有利于和副作用代码解耦。
拆分store
mobx中的store的创建偏向于面向对象的形式,mobx官方给出的例子todomvc中的store更接近于mvc中的model。
但是这样也会带来一个问题,业务逻辑我们应该放到哪里?如果也放到store里面很容易造成不同store之间数据的耦合,因为业务代码必然会耦合不同的数据。
我参考了dobjs后,推荐将store拆分为action和dataModel两种。
action和dataModel一起组合成了页面的总store,dataModel只存放UI数据以及只涉及自身数据变化的action操作(在mobx严格模式中,修改数据一定要用action或flow)。
action store则是负责存放一些需要使用来自不同store数据的action操作。
我个人理解,dataModel更像MVC中的model,action store是controller,react components则是view,三者构成了mvc的结构。
细粒度的渲染
observer可以给组件增加订阅功能,一旦收到数据变化的通知就会将组件重新渲染,从而做到更细粒度的更新,这是redux和react很难做到的,因为react中组件重新渲染基本是依赖于setState和接收到新的props,子组件的渲染几乎一定会伴随着父组件的渲染。
也许很多人没有注意到,mobx-react中还提供了一个Observer组件,这个组件接收一个render方法或者render props。
也许你要问这个和observer有什么区别?还写的更加复杂了,下面这个例子对比起来会比较明显。
上面这个代码,Container组件监听到person.name改变的时候会重新渲染,这样就导致了原本不需要重新渲染的Header和Footer也跟着渲染了,如果使用Observer就可以做到更细粒度的渲染。
如果在Header和Footer里面做console.log,你会发现只有被Observer包裹的App组件进行了重新渲染,由于Container没有订阅数据变化,所以也不会重新渲染。
但如果不是对性能有极致的追求,observer已经足够了,大量的Observer会花费你很多精力来管理渲染问题。
参考链接:
The text was updated successfully, but these errors were encountered: