Skip to content

Commit b0667e3

Browse files
authored
feat: improve performance of vuex and events tabs (#721)
* MVP * feat: devtools-side lru cache * fix: remove loading if snapshot was cached on backend-side * feat: state recording/loading UI * feat: virtual scrollers * feat(bridge): batch messages * fix: debounce scroll to selected item * feat(vuex): snapshot adjustements * chore(dev): exmaples changes * fix: history scrolling performance issues * fix: time travel * fix: commit/revert * feat(bridge): immediatly send the first message and batch the next ones * test: fix e2e
1 parent fd7fbc2 commit b0667e3

23 files changed

+657
-278
lines changed

cypress/integration/events.tab.js renamed to cypress/integration/events-tab.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ suite('events tab', () => {
2525

2626
it('should search events', () => {
2727
cy.get('.left .search input').clear().type('event')
28-
cy.get('.history .entry').should('have.length', 3)
28+
cy.get('.history .entry[data-active="true"]').should('have.length', 3)
2929
cy.get('.left .search input').clear().type('<eventchild1>')
30-
cy.get('.history .entry').should('have.length', 1)
30+
cy.get('.history .entry[data-active="true"]').should('have.length', 1)
3131
cy.get('.left .search input').clear()
3232
cy.get('.button.reset').click()
33-
cy.get('.history .entry').should('have.length', 0)
33+
cy.get('.history .entry[data-active="true"]').should('have.length', 0)
3434
})
3535
})

cypress/integration/vuex.tab.js renamed to cypress/integration/vuex-tab.js

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ suite('vuex tab', () => {
1111
})
1212
cy.get('.vuex-tab').click()
1313
cy.get('.history .entry').should('have.length', 4)
14+
cy.get('[data-id="load-vuex-state"]').click()
15+
cy.get('.recording-vuex-state').should('not.be.visible')
16+
cy.get('.loading-vuex-state').should('not.be.visible')
1417
cy.get('.vuex-state-inspector').then(el => {
1518
expect(el.text()).to.include('type:"DECREMENT"')
1619
expect(el.text()).to.include('count:1')
1720
})
18-
cy.get('.history .entry:nth-child(4)').should('have.class', 'inspected').should('have.class', 'active')
21+
cy.get('.history .entry').eq(3).should('have.class', 'inspected').should('have.class', 'active')
1922
})
2023

