版本:V5.0.2 背景 使用ECharts的lazyUpdate模式多次执行setOption,仅对最终的option渲染一次。ECharts是和React一致自己单独实现了一套任务管理机制,还是基于setTimeout或Promise做的异步渲染?我们尝试从ECharts的源码中来一探究竟。
结论 代码挺好跟,但涉及到Even loop深入理解起来会相对困难。简单说明就是采用lazyUpdate更新图表的话,图表会在下一个 animation frame 中更新而在下一个animation frame之前执行的setOption会根据notMerge参数来判断是合并option还是采用最后一次option直接渲染。
定位实现代码
极好定位这里不再赘述
echarts.ts - apache/echarts
核心逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 setOption<Opt extends ECBasicOption >(option : Opt , notMerge ?: boolean | SetOptionOpts , lazyUpdate ?: boolean ): void { if (__DEV__) { assert (!this [IN_MAIN_PROCESS_KEY ], '`setOption` should not be called during main process.' ); } if (this ._disposed ) { disposedWarning (this .id ); return ; } let silent; let replaceMerge; let transitionOpt : SetOptionTransitionOpt ; if (isObject (notMerge)) { lazyUpdate = notMerge.lazyUpdate ; silent = notMerge.silent ; replaceMerge = notMerge.replaceMerge ; transitionOpt = notMerge.transition ; notMerge = notMerge.notMerge ; } this [IN_MAIN_PROCESS_KEY ] = true ; if (!this ._model || notMerge) { const optionManager = new OptionManager (this ._api ); const theme = this ._theme ; const ecModel = this ._model = new GlobalModel (); ecModel.scheduler = this ._scheduler ; ecModel.init (null , null , null , theme, this ._locale , optionManager); } this ._model .setOption (option as ECBasicOption , { replaceMerge }, optionPreprocessorFuncs); const updateParams = { seriesTransition : transitionOpt, optionChanged : true } as UpdateLifecycleParams ; if (lazyUpdate) { this [PENDING_UPDATE ] = { silent : silent, updateParams : updateParams }; this [IN_MAIN_PROCESS_KEY ] = false ; this .getZr ().wakeUp (); } else { prepare (this ); updateMethods.update .call (this , null , updateParams); this ._zr .flush (); this [PENDING_UPDATE ] = null ; this [IN_MAIN_PROCESS_KEY ] = false ; flushPendingActions.call (this , silent); triggerUpdatedEvent.call (this , silent); } } wakeUp ( ) { this .animation .start (); this ._stillFrameAccum = 0 ; } start ( ) { if (this ._running ) { return ; } this ._time = new Date ().getTime (); this ._pausedTime = 0 ; this ._startLoop (); } _startLoop ( ) { const self = this ; this ._running = true ; function step ( ) { if (self._running ) { requestAnimationFrame (step); !self._paused && self.update (); } } requestAnimationFrame (step); }
参考 & 引用 react的setstate原理 (juejin.cn)
requestAnimationFrame 详解
requestIdleCallback和requestAnimationFrame详解
一帧剖析
requestAnimationFrame first tick (stackblitz.com)
requestAnimationFrame是一个宏任务么
Renderer Process
Compositor Thread
Compositor Tile Worker(s)
Main Thread
GPU Thread
GPU Process
Input event
handlers
requestAnim-
ationFrame
Parse
HTML
Recalc
Styles
Layout
Update
Layer Tree
Paint
Frame
Start
Composite
Raster
Scheduled
Rasterize
Frame
End
requestIdleCallback
Layer tiles uploaded to
GPU and composited.
vsync and input data
commit
commit