├── .gitignore ├── index.html ├── package.json ├── src ├── computed.js ├── main.js ├── mix.js ├── reactiveArray.js ├── reactiveMap.js ├── reactiveObject.js ├── ref.js ├── watch.js └── watchEffect.js └── vue.global.js /.gitignore: -------------------------------------------------------------------------------- 1 | # We don't need to share config 2 | .idea 3 | 4 | # Created by https://www.gitignore.io/api/node,intellij 5 | # Edit at https://www.gitignore.io/?templates=node,intellij 6 | 7 | ### Intellij ### 8 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 9 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 10 | 11 | # User-specific stuff 12 | .idea/**/workspace.xml 13 | .idea/**/tasks.xml 14 | .idea/**/usage.statistics.xml 15 | .idea/**/dictionaries 16 | .idea/**/shelf 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | 21 | # Sensitive or high-churn files 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.local.xml 25 | .idea/**/sqlDataSources.xml 26 | .idea/**/dynamic.xml 27 | .idea/**/uiDesigner.xml 28 | .idea/**/dbnavigator.xml 29 | 30 | # Gradle 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Gradle and Maven with auto-import 35 | # When using Gradle or Maven with auto-import, you should exclude module files, 36 | # since they will be recreated, and may cause churn. Uncomment if using 37 | # auto-import. 38 | .idea/modules.xml 39 | .idea/*.iml 40 | .idea/modules 41 | *.iml 42 | *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # Crashlytics plugin (for Android Studio and IntelliJ) 66 | com_crashlytics_export_strings.xml 67 | crashlytics.properties 68 | crashlytics-build.properties 69 | fabric.properties 70 | 71 | # Editor-based Rest Client 72 | .idea/httpRequests 73 | 74 | # Android studio 3.1+ serialized cache file 75 | .idea/caches/build_file_checksums.ser 76 | 77 | ### Intellij Patch ### 78 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 79 | 80 | # *.iml 81 | # modules.xml 82 | # .idea/misc.xml 83 | # *.ipr 84 | 85 | # Sonarlint plugin 86 | .idea/**/sonarlint/ 87 | 88 | # SonarQube Plugin 89 | .idea/**/sonarIssues.xml 90 | 91 | # Markdown Navigator plugin 92 | .idea/**/markdown-navigator.xml 93 | .idea/**/markdown-navigator/ 94 | 95 | ### Node ### 96 | # Logs 97 | logs 98 | *.log 99 | npm-debug.log* 100 | yarn-debug.log* 101 | yarn-error.log* 102 | lerna-debug.log* 103 | 104 | # Diagnostic reports (https://nodejs.org/api/report.html) 105 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 106 | 107 | # Runtime data 108 | pids 109 | *.pid 110 | *.seed 111 | *.pid.lock 112 | 113 | # Directory for instrumented libs generated by jscoverage/JSCover 114 | lib-cov 115 | 116 | # Coverage directory used by tools like istanbul 117 | coverage 118 | *.lcov 119 | 120 | # nyc test coverage 121 | .nyc_output 122 | 123 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 124 | .grunt 125 | 126 | # Bower dependency directory (https://bower.io/) 127 | bower_components 128 | 129 | # node-waf configuration 130 | .lock-wscript 131 | 132 | # Compiled binary addons (https://nodejs.org/api/addons.html) 133 | build/Release 134 | 135 | # Dependency directories 136 | node_modules/ 137 | jspm_packages/ 138 | 139 | # TypeScript v1 declaration files 140 | typings/ 141 | 142 | # TypeScript cache 143 | *.tsbuildinfo 144 | 145 | # Optional npm cache directory 146 | .npm 147 | 148 | # Optional eslint cache 149 | .eslintcache 150 | 151 | # Optional REPL history 152 | .node_repl_history 153 | 154 | # Output of 'npm pack' 155 | *.tgz 156 | 157 | # Yarn Integrity file 158 | .yarn-integrity 159 | 160 | # dotenv environment variables file 161 | .env 162 | .env.test 163 | 164 | # parcel-bundler cache (https://parceljs.org/) 165 | .cache 166 | 167 | # next.js build output 168 | .next 169 | 170 | # nuxt.js build output 171 | .nuxt 172 | 173 | # rollup.js default build output 174 | dist/ 175 | 176 | # Uncomment the public line if your project uses Gatsby 177 | # https://nextjs.org/blog/next-9-1#public-directory-support 178 | # https://create-react-app.dev/docs/using-the-public-folder/#docsNav 179 | # public 180 | 181 | # Storybook build outputs 182 | .out 183 | .storybook-out 184 | 185 | # vuepress build output 186 | .vuepress/dist 187 | 188 | # Serverless directories 189 | .serverless/ 190 | 191 | # FuseBox cache 192 | .fusebox/ 193 | 194 | # DynamoDB Local files 195 | .dynamodb/ 196 | 197 | # Temporary folders 198 | tmp/ 199 | temp/ 200 | 201 | # End of https://www.gitignore.io/api/node,intellij 202 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue tests 8 | 9 | 10 | 11 | 12 | 60 | 61 | 62 |