2124
it('should filter state & getters', () => {
@@ -28,28 +31,30 @@ suite('vuex tab', () => {
2831

2932
it('should filter history', () => {
3033
cy.get('.left .search input').clear().type('inc')
31-
cy.get('.history .entry').should('have.length', 3)
32-
cy.get('.history .entry.inspected').should('have.length', 1)
33-
cy.get('.history .entry.active').should('have.length', 0)
34+
cy.get('.history .entry[data-active="true"]').should('have.length', 2)
35+
cy.get('.history .entry[data-active="true"].inspected').should('have.length', 0)
36+
cy.get('.history .entry[data-active="true"].active').should('have.length', 0)
3437

3538
cy.get('.left .search input').clear().type('/dec/i')
36-
cy.get('.history .entry').should('have.length', 2)
37-
cy.get('.history .entry.inspected').should('have.length', 1)
38-
cy.get('.history .entry.active').should('have.length', 0)
39+
cy.get('.history .entry[data-active="true"]').should('have.length', 1)
40+
cy.get('.history .entry[data-active="true"].inspected').should('have.length', 0)
41+
cy.get('.history .entry[data-active="true"].active').should('have.length', 0)
3942

4043
cy.get('.left .search input').clear().type('/dec)/i')
41-
cy.get('.history .entry').should('have.length', 4)
42-
cy.get('.history .entry.inspected').should('have.length', 1)
43-
cy.get('.history .entry.active').should('have.length', 1)
44+
cy.get('.history .entry[data-active="true"]').should('have.length', 3)
45+
cy.get('.history .entry[data-active="true"].inspected').should('have.length', 0)
46+
cy.get('.history .entry[data-active="true"].active').should('have.length', 1)
4447

4548
cy.get('.left .search input').clear()
4649
})
4750

4851
it('should inspect state', () => {
49-
cy.get('.history .entry:nth-child(3) .mutation-type').click()
50-
cy.get('.history .entry:nth-child(3)')
52+
cy.get('.history .entry .mutation-type').eq(2).click()
53+
cy.get('.history .entry').eq(2)
5154
.should('have.class', 'inspected')
5255
.should('not.have.class', 'active')
56+
cy.get('.recording-vuex-state').should('not.be.visible')
57+
cy.get('.loading-vuex-state').should('not.be.visible')
5358
cy.get('.vuex-state-inspector').then(el => {
5459
expect(el.text()).to.include('type:"INCREMENT"')
5560
expect(el.text()).to.include('count:2')
@@ -60,42 +65,45 @@ suite('vuex tab', () => {
6065
})
6166

6267
it('should time-travel', () => {
63-
cy.get('.history .entry:nth-child(3) .entry-actions .action:nth-child(3)').click({ force: true })
64-
cy.get('.history .entry:nth-child(3)')
68+
cy.get('.history .entry[data-index="2"] .entry-actions .action:nth-child(3)').click({ force: true })
69+
cy.get('.history .entry[data-index="2"]')
6570
.should('have.class', 'inspected')
6671
.should('have.class', 'active')
6772
cy.get('#target').iframe().then(({ get }) => {
6873
get('#counter p').contains('2')
6974
})
7075

71-
cy.get('.history .entry:nth-child(2) .mutation-type').click({ force: true })
72-
cy.get('.history .entry:nth-child(2)')
76+
cy.get('.history .entry[data-index="1"] .mutation-type').click({ force: true })
77+
cy.get('.history .entry[data-index="1"]')
7378
.should('have.class', 'inspected')
7479
.should('not.have.class', 'active')
75-
cy.get('.history .entry:nth-child(3)')
80+
cy.get('.history .entry[data-index="2"]')
7681
.should('not.have.class', 'inspected')
7782
.should('have.class', 'active')
83+
cy.get('.recording-vuex-state').should('not.be.visible')
84+
cy.get('.loading-vuex-state').should('not.be.visible')
85+
cy.get('.recording-vuex-state').should('not.be.visible')
7886
cy.get('.vuex-state-inspector').then(el => {
7987
expect(el.text()).to.include('type:"INCREMENT"')
8088
expect(el.text()).to.include('count:1')
8189
})
8290
cy.get('#target').iframe().then(({ get }) => {
8391
get('#counter p').contains('2')
8492
})
85-
cy.get('.history .entry:nth-child(2) .entry-actions .action:nth-child(3)').click({ force: true })
86-
cy.get('.history .entry:nth-child(2)')
93+
cy.get('.history .entry[data-index="1"] .entry-actions .action:nth-child(3)').click({ force: true })
94+
cy.get('.history .entry[data-index="1"]')
8795
.should('have.class', 'inspected')
8896
.should('have.class', 'active')
89-
cy.get('.history .entry:nth-child(3)')
97+
cy.get('.history .entry[data-index="2"]')
9098
.should('not.have.class', 'inspected')
9199
.should('not.have.class', 'active')
92100
cy.get('#target').iframe().then(({ get }) => {
93101
get('#counter p').contains('1')
94102
})
95103

96104
// Base state
97-
cy.get('.history .entry:nth-child(1) .mutation-type').click({ force: true })
98-
cy.get('.history .entry:nth-child(1)')
105+
cy.get('.history .entry[data-index="0"] .mutation-type').click({ force: true })
106+
cy.get('.history .entry[data-index="0"]')
99107
.should('have.class', 'inspected')
100108
.should('not.have.class', 'active')
101109
cy.get('.vuex-state-inspector').then(el => {
@@ -104,8 +112,8 @@ suite('vuex tab', () => {
104112
cy.get('#target').iframe().then(({ get }) => {
105113
get('#counter p').contains('1')
106114
})
107-
cy.get('.history .entry:nth-child(1) .entry-actions .action:nth-child(1)').click({ force: true })
108-
cy.get('.history .entry:nth-child(1)')
115+
cy.get('.history .entry[data-index="0"] .entry-actions .action:nth-child(1)').click({ force: true })
116+
cy.get('.history .entry[data-index="0"]')
109117
.should('have.class', 'inspected')
110118
.should('have.class', 'active')
111119
cy.get('#target').iframe().then(({ get }) => {
@@ -114,10 +122,10 @@ suite('vuex tab', () => {
114122
})
115123

116124
it('should revert', () => {
117-
cy.get('.history .entry:nth-child(4) .mutation-type').click({ force: true })
118-
cy.get('.history .entry:nth-child(4) .action:nth-child(2)').click({ force: true })
119-
cy.get('.history .entry').should('have.length', 3)
120-
cy.get('.history .entry:nth-child(3)')
125+
cy.get('.history .entry[data-index="3"] .mutation-type').click({ force: true })
126+
cy.get('.history .entry[data-index="3"]').find('.action:nth-child(2)').click({ force: true })
127+
cy.get('.history .entry[data-active="true"]').should('have.length', 3)
128+
cy.get('.history .entry[data-index="2"]')
121129
.should('have.class', 'inspected')
122130
.should('have.class', 'active')
123131
cy.get('.vuex-state-inspector').then(el => {
@@ -129,10 +137,10 @@ suite('vuex tab', () => {
129137
})
130138

131139
it('should commit', () => {
132-
cy.get('.history .entry:nth-child(3) .mutation-type').click({ force: true })
133-
cy.get('.history .entry:nth-child(3) .action:nth-child(1)').click({ force: true })
134-
cy.get('.history .entry').should('have.length', 1)
135-
cy.get('.history .entry:nth-child(1)')
140+
cy.get('.history .entry[data-index="2"] .mutation-type').click({ force: true })
141+
cy.get('.history .entry[data-index="2"] .action:nth-child(1)').click({ force: true })
142+
cy.get('.history .entry[data-active="true"]').should('have.length', 1)
143+
cy.get('.history .entry[data-index="0"]')
136144
.should('have.class', 'inspected')
137145
.should('have.class', 'active')
138146
cy.get('.vuex-state-inspector').then(el => {
@@ -153,7 +161,9 @@ suite('vuex tab', () => {
153161
.click({ force: true })
154162
.click({ force: true })
155163
})
156-
cy.get('.history .entry:nth-child(4)').click({ force: true })
164+
cy.get('.history .entry[data-index="3"]').click({ force: true })
165+
cy.get('.recording-vuex-state').should('not.be.visible')
166+
cy.get('.loading-vuex-state').should('not.be.visible')
157167
cy.get('.vuex-state-inspector').then(el => {
158168
expect(el.text()).to.include('isPositive:false')
159169
})

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@
4141
"circular-json-es6": "^2.0.1",
4242
"lodash.debounce": "^4.0.8",
4343
"lodash.groupby": "^4.6.0",
44+
"lru-cache": "^4.1.3",
4445
"uglifyjs-webpack-plugin": "^1.1.4",
4546
"vue": "^2.5.13",
4647
"vue-router": "^3.0.1",
48+
"vue-virtual-scroller": "^0.12.0",
4749
"vuex": "^3.0.1"
4850
},
4951
"devDependencies": {

shells/dev/target/Counter.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
<button class="increment" @click="increment">+1</button>
55
<button class="decrement" @click="decrement">-1</button>
66

7+
<br>
8+
9+
<button @click="doLotMutations">Do a lot of mutations</button>
10+
711
<p>Your counter is {{ $store.getters.isPositive ? 'positive' : 'negative' }}</p>
812
</div>
913
</template>
@@ -30,6 +34,14 @@ export default {
3034
},
3135
decrement () {
3236
this.$store.commit('DECREMENT', 2)
37+
},
38+
doLotMutations () {
39+
for (let i = 0; i < 10000; i++) {
40+
this.increment()
41+
}
42+
for (let i = 0; i < 10000; i++) {
43+
this.decrement()
44+
}
3345
}
3446
}
3547
}

shells/dev/target/EventChild.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
<div>
33
<button class="btn-emit-event" @click="emitEvent">Emit</button>
44
<button class="btn-emit-event1" @click="emitEvent1">Emit</button>
5-
<button class="btn-emit-event2" @click="emitEvent2">Emit</button>
5+
<button class="btn-emit-event2" @click="emitEvent2">Emit</button>
6+
7+
<br>
8+
9+
<button @click="emitManyEvents">Emit a lot of events</button>
610
</div>
711
</template>
812

@@ -35,6 +39,12 @@ export default {
3539
}
3640
}
3741
this.$emit('event-2', complexData)
42+
},
43+
44+
emitManyEvents () {
45+
for (let i = 0; i < 10000; i++) {
46+
this.$emit('event', i)
47+
}
3848
}
3949
}
4050
}

shells/dev/target/Events.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default {
2525
},
2626
methods: {
2727
log (data) {
28-
console.log('Event fired from child component with data', data)
28+
// console.log('Event fired from child component with data', data)
2929
}
3030
}
3131
}

0 commit comments

Comments
 (0)