├── env ├── prod │ └── cljs │ │ └── football │ │ └── prod.cljs └── dev │ └── cljs │ └── football │ └── dev.cljs ├── public ├── index.html └── css │ └── index.css ├── README.md ├── src └── football │ ├── data.cljs │ └── core.cljs └── project.clj /env/prod/cljs/football/prod.cljs: -------------------------------------------------------------------------------- 1 | (ns football.prod 2 | (:require [football.core :as core])) 3 | 4 | ;;ignore println statements in prod 5 | (set! *print-fn* (fn [& _])) 6 | 7 | (core/init!) 8 | -------------------------------------------------------------------------------- /env/dev/cljs/football/dev.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-no-load football.dev 2 | (:require [football.core :as core] 3 | [figwheel.client :as figwheel :include-macros true])) 4 | 5 | (enable-console-print!) 6 | 7 | (figwheel/watch-and-reload 8 | :websocket-url "ws://localhost:3449/figwheel-ws" 9 | :jsload-callback core/mount-root) 10 | 11 | (core/init!) 12 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a version of the [VueReactPerf](https://github.com/footballradar/VueReactPerf) benchmark implemented using [Reagent](http://reagent-project.github.io/). The original benchmark is discussed [here](https://engineering.footballradar.com/a-fairer-vue-of-react-comparing-react-to-vue-for-dynamic-tabular-data-part-2/?utm_content=buffer0e901). 2 | 3 | Prerequisites: 4 | 5 | * [JDK](http://www.azul.com/downloads/zulu/) 6 | * [Leiningen](https://leiningen.org/) 7 | 8 | To build the benchmark in development mode run: 9 | 10 | lein figwheel 11 | 12 | To build the optimized version for release run: 13 | 14 | lein release 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Devanagari Sangam MN" 3 | } 4 | 5 | table { 6 | border-spacing: 0; 7 | border-collapse: collapse; 8 | } 9 | 10 | tr { 11 | border-bottom: 1px solid #e0e0e0; 12 | padding: 5px; 13 | } 14 | 15 | td, th { 16 | padding: 0; 17 | height: 100%; 18 | white-space: nowrap; 19 | } 20 | 21 | .cell--teams { 22 | padding-left:25px 23 | } 24 | 25 | 26 | .cards { 27 | display: flex; 28 | justify-content: center; 29 | } 30 | 31 | .cards__card { 32 | padding: 5px; 33 | } 34 | 35 | .cards__card--yellow { 36 | background-color: #fff68f; 37 | } 38 | 39 | .cards__card--red { 40 | background-color: #f44343 41 | } 42 | 43 | .player { 44 | display: flex; 45 | justify-content: space-between; 46 | } 47 | 48 | .player__name { 49 | padding: 0 0 0 5px; 50 | margin: 0; 51 | display: flex; 52 | flex-direction: column; 53 | } 54 | 55 | .player__effort { 56 | width: 20px; 57 | } 58 | 59 | .player__effort--low { 60 | background-color: #f77b7b; 61 | } 62 | 63 | .player__effort--high { 64 | background-color: #49B687; 65 | } 66 | 67 | .u-center { 68 | text-align: center; 69 | } 70 | 71 | .u-small { 72 | font-size: 10px; 73 | } 74 | -------------------------------------------------------------------------------- /src/football/data.cljs: -------------------------------------------------------------------------------- 1 | (ns football.data 2 | (:require [reagent.core :as reagent])) 3 | 4 | (defonce games (reagent/atom nil)) 5 | 6 | (defn generate-fake-player [] 7 | {:name (-> js/faker .-name (.findName)) 8 | :effort-level (rand-int 10) 9 | :invited-next-week? (> (rand) 0.5)}) 10 | 11 | (defn generate-fake-game [] 12 | {:id (-> js/faker .-random (.uuid)) 13 | :clock 0 14 | :score {:home 0 :away 0} 15 | :teams {:home (-> js/faker .-address (.city)) 16 | :away (-> js/faker .-address (.city))} 17 | :outrageous-tackles 0 18 | :cards {:yellow 0 :red 0} 19 | :players (mapv generate-fake-player (range 4))}) 20 | 21 | (defn generate-games [game-count] 22 | (reset! games (mapv generate-fake-game (range game-count)))) 23 | 24 | (defn maybe-update [game prob path f] 25 | (if (< (rand-int 100) prob) 26 | (update-in game path f) 27 | game)) 28 | 29 | (defn update-rand-player [game idx] 30 | (-> game 31 | (assoc-in [:players idx :effort-level] (rand-int 10)) 32 | (assoc-in [:players idx :invited-next-week?] (> (rand) 0.5)))) 33 | 34 | (defn update-game [game] 35 | (-> game 36 | (update :clock inc) 37 | (maybe-update 5 [:score :home] inc) 38 | (maybe-update 5 [:score :away] inc) 39 | (maybe-update 8 [:cards :yellow] inc) 40 | (maybe-update 2 [:cards :red] inc) 41 | (maybe-update 10 [:outrageous-tackles] inc) 42 | (update-rand-player (rand-int 4)))) 43 | 44 | (defn update-game-at-interval [interval idx] 45 | (swap! games update idx update-game) 46 | (js/setTimeout update-game-at-interval interval interval idx)) 47 | 48 | (def event-interval 1000) 49 | 50 | (defn update-games [game-count] 51 | (dotimes [i game-count] 52 | (swap! games update i update-game) 53 | (js/setTimeout #(update-game-at-interval event-interval i) 54 | (* i event-interval)))) 55 | -------------------------------------------------------------------------------- /src/football/core.cljs: -------------------------------------------------------------------------------- 1 | (ns football.core 2 | (:require 3 | [football.data :as data] 4 | [reagent.core :as reagent])) 5 | 6 | (defn player-component [{:keys [name invited-next-week? effort-level]}] 7 | [:td 8 | [:div.player 9 | [:p.player__name 10 | [:span name] 11 | [:span.u-small (if invited-next-week? "Doing well" "Not coming again")]] 12 | [:div {:class-name (str "player__effort " 13 | (if (< effort-level 5) 14 | "player__effort--low" 15 | "player__effort--high"))}]]]) 16 | 17 | (defn game-component [game] 18 | [:tr 19 | [:td.u-center (:clock game)] 20 | [:td.u-center (-> game :score :home) "-" (-> game :score :away)] 21 | [:td.cell--teams (-> game :teams :home) "-" (-> game :teams :away)] 22 | [:td.u-center (:outrageous-tackles game)] 23 | [:td 24 | [:div.cards 25 | [:div.cards__card.cards__card--yellow (-> game :cards :yellow)] 26 | [:div.cards__card.cards__card--red (-> game :cards :red)]]] 27 | (for [player (:players game)] 28 | ^{:key player} 29 | [player-component player])]) 30 | 31 | (defn games-component [] 32 | [:tbody 33 | (for [game @data/games] 34 | ^{:key game} 35 | [game-component game])]) 36 | 37 | (defn games-table-component [] 38 | [:table 39 | [:thead 40 | [:tr 41 | [:th {:width "50px"} "Clock"] 42 | [:th {:width "50px"} "Score"] 43 | [:th {:width "200px"} "Teams"] 44 | [:th "Outrageous Tackles"] 45 | [:th {:width "100px"} "Cards"] 46 | [:th {:width "100px"} "Players"] 47 | [:th {:width "100px"} ""] 48 | [:th {:width "100px"} ""] 49 | [:th {:width "100px"} ""] 50 | [:th {:width "100px"} ""]]] 51 | [games-component]]) 52 | 53 | (defn home-page [] 54 | [games-table-component]) 55 | 56 | (defn mount-root [] 57 | (reagent/render [home-page] (.getElementById js/document "app"))) 58 | 59 | (def game-count 50) 60 | 61 | (defn init! [] 62 | (data/generate-games game-count) 63 | (data/update-games game-count) 64 | (mount-root)) 65 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject football "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :dependencies [[org.clojure/clojure "1.8.0" :scope "provided"] 8 | [org.clojure/clojurescript "1.9.495" :scope "provided"] 9 | [reagent "0.6.1"]] 10 | 11 | :plugins [[lein-cljsbuild "1.1.5"] 12 | [lein-figwheel "0.5.9"]] 13 | 14 | :min-lein-version "2.5.0" 15 | 16 | :clean-targets ^{:protect false} 17 | [:target-path 18 | [:cljsbuild :builds :app :compiler :output-dir] 19 | [:cljsbuild :builds :app :compiler :output-to]] 20 | 21 | :resource-paths ["public"] 22 | 23 | :figwheel {:http-server-root "." 24 | :nrepl-port 7002 25 | :nrepl-middleware ["cemerick.piggieback/wrap-cljs-repl"] 26 | :css-dirs ["public/css"]} 27 | 28 | :cljsbuild {:builds {:app 29 | {:source-paths ["src" "env/dev/cljs"] 30 | :compiler 31 | {:main "football.dev" 32 | :output-to "public/js/app.js" 33 | :output-dir "public/js/out" 34 | :asset-path "js/out" 35 | :source-map true 36 | :optimizations :none 37 | :pretty-print true} 38 | :figwheel 39 | {:open-urls ["http://localhost:3449/index.html"]}} 40 | :release 41 | {:source-paths ["src" "env/prod/cljs"] 42 | :compiler 43 | {:output-to "public/js/app.js" 44 | :output-dir "public/js/release" 45 | :externs ["public/vendor/js/faker.min.js"] 46 | :closure-warnings 47 | {:externs-validation :off :non-standard-jsdoc :off} 48 | :asset-path "js/out" 49 | :optimizations :advanced 50 | :pretty-print false}}}} 51 | 52 | :aliases {"release" ["do" "clean" ["cljsbuild" "once" "release"]]} 53 | 54 | :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.9"] 55 | [org.clojure/tools.nrepl "0.2.12"] 56 | [com.cemerick/piggieback "0.2.2-SNAPSHOT"]]}}) 57 | --------------------------------------------------------------------------------