版本: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