App.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <script setup lang="ts" vapor>
  2. import {
  3. ref,
  4. shallowRef,
  5. triggerRef,
  6. watch,
  7. type ShallowRef,
  8. type WatchSource,
  9. } from '@vue/vapor'
  10. import { buildData } from './data'
  11. import { defer, wrap } from './profiling'
  12. const isVapor = !!import.meta.env.IS_VAPOR
  13. const selected = ref<number>()
  14. const rows = shallowRef<
  15. {
  16. id: number
  17. label: ShallowRef<string>
  18. }[]
  19. >([])
  20. // Bench Add: https://jsbench.me/45lzxprzmu/1
  21. const add = wrap('add', () => {
  22. rows.value.push(...buildData(1000))
  23. triggerRef(rows)
  24. })
  25. const remove = wrap('remove', (id: number) => {
  26. rows.value.splice(
  27. rows.value.findIndex(d => d.id === id),
  28. 1,
  29. )
  30. triggerRef(rows)
  31. })
  32. const select = wrap('select', (id: number) => {
  33. selected.value = id
  34. })
  35. const run = wrap('run', () => {
  36. rows.value = buildData()
  37. selected.value = undefined
  38. })
  39. const update = wrap('update', () => {
  40. const _rows = rows.value
  41. for (let i = 0, len = _rows.length; i < len; i += 10) {
  42. _rows[i].label.value += ' !!!'
  43. }
  44. })
  45. const runLots = wrap('runLots', () => {
  46. rows.value = buildData(10000)
  47. selected.value = undefined
  48. })
  49. const clear = wrap('clear', () => {
  50. rows.value = []
  51. selected.value = undefined
  52. })
  53. const swapRows = wrap('swap', () => {
  54. const _rows = rows.value
  55. if (_rows.length > 998) {
  56. const d1 = _rows[1]
  57. const d998 = _rows[998]
  58. _rows[1] = d998
  59. _rows[998] = d1
  60. triggerRef(rows)
  61. }
  62. })
  63. async function bench() {
  64. for (let i = 0; i < 30; i++) {
  65. rows.value = []
  66. await defer()
  67. await runLots()
  68. }
  69. }
  70. // Reduce the complexity of `selected` from O(n) to O(1).
  71. function createSelector(source: WatchSource) {
  72. const cache: Record<keyof any, ShallowRef<boolean>> = {}
  73. watch(source, (val, old) => {
  74. if (old != undefined) cache[old]!.value = false
  75. if (val != undefined) cache[val]!.value = true
  76. })
  77. return (id: keyof any) => (cache[id] ??= shallowRef(false)).value
  78. }
  79. const isSelected = createSelector(selected)
  80. </script>
  81. <template>
  82. <h1>Vue.js ({{ isVapor ? 'Vapor' : 'Virtual DOM' }}) Benchmark</h1>
  83. <div
  84. id="control"
  85. style="display: flex; flex-direction: column; width: fit-content; gap: 6px"
  86. >
  87. <button @click="bench">Benchmark mounting</button>
  88. <button id="run" @click="run">Create 1,000 rows</button>
  89. <button id="runLots" @click="runLots">Create 10,000 rows</button>
  90. <button id="add" @click="add">Append 1,000 rows</button>
  91. <button id="update" @click="update">Update every 10th row</button>
  92. <button id="clear" @click="clear">Clear</button>
  93. <button id="swaprows" @click="swapRows">Swap Rows</button>
  94. </div>
  95. <div id="time"></div>
  96. <table>
  97. <tbody>
  98. <tr
  99. v-for="row of rows"
  100. :key="row.id"
  101. :class="{ danger: isSelected(row.id) }"
  102. v-memo="[row.label, row.id === selected]"
  103. >
  104. <td>{{ row.id }}</td>
  105. <td>
  106. <a @click="select(row.id)">{{ row.label.value }}</a>
  107. </td>
  108. <td>
  109. <button @click="remove(row.id)">x</button>
  110. </td>
  111. <td class="col-md-6"></td>
  112. </tr>
  113. </tbody>
  114. </table>
  115. </template>
  116. <style>
  117. .danger {
  118. background-color: red;
  119. }
  120. </style>