Vue3 benchmarks

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |

Link usage:

?v=3.0.0-rc.12&b=ref,computed

83 | 84 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-next-benchmarks", 3 | "version": "1.0.0", 4 | "description": "Benchmarks Vue3 core features, to keep track of performance", 5 | "main": "index.js", 6 | "author": "Bas van Meurs, bvanmeurs1985@gmail.com", 7 | "license": "MIT" 8 | } 9 | -------------------------------------------------------------------------------- /src/computed.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, watch, watchEffect } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | bench(() => { 7 | return suite.add("create computed", () => { 8 | const c = computed(() => 100); 9 | }); 10 | }); 11 | 12 | bench(() => { 13 | let i = 0; 14 | const o = ref(100); 15 | return suite.add("write independent ref dep", () => { 16 | o.value = i++; 17 | }); 18 | }); 19 | 20 | bench(() => { 21 | const v = ref(100); 22 | const c = computed(() => { 23 | return v.value * 2 24 | }); 25 | let i = 0; 26 | return suite.add("write ref, don't read computed (never invoked)", () => { 27 | v.value = i++; 28 | }); 29 | }) 30 | 31 | bench(() => { 32 | const v = ref(100); 33 | const c = computed(() => { 34 | return v.value * 2 35 | }); 36 | const cv = c.value; 37 | let i = 0; 38 | return suite.add("write ref, don't read computed (invoked)", () => { 39 | v.value = i++; 40 | }); 41 | }) 42 | 43 | bench(() => { 44 | const v = ref(100); 45 | const c = computed(() => { 46 | return v.value * 2 47 | }); 48 | let i = 0; 49 | return suite.add("write ref, read computed", () => { 50 | v.value = i++; 51 | const cv = c.value; 52 | }); 53 | }); 54 | 55 | 56 | bench(() => { 57 | const v = ref(100); 58 | const computeds = []; 59 | for (let i = 0, n = 1000; i < n; i++) { 60 | const c = computed(() => { 61 | return v.value * 2 62 | }); 63 | computeds.push(c); 64 | } 65 | let i = 0; 66 | return suite.add("write ref, don't read 1000 computeds (never invoked)", () => { 67 | v.value = i++; 68 | }); 69 | }) 70 | 71 | bench(() => { 72 | const v = ref(100); 73 | const computeds = []; 74 | for (let i = 0, n = 1000; i < n; i++) { 75 | const c = computed(() => { 76 | return v.value * 2 77 | }); 78 | const cv = c.value; 79 | computeds.push(c); 80 | } 81 | let i = 0; 82 | return suite.add("write ref, don't read 1000 computeds (invoked)", () => { 83 | v.value = i++; 84 | }); 85 | }); 86 | 87 | bench(() => { 88 | const v = ref(100); 89 | const computeds = []; 90 | for (let i = 0, n = 1000; i < n; i++) { 91 | const c = computed(() => { 92 | return v.value * 2 93 | }); 94 | const cv = c.value; 95 | computeds.push(c); 96 | } 97 | let i = 0; 98 | return suite.add("write ref, read 1000 computeds", () => { 99 | v.value = i++; 100 | computeds.forEach(c => c.value); 101 | }); 102 | }); 103 | 104 | bench(() => { 105 | const refs = []; 106 | for (let i = 0, n = 1000; i < n; i++) { 107 | refs.push(ref(i)); 108 | } 109 | const c = computed(() => { 110 | let total = 0; 111 | refs.forEach(ref => total += ref.value); 112 | return total; 113 | }); 114 | let i = 0; 115 | const n = refs.length; 116 | return suite.add("1000 refs, 1 computed", () => { 117 | refs[i++ % n].value++; 118 | const v = c.value; 119 | }); 120 | }); 121 | 122 | return suite; 123 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | const queryParams = new URLSearchParams(window.location.search); 2 | const version = queryParams.get("v"); 3 | const available = ["ref", "computed", "watch", "watchEffect", "mix", "reactiveObject", "reactiveMap", "reactiveArray"]; 4 | 5 | const urlInput = document.getElementById("url"); 6 | urlInput.value = version || "3.2.2"; 7 | 8 | const benchmarksInput = document.getElementById("benchmarks"); 9 | benchmarksInput.value = queryParams.get("b") || available.join(","); 10 | 11 | const abortButton = document.getElementById("abort"); 12 | const startButton = document.getElementById("start"); 13 | const results = document.getElementById("results"); 14 | 15 | const standaloneInput = document.getElementById("standalone"); 16 | const iterationsInput = document.getElementById("iterations"); 17 | 18 | // This allows aborting and outputting while tests are being run. 19 | Benchmark.options.async = true; 20 | 21 | const maxTime = document.getElementById("maxTime"); 22 | maxTime.onchange = function() { 23 | Benchmark.options.maxTime = parseFloat(maxTime.value) || 0; 24 | } 25 | 26 | 27 | function log(...args) { 28 | console.log(...args); 29 | } 30 | 31 | function addBenchmark(name) { 32 | const tr = document.createElement('tr'); 33 | tr.innerHTML += `` 34 | tr.firstElementChild.innerText = name; 35 | results.appendChild(tr); 36 | } 37 | 38 | function addResult(name, result, fn) { 39 | const tr = document.createElement('tr'); 40 | tr.innerHTML += `` 41 | const span = tr.children.item(0).firstElementChild; 42 | span.innerText = name; 43 | span.setAttribute("title", fn.toString()); 44 | tr.children.item(1).innerText = result; 45 | results.appendChild(tr); 46 | } 47 | 48 | // @see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string 49 | const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; 50 | 51 | function getUrl() { 52 | let url = urlInput.value; 53 | if (url.trim() === "") { 54 | alert("Please enter URL"); 55 | } else if (semverRegex.test(url)) { 56 | url = `https://cdnjs.cloudflare.com/ajax/libs/vue/${url}/vue.global.js`; 57 | } 58 | return url; 59 | } 60 | 61 | window.start = function() { 62 | const benchmarks = benchmarksInput.value.trim() !== "" ? benchmarksInput.value.split(",") : available; 63 | 64 | document.getElementById("started").style.display = "block"; 65 | 66 | let url = getUrl(); 67 | 68 | log("Use Vue3: " + url); 69 | log("Benchmarks: " + benchmarks.join(",")); 70 | log("Available: " + available.join(",")); 71 | 72 | results.innerHTML = `Benchmark (hover to show code)Result`; 73 | 74 | abortButton.removeAttribute("disabled"); 75 | startButton.setAttribute("disabled", "disabled"); 76 | 77 | injectScript(url).then(() => { 78 | runTests(benchmarks).then(() => { 79 | startButton.removeAttribute("disabled"); 80 | abortButton.setAttribute("disabled", "disabled"); 81 | alert("All benchmarks finished"); 82 | }); 83 | }); 84 | } 85 | 86 | let suite; 87 | let aborted = false; 88 | window.abort = function() { 89 | startButton.removeAttribute("disabled"); 90 | abortButton.setAttribute("disabled", "disabled"); 91 | 92 | if (suite) { 93 | suite.abort(); 94 | } 95 | aborted = true; 96 | } 97 | 98 | function bench(cb) { 99 | const suite = cb(); 100 | const benchmark = suite[suite.length - 1]; 101 | benchmark.bench = cb; 102 | } 103 | 104 | async function runTests(benchmarks) { 105 | for (let i = 0, n = benchmarks.length; i < n; i++) { 106 | const name = benchmarks[i]; 107 | 108 | window.go = undefined; 109 | await injectScript(`src/${name}.js`); 110 | 111 | suite = go(); 112 | 113 | log("Benchmark: " + name); 114 | addBenchmark(name); 115 | 116 | suite.on("cycle", function(event) { 117 | addResult(event.target.name, event.target.toString().substr(event.target.name.length + 3), event.target.bench); 118 | log(String(event.target)); 119 | }); 120 | 121 | suite.run(); 122 | 123 | await new Promise((resolve) => { 124 | suite.on("complete", resolve); 125 | }); 126 | 127 | } 128 | } 129 | 130 | window.standalone = function() { 131 | const v = standaloneInput.value; 132 | const index = v.indexOf(":"); 133 | if (index === -1) { 134 | alert("Format: {name}:{benchmark}"); 135 | } else { 136 | const name = v.substr(0, index).trim(); 137 | const bench = v.substr(index + 1).trim(); 138 | startStandalone(name, bench); 139 | } 140 | } 141 | 142 | async function startStandalone(name, benchmarkName) { 143 | let url = getUrl(); 144 | 145 | log("Use Vue3: " + url); 146 | log("Benchmarks: " + name + ":" + benchmarkName); 147 | 148 | await injectScript(url); 149 | 150 | window.go = undefined; 151 | await injectScript(`src/${name}.js`); 152 | 153 | suite = go(); 154 | 155 | let bench; 156 | for (const key in suite) { 157 | if (parseInt(key) >= 0) { 158 | const b = suite[key]; 159 | if (b.name === benchmarkName) { 160 | bench = b; 161 | } 162 | } 163 | } 164 | 165 | if (!bench) { 166 | alert("Benchmark not found."); 167 | return; 168 | } 169 | 170 | const iterations = parseInt(iterationsInput.value.replace(/_/g, "")) || 1e4; 171 | log("Iterations: " + iterations); 172 | 173 | const f = bench.fn; 174 | console.profile(benchmarkName); 175 | for (let i = 0; i < iterations; i++) { 176 | f(); 177 | } 178 | console.profileEnd(benchmarkName); 179 | } 180 | 181 | function injectScript(src) { 182 | return new Promise((resolve, reject) => { 183 | const script = document.createElement('script'); 184 | script.src = src; 185 | script.addEventListener('load', resolve); 186 | script.addEventListener('error', e => reject(e.error)); 187 | document.head.appendChild(script); 188 | }); 189 | } 190 | 191 | 192 | -------------------------------------------------------------------------------- /src/mix.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, watch, watchEffect } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | bench(() => { 7 | const v = ref(100); 8 | const c = computed(() => v.value * 2); 9 | const c2 = computed(() => c.value * 2); 10 | const v2 = ref(10); 11 | 12 | const w = watch(c2, (v) => { 13 | v2.value = v; 14 | }); 15 | 16 | let ctr = 0; 17 | const we = watchEffect(() => { 18 | const v = c.value + v2.value; 19 | }); 20 | 21 | return suite.add("mix of dependent refs, computed, watch and watchEffect", function(deferred) { 22 | v.value += 50; 23 | Vue.nextTick(() => deferred.resolve()); 24 | }, { defer: true }); 25 | }); 26 | 27 | return suite; 28 | } -------------------------------------------------------------------------------- /src/reactiveArray.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, reactive, shallowRef, triggerRef, readonly, toRaw } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | for (let amount = 10; amount < 1e6; amount *= 10) { 7 | bench(() => { 8 | const rawArray = []; 9 | for (let i = 0, n = amount; i < n; i++) { 10 | rawArray.push(i) 11 | } 12 | const r = reactive(rawArray); 13 | const c = computed(() => { 14 | return r.reduce((v, a) => a + v, 0) 15 | }); 16 | 17 | return suite.add(`reduce *reactive* array, ${amount} elements`, () => { 18 | for (let i = 0, n = r.length; i < n; i++) { 19 | r[i]++ 20 | } 21 | const value = c.value 22 | }); 23 | }); 24 | 25 | bench(() => { 26 | const rawArray = []; 27 | for (let i = 0, n = amount; i < n; i++) { 28 | rawArray.push(i) 29 | } 30 | const r = reactive(rawArray); 31 | const c = computed(() => { 32 | return r.reduce((v, a) => a + v, 0) 33 | }); 34 | 35 | return suite.add(`reduce *reactive* array, ${amount} elements, only change first value`, () => { 36 | r[0]++ 37 | const value = c.value 38 | }); 39 | }); 40 | 41 | bench(() => { 42 | const rawArray = []; 43 | for (let i = 0, n = amount; i < n; i++) { 44 | rawArray.push(i) 45 | } 46 | const r = reactive({ arr: readonly(rawArray) }); 47 | const c = computed(() => { 48 | return r.arr.reduce((v, a) => a + v, 0) 49 | }); 50 | 51 | return suite.add(`reduce *readonly* array, ${amount} elements`, () => { 52 | r.arr = r.arr.map(v => v + 1) 53 | const value = c.value 54 | }); 55 | }); 56 | 57 | bench(() => { 58 | const rawArray = []; 59 | for (let i = 0, n = amount; i < n; i++) { 60 | rawArray.push(i) 61 | } 62 | const r = shallowRef(rawArray); 63 | const c = computed(() => { 64 | return r.value.reduce((v, a) => a + v, 0) 65 | }); 66 | 67 | return suite.add(`reduce *raw* array, copied, ${amount} elements`, () => { 68 | r.value = r.value.map(v => v + 1) 69 | const value = c.value 70 | }); 71 | }); 72 | 73 | bench(() => { 74 | const rawArray = []; 75 | for (let i = 0, n = amount; i < n; i++) { 76 | rawArray.push(i) 77 | } 78 | const r = shallowRef(rawArray); 79 | const c = computed(() => { 80 | return r.value.reduce((v, a) => a + v, 0) 81 | }); 82 | 83 | return suite.add(`reduce *raw* array, manually triggered, ${amount} elements`, () => { 84 | for (let i = 0, n = rawArray.length; i < n; i++) { 85 | rawArray[i]++ 86 | } 87 | triggerRef(r); 88 | const value = c.value 89 | }); 90 | }); 91 | 92 | } 93 | 94 | return suite; 95 | } -------------------------------------------------------------------------------- /src/reactiveMap.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, reactive } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | function createMap(obj) { 7 | const map = new Map(); 8 | for (const key in obj) { 9 | if (obj.hasOwnProperty(key)) { 10 | map.set(key, obj[key]); 11 | } 12 | } 13 | return map; 14 | } 15 | 16 | bench(() => { 17 | return suite.add("create reactive map", () => { 18 | const r = reactive(createMap({a: 1})); 19 | }); 20 | }); 21 | 22 | bench(() => { 23 | let i = 0; 24 | const r = reactive(createMap({a: 1})); 25 | return suite.add("write reactive map property", () => { 26 | r.set("a", i++); 27 | }); 28 | }); 29 | 30 | bench(() => { 31 | const r = reactive(createMap({a: 1})); 32 | const c = computed(() => { 33 | return r.get("a") * 2 34 | }); 35 | let i = 0; 36 | return suite.add("write reactive map, don't read computed (never invoked)", () => { 37 | r.set("a", i++); 38 | }); 39 | }) 40 | 41 | bench(() => { 42 | const r = reactive(createMap({a: 1})); 43 | const c = computed(() => { 44 | return r.get("a") * 2 45 | }); 46 | const cv = c.value; 47 | let i = 0; 48 | return suite.add("write reactive map, don't read computed (invoked)", () => { 49 | r.set("a", i++); 50 | }); 51 | }) 52 | 53 | bench(() => { 54 | const r = reactive(createMap({a: 1})); 55 | const c = computed(() => { 56 | return r.get("a") * 2 57 | }); 58 | let i = 0; 59 | return suite.add("write reactive map, read computed", () => { 60 | r.set("a", i++); 61 | const cv = c.value; 62 | }); 63 | }); 64 | 65 | bench(() => { 66 | const _m = new Map(); 67 | for (let i = 0; i < 10000; i++) { 68 | _m.set(i, i); 69 | } 70 | const r = reactive(_m); 71 | const c = computed(() => { 72 | let total = 0; 73 | r.forEach((value, key) => { 74 | total += value; 75 | }); 76 | return total; 77 | }); 78 | let i = 0; 79 | return suite.add("write reactive map (10'000 items), read computed", () => { 80 | r.set(5000, r.get(5000) + 1); 81 | const cv = c.value; 82 | }); 83 | }); 84 | 85 | bench(() => { 86 | const r = reactive(createMap({a: 1})); 87 | const computeds = []; 88 | for (let i = 0, n = 1000; i < n; i++) { 89 | const c = computed(() => { 90 | return r.get("a") * 2 91 | }); 92 | computeds.push(c); 93 | } 94 | let i = 0; 95 | return suite.add("write reactive map, don't read 1000 computeds (never invoked)", () => { 96 | r.set("a", i++); 97 | }); 98 | }) 99 | 100 | bench(() => { 101 | const r = reactive(createMap({a: 1})); 102 | const computeds = []; 103 | for (let i = 0, n = 1000; i < n; i++) { 104 | const c = computed(() => { 105 | return r.get("a") * 2 106 | }); 107 | const cv = c.value; 108 | computeds.push(c); 109 | } 110 | let i = 0; 111 | return suite.add("write reactive map, don't read 1000 computeds (invoked)", () => { 112 | r.set("a", i++); 113 | }); 114 | }); 115 | 116 | bench(() => { 117 | const r = reactive(createMap({a: 1})); 118 | const computeds = []; 119 | for (let i = 0, n = 1000; i < n; i++) { 120 | const c = computed(() => { 121 | return r.get("a") * 2 122 | }); 123 | computeds.push(c); 124 | } 125 | let i = 0; 126 | return suite.add("write reactive map, read 1000 computeds", () => { 127 | r.set("a", i++); 128 | computeds.forEach(c => c.value); 129 | }); 130 | }); 131 | 132 | bench(() => { 133 | const reactives = []; 134 | for (let i = 0, n = 1000; i < n; i++) { 135 | reactives.push(reactive(createMap({a: i}))); 136 | } 137 | const c = computed(() => { 138 | let total = 0; 139 | reactives.forEach(r => total += r.get("a")); 140 | return total; 141 | }); 142 | let i = 0; 143 | const n = reactives.length; 144 | return suite.add("1000 reactive maps, 1 computed", () => { 145 | reactives[i++ % n].set("a", reactives[i++ % n].get("a") + 1); 146 | const v = c.value; 147 | }); 148 | }); 149 | 150 | return suite; 151 | } -------------------------------------------------------------------------------- /src/reactiveObject.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, reactive } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | bench(() => { 7 | return suite.add("create reactive obj", () => { 8 | const r = reactive({a: 1}); 9 | }); 10 | }); 11 | 12 | bench(() => { 13 | let i = 0; 14 | const r = reactive({a: 1}); 15 | return suite.add("write reactive obj property", () => { 16 | r.a = i++; 17 | }); 18 | }); 19 | 20 | bench(() => { 21 | const r = reactive({a: 1}); 22 | const c = computed(() => { 23 | return r.a * 2 24 | }); 25 | let i = 0; 26 | return suite.add("write reactive obj, don't read computed (never invoked)", () => { 27 | r.a = i++; 28 | }); 29 | }) 30 | 31 | bench(() => { 32 | const r = reactive({a: 1}); 33 | const c = computed(() => { 34 | return r.a * 2 35 | }); 36 | const cv = c.value; 37 | let i = 0; 38 | return suite.add("write reactive obj, don't read computed (invoked)", () => { 39 | r.a = i++; 40 | }); 41 | }) 42 | 43 | bench(() => { 44 | const r = reactive({a: 1}); 45 | const c = computed(() => { 46 | return r.a * 2 47 | }); 48 | let i = 0; 49 | return suite.add("write reactive obj, read computed", () => { 50 | r.a = i++; 51 | const cv = c.value; 52 | }); 53 | }); 54 | 55 | bench(() => { 56 | const r = reactive({a: 1}); 57 | const computeds = []; 58 | for (let i = 0, n = 1000; i < n; i++) { 59 | const c = computed(() => { 60 | return r.a * 2 61 | }); 62 | computeds.push(c); 63 | } 64 | let i = 0; 65 | return suite.add("write reactive obj, don't read 1000 computeds (never invoked)", () => { 66 | r.a = i++; 67 | }); 68 | }) 69 | 70 | bench(() => { 71 | const r = reactive({a: 1}); 72 | const computeds = []; 73 | for (let i = 0, n = 1000; i < n; i++) { 74 | const c = computed(() => { 75 | return r.a * 2 76 | }); 77 | const cv = c.value; 78 | computeds.push(c); 79 | } 80 | let i = 0; 81 | return suite.add("write reactive obj, don't read 1000 computeds (invoked)", () => { 82 | r.a = i++; 83 | }); 84 | }); 85 | 86 | bench(() => { 87 | const r = reactive({a: 1}); 88 | const computeds = []; 89 | for (let i = 0, n = 1000; i < n; i++) { 90 | const c = computed(() => { 91 | return r.a * 2 92 | }); 93 | computeds.push(c); 94 | } 95 | let i = 0; 96 | return suite.add("write reactive obj, read 1000 computeds", () => { 97 | r.a = i++; 98 | computeds.forEach(c => c.value); 99 | }); 100 | }); 101 | 102 | bench(() => { 103 | const reactives = []; 104 | for (let i = 0, n = 1000; i < n; i++) { 105 | reactives.push(reactive({a: i})); 106 | } 107 | const c = computed(() => { 108 | let total = 0; 109 | reactives.forEach(r => total += r.a); 110 | return total; 111 | }); 112 | let i = 0; 113 | const n = reactives.length; 114 | return suite.add("1000 reactive objs, 1 computed", () => { 115 | reactives[i++ % n].a++; 116 | const v = c.value; 117 | }); 118 | }); 119 | 120 | return suite; 121 | } -------------------------------------------------------------------------------- /src/ref.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, watch, watchEffect } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | bench(() => { 7 | const v = ref(100); 8 | return suite.add("create ref", () => { 9 | const v = ref(100); 10 | }); 11 | }); 12 | 13 | bench(() => { 14 | let i = 0; 15 | const v = ref(100); 16 | return suite.add("write ref", () => { 17 | v.value = i++; 18 | }); 19 | }) 20 | 21 | bench(() => { 22 | const v = ref(100); 23 | return suite.add("read ref", () => { 24 | const i = v.value; 25 | }); 26 | }); 27 | 28 | bench(() => { 29 | let i = 0; 30 | const v = ref(100); 31 | return suite.add("write/read ref", () => { 32 | v.value = i++; 33 | const q = v.value; 34 | }); 35 | }); 36 | 37 | return suite; 38 | } -------------------------------------------------------------------------------- /src/watch.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, watch, watchEffect, } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | bench(() => { 7 | return suite.add("create watcher", () => { 8 | const v = ref(100); 9 | const w = watch(v, (v) => { 10 | }); 11 | }); 12 | }); 13 | 14 | bench(() => { 15 | let c = 0; 16 | const v = ref(100); 17 | const w = watch(v, (v) => { 18 | }); 19 | let i = 0; 20 | return suite.add("update ref to trigger watcher (scheduled but not executed)", () => { 21 | v.value = i++; 22 | }); 23 | }) 24 | 25 | bench(() => { 26 | let c = 0; 27 | const v = ref(100); 28 | const w = watch(v, (v) => { 29 | }); 30 | let i = 0; 31 | return suite.add("update ref to trigger watcher (executed)", function(deferred) { 32 | v.value = i++; 33 | Vue.nextTick(() => deferred.resolve()); 34 | }, { defer: true }); 35 | }) 36 | 37 | return suite; 38 | } -------------------------------------------------------------------------------- /src/watchEffect.js: -------------------------------------------------------------------------------- 1 | function go() { 2 | const { ref, computed, watch, watchEffect, } = Vue; 3 | 4 | const suite = new Benchmark.Suite(); 5 | 6 | bench(() => { 7 | return suite.add("create watchEffect", () => { 8 | const we = watchEffect(() => { 9 | }); 10 | }); 11 | }); 12 | 13 | bench(() => { 14 | let c = 0; 15 | const v = ref(100); 16 | const w = watchEffect(v, (v) => { 17 | const v2 = v.value; 18 | }); 19 | let i = 0; 20 | return suite.add("update ref to trigger watchEffect (scheduled but not executed)", () => { 21 | v.value = i++; 22 | }); 23 | }) 24 | 25 | bench(() => { 26 | let c = 0; 27 | const v = ref(100); 28 | const w = watchEffect(v, (v) => { 29 | const v2 = v.value; 30 | }); 31 | let i = 0; 32 | return suite.add("update ref to trigger watchEffect (executed)", function(deferred) { 33 | v.value = i++; 34 | Vue.nextTick(() => deferred.resolve()); 35 | }, { defer: true }); 36 | }) 37 | 38 | return suite; 39 | } --------------------------------------------------------------------------------