August 3, 2019
By: Kevin
Reagent文档: Batching and Timing
https://github.com/reagent-project/reagent/blob/master/doc/BatchingAndTiming.md
state变化 -> UI变化
react会尽可能批量处理render, 批量render是react高性能的重要原因. 多次对程序状态的修改, 可能会体现为一次页面的更新. 页面(Dom)重绘的时机是浏览器自己控制的, 在'适当时机'更新. 这种行为有诸多好处
- 程序状态,比如一个atom的值的修改可以发生的非常频繁,页面更新是跟不上的. Reagent对状态的修改不会直接触发更新.
- 如果多个对atom的更新同时发生,它们有可能只会引发一次页面更新.
- 状态的修改和页面的重绘是异步的,修改的代码可以在event handler, 也可以在core.asyn的某个block, 非常灵活.
- 重绘机制可以和Reagent的requestAnimationFrame一起使用,动画更加顺滑.
关于异步重绘和以前同步绘制的比较在David Nolen(cljs的主要维护者)的一篇blog里有充分的比较.
react
backbone

接下来是一个例子, 滑动rgb的滑杆, 下面会生成一定数量(数量可控)的颜色div. 我的电脑上2017 macbook: 2.3 GHz Intel Core i5上.
- 在有1000个div的时候, 变一下颜色需要40ms左右,
- 改变数量的时候只需要4ms!!!
(require '[reagent.core :as r])
(defn timing-wrapper [f]
(let [start-time (r/atom nil)
render-time (r/atom nil)
now #(.now js/Date)
start #(reset! start-time (now))
stop #(reset! render-time (- (now) @start-time))
timed-f (with-meta f
{:component-will-mount start
:component-will-update start
:component-did-mount stop
:component-did-update stop})]
(fn []
[:div
[:p [:em "render time: " @render-time "ms"]]
[timed-f]])))
(def base-color (r/atom {:red 130 :green 160 :blue 120}))
(def ncolors (r/atom 20))
(def random-colors (r/atom nil))
(defn to-rgb [{:keys [red green blue]}]
(let [hex #(str (if (< % 16) "0")
(-> % js/Math.round (.toString 16)))]
(str "#" (hex red) (hex green) (hex blue))))
(defn tweak-color [{:keys [red green blue]}]
(let [rnd #(-> (js/Math.random) (* 256))
tweak #(-> % (+ (rnd)) (/ 2) js/Math.floor)]
{:red (tweak red) :green (tweak green) :blue (tweak blue)}))
(defn reset-random-colors [color]
(reset! random-colors
(repeatedly #(-> color tweak-color to-rgb))))
(defn color-choose [color-part]
[:div.color-slider
(name color-part) " " (color-part @base-color)
[:input {:type "range" :min 0 :max 255
:value (color-part @base-color)
:on-change (fn [e]
(swap! base-color assoc
color-part (-> e .-target .-value int))
(reset-random-colors @base-color))}]])
(defn ncolors-choose []
[:div.color-slider
"number of color divs " @ncolors
[:input {:type "range" :min 0 :max 2000
:value @ncolors
:on-change #(reset! ncolors (-> % .-target .-value int))}]])
(defn color-plate [color]
[:div
{:style {:background-color color}}
color])
(defn palette []
(let [color @base-color
n @ncolors]
[:div
[:p "base color: "]
[color-plate (to-rgb color)]
[:div.color-samples
[:p n " random matching colors:"]
(map-indexed (fn [k v]
^{:key k} [color-plate v])
(take n @random-colors))]]))
(defn color-demo []
(reset-random-colors @base-color)
(fn []
[:div
[:h2 "Matching colors"]
[color-choose :red]
[color-choose :green]
[color-choose :blue]
[ncolors-choose]
[timing-wrapper palette]]))