说到底技术还是为业务服务的。最后以一篇阅读到的论文Seven Rules of Thumb for Web Site Experimenters上的一个例子来结束这个小节。简单来说我只想强调两点:
1) 不要盲目的、绝对的衡量性能的好坏;
2) 多从业务出发考虑问题
At Bing, we use multiple performance metrics for diagnostics, but our key time-related metric is Time-To-Success (TTS) [24], which side-steps the measurement issues. For a search engine, our goal is to allow users to complete a task faster. For clickable elements, a user clicking faster on a result from which they do not come back for at least 30 seconds is considered a successful click. TTS as a metric captures perceived performance well: if it improves, then important areas of the pages are rendering faster so that users can interpret the page and click faster. This relatively simple metric does not suffer from heuristics needed for many performance metrics. It is highly robust to changes, and very sensitive. Its main deficiency is that it only works for clickable elements. For queries where the SERP has the answer (e.g., for “time” query), users can be satisfied and abandon the page without clicking.class StopWatch { @observable currentTimestamp = 0; @action updateCurrentTimestamp = value => { this.currentTimestamp = value; }; } const stopWatch = new StopWatch(); // 堆代码 duidaima.com @inject("store") @observer class StopWatchApp extends React.Component { constructor(props) { super(props); const stopWatch = this.props.store; setInterval(() => stopWatch.updateCurrentTimestamp(Date.now())); } render() { const stopWatch = this.props.store; return <div>{stopWatch.currentTimestamp}</div>; } } ReactDOM.render( <Provider store={stopWatch}> <div> <StopWatchApp /> </div> </Provider>, document.querySelector("#app") );Redux 版本:
const UPDATE_ACTION = "UPDATE_ACTION"; const createUpdateAction = () => ({ type: UPDATE_ACTION }); const stopWatch = function( initialState = { currentTimestamp: 0 }, action ) { switch (action.type) { case UPDATE_ACTION: initialState.currentTimestamp = Date.now(); return Object.assign({}, initialState); default: return initialState; } }; const store = createStore( combineReducers({ stopWatch }) ); class StopWatch extends React.Component { constructor(props) { super(props); const { update } = this.props; setInterval(update); } render() { const { currentTimestamp } = this.props; return <div>{currentTimestamp}</div>; } } const WrappedStopWatch = connect( function mapStateToProps(state, props) { const { stopWatch: { currentTimestamp } } = state; return { currentTimestamp }; }, function(dispatch) { return { update: () => { dispatch(createUpdateAction()); } }; } )(StopWatch); ReactDOM.render( <Provider store={store}> <div> <WrappedStopWatch /> </div> </Provider>, document.querySelector("#app") );注意在上面的 Redux 版本代码中,每一个 StopWatch 直接订阅 store 中的 currentTimestamp 状态。在后面我们会尝试另一种方式。
ReactDOM.render( <Provider store={store}> <div> <WrappedStopWatch /> <WrappedStopWatch /> <WrappedStopWatch /> <WrappedStopWatch /> <WrappedStopWatch /> // ...省略后面的15个 </div> </Provider>, document.querySelector("#app") );你会感受到 Redux 明显出现了卡顿(通过肉眼就能观察出来,这里就不需要使用精确的时间显示差别了),或者说变化速率明显比 Mobx 版本更慢。这里就不贴视频或者是 gif 图了。各位运行代码就能一目了然。
const StopWatch = ({ currentTimestamp }) => { return <div>{currentTimestamp}</div>; }; class Container extends React.Component { constructor(props) { super(props); const { update } = this.props; setInterval(update); } render() { const { currentTimestamp } = this.props; return ( <div> <StopWatch currentTimestamp={currentTimestamp} /> // 省略剩下的 19 个 </div> ); } } const WrappedContainer = connect( function mapStateToProps(state, props) { const { stopWatch: { currentTimestamp } } = state; return { currentTimestamp }; }, function(dispatch) { return { update: () => { dispatch(createUpdateAction()); } }; } )(Container); ReactDOM.render( <Provider store={store}> <div> <WrappedContainer /> </div> </Provider>, document.querySelector("#app") );这段代码验证了我们的想法,修改之后程序变得健步如飞了,达到了和 Mobx 相同的显示速率。这也验证了我们的假设,dispatch确实会带来性能上的损失,但可怕的事情是dispatch是 Redux 事件机制的意志体现。这里我们不继续探究为什么dispatch的变慢的原因。
class App extends Component { render() { const { ids } = this.props; return ( <div> {ids.map(id => { return <Item key={id} id={id} />; })} </div> ); } }另一个关于 Mobx 与 Redux 性能对比测试的例子是来自于 Mobx 的作者 Michel Weststrate(好吧,这听上去就有失公允了),来自他的这篇 twitter
class Person { @observable firstName = "Michel"; @observable lastName = "Weststrate"; @observable nickName; @computed get fullName() { return this.firstName + " " + this.lastName; } } // Example React component that observes state const profileView = observer(props => { if (props.person.nickName) return <div>{props.person.nickName}</div> else return <div>{props.person.fullName}</div> });从代码中我们得到的依赖关系如下: