Skip to content
This repository was archived by the owner on Jan 6, 2024. It is now read-only.

Commit f7b186c

Browse files
perf: improve graph page performance (#145)
1 parent 8eb54df commit f7b186c

File tree

3 files changed

+84
-50
lines changed

3 files changed

+84
-50
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
"lint": "eslint .",
5454
"prepublishOnly": "npm run build",
5555
"release": "bumpp -r",
56-
"dep:up": "taze -I major"
56+
"dep:up": "taze -I major",
57+
"prepare": "simple-git-hooks"
5758
},
5859
"peerDependencies": {
5960
"vite": "^3.1.0 || ^4.0.0-0"

packages/client/logic/graph.ts

+61-36
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Fuse from 'fuse.js'
33
import { Minimatch } from 'minimatch'
44
import { rpc } from './rpc'
55

6-
export const list = ref<ModuleInfo[]>(await rpc.componentGraph())
6+
const list = ref<ModuleInfo[]>(await rpc.componentGraph())
77
export const searchText = useStorage('vite-inspect-search-text', '')
88
export const includeNodeModules = useStorage('vite-inspect-include-node-modules', false)
99
export const includeVirtual = useStorage('vite-inspect-include-virtual', false)
@@ -63,61 +63,86 @@ function uniqById(data: typeof list.value) {
6363
return uniqueArray
6464
}
6565

66-
function fuzzySearchDeps(data: typeof list.value, id: string) {
66+
function fuzzySearchDeps(data: ModuleInfo[], text: string) {
6767
const fuzzySearcher = new Fuse(data, {
6868
ignoreLocation: true,
6969
keys: ['id'],
7070
shouldSort: true,
7171
threshold: 0.1,
7272
})
73-
const result = fuzzySearcher.search(id).map(i => i.item)
74-
if (!result) {
73+
const ids = fuzzySearcher.search(text).map(i => i.item.id)
74+
if (!ids.length) {
7575
return {
76-
main: [],
77-
allWithDeps: [],
76+
matchedKeys: [],
77+
data: [],
7878
}
7979
}
8080
return {
81-
main: result,
82-
allWithDeps: uniqById(result.flatMap(item => getDepsByExactId(data, item.id))),
81+
matchedKeys: ids,
82+
data: uniqById(ids.flatMap(id => getDepsByExactId(data, id))),
8383
}
8484
}
8585

86-
function filterByUserDefinedGlob(data: typeof list.value) {
87-
if (!userCustomGlobPattern.value.trim().length)
86+
function filterByUserDefinedGlob(data: ModuleInfo[], pattern: string) {
87+
if (!pattern.trim().length)
8888
return data
89-
const globPattern = userCustomGlobPattern.value.trim().split(', ')
90-
const globInstances = new Map(globPattern.map(pattern => [pattern, new Minimatch(pattern, { matchBase: true })]))
91-
return data.filter(item => globPattern.every(pattern =>
89+
const globPatterns = pattern.trim().split(', ')
90+
const globInstances = new Map(globPatterns.map(pattern => [pattern, new Minimatch(pattern, { matchBase: true, dot: true, partial: true })]))
91+
return data.filter(item => globPatterns.every(pattern =>
9292
globInstances.get(pattern)!.match(item.id),
9393
))
9494
}
9595

9696
const { graphSettings } = useGraphSettings()
9797

98-
export const searchResults = computed(() => {
99-
let data = (
100-
list.value
101-
) || []
102-
103-
if (!includeNodeModules.value)
104-
data = data.filter(item => !item.id.includes('/node_modules/'))
105-
if (!includeVirtual.value)
106-
data = data.filter(item => !item.virtual)
107-
108-
if (graphSettings.value.enableUserDefinedGlob)
109-
data = filterByUserDefinedGlob(data)
110-
111-
if (!searchText.value) {
112-
return {
113-
main: [],
114-
data,
98+
const getDebounceTime = (len: number) => len > 85 ? 350 : 150
99+
export const searchResults = ref<ModuleInfo[]>([])
100+
export const matchedKeys = ref<string[]>([])
101+
102+
const filterer = {
103+
excludeNodeModules: (data: ModuleInfo[]) => {
104+
return includeNodeModules.value ? data : data.filter(item => !item.id.includes('/node_modules/'))
105+
},
106+
excludeVirtual: (data: ModuleInfo[]) => {
107+
return includeVirtual.value ? data : data.filter(item => !item.virtual)
108+
},
109+
userCustomGlobPattern: (data: ModuleInfo[], pattern: string) => {
110+
if (!graphSettings.value.enableUserDefinedGlob || !pattern.trim().length)
111+
return data
112+
return filterByUserDefinedGlob(data, pattern)
113+
},
114+
searchText: (data: ModuleInfo[], text: string) => {
115+
if (!text.trim().length) {
116+
matchedKeys.value = []
117+
return data
115118
}
116-
}
119+
const { data: searchData, matchedKeys: searchMatchedKeys } = fuzzySearchDeps(data, searchText.value.trim())
120+
matchedKeys.value = searchMatchedKeys
121+
return searchData
122+
},
123+
}
117124

118-
const { main, allWithDeps } = fuzzySearchDeps(data, searchText.value.trim())
119-
return {
120-
main,
121-
data: allWithDeps,
122-
}
125+
const allDataCanBeSearched = computed(() => {
126+
return filterer.excludeVirtual(
127+
filterer.excludeNodeModules(list.value),
128+
)
123129
})
130+
131+
debouncedWatch([searchText, userCustomGlobPattern, allDataCanBeSearched], ([,,list]) => {
132+
filterData(false, list)
133+
}, { debounce: computed(() => getDebounceTime(searchResults.value.length)) })
134+
135+
async function filterData(isInit = false, givenData?: ModuleInfo[]) {
136+
let data: ModuleInfo[] = givenData ?? []
137+
if (isInit)
138+
data = list.value = await rpc.componentGraph()
139+
data = filterer.searchText(
140+
filterer.userCustomGlobPattern(
141+
filterer.excludeVirtual(
142+
filterer.excludeNodeModules(data),
143+
), userCustomGlobPattern.value)
144+
, searchText.value)
145+
searchResults.value = data
146+
}
147+
148+
await filterData(true)

packages/client/pages/graph.vue

+21-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import type { Data, Options } from 'vis-network'
33
import { Network } from 'vis-network'
4+
import { matchedKeys } from '../logic/graph'
45
import { searchResults as modules } from '~/logic/graph'
56
import { useDevToolsClient } from '~/logic/client'
67
import { rootPath } from '~/logic/global'
@@ -29,8 +30,19 @@ function getHoverPath(level: GraphSettingsType['hoverPathLevel'], fullPath: stri
2930
}
3031
}
3132
33+
const isMatched = (id: string) => matchedKeys.value.includes(id)
34+
35+
function getFontStyle(id: string) {
36+
return {
37+
color: isMatched(id)
38+
? '#F19B4A'
39+
: isDark.value ? 'white' : 'black',
40+
multi: 'html',
41+
}
42+
}
43+
3244
const data = computed<Data>(() => {
33-
const { data, main } = modules.value
45+
const data = modules.value
3446
if (!data)
3547
return { node: [], edges: [] }
3648
const nodes: Data['nodes'] = data.map((mod) => {
@@ -42,20 +54,14 @@ const data = computed<Data>(() => {
4254
modulesMap.value.set(id, { filePath: path })
4355
else
4456
modulesMap.value.get(id)!.filePath = path
45-
const isInMain = !!main.find(i => i.id === id)
4657
4758
return {
4859
id,
49-
label: isInMain ? `<b>${pathSegments.at(-1)}</b>` : pathSegments.at(-1),
60+
label: isMatched(id) ? `<b>${pathSegments.at(-1)}</b>` : pathSegments.at(-1),
5061
title: getHoverPath(settings.graphSettings.value.hoverPathLevel, path, rootPath),
5162
group: path.match(/\.(\w+)$/)?.[1] || 'unknown',
5263
size: 15 + Math.min(mod.deps.length / 2, 8),
53-
font: {
54-
color: isInMain
55-
? '#F19B4A'
56-
: isDark.value ? 'white' : 'black',
57-
multi: 'html',
58-
},
64+
font: getFontStyle(id),
5965
shape: mod.id.includes('/node_modules/')
6066
? 'hexagon'
6167
: mod.virtual
@@ -135,7 +141,9 @@ onMounted(() => {
135141
if (!settings.graphSettings.value.highlightSelection)
136142
return
137143
// @ts-expect-error network body typing error
138-
network.body.data.nodes.update(network.body.data.nodes.getIds().map(id => ({ id, size: 16 })))
144+
network.body.data.nodes.update(network.body.data.nodes.getIds().map(id => ({
145+
id, size: 16, font: getFontStyle(id),
146+
})))
139147
// @ts-expect-error network body typing error
140148
network.body.data.edges.update(network.body.data.edges.getIds().map((id) => {
141149
// @ts-expect-error network body typing error
@@ -163,9 +171,9 @@ onMounted(() => {
163171
// @ts-expect-error network body typing error
164172
const nonConnectedEdges = network.body.data.edges.getIds().filter(id => !network.getConnectedEdges(nodeId).includes(id))
165173
// @ts-expect-error network body typing error
166-
network.body.data.nodes.update(nonConnectedNodes.map(id => ({ id, color: 'rgb(69,69,69,.3)' })))
174+
network.body.data.nodes.update(nonConnectedNodes.map(id => ({ id, color: '#cccccc10', font: { color: '#cccccc10' } })))
167175
// @ts-expect-error network body typing error
168-
network.body.data.edges.update(nonConnectedEdges.map(id => ({ id, color: 'rgb(69,69,69,.3)' })))
176+
network.body.data.edges.update(nonConnectedEdges.map(id => ({ id, color: '#cccccc10' })))
169177
// @ts-expect-error network body typing error
170178
network.body.data.nodes.update([{ id: nodeId, color: options.groups[network.body.data.nodes.get(nodeId).group]?.color, size: 26 }])
171179
lastSelectedNode.value = nodeId
@@ -194,7 +202,7 @@ const { showGraphSetting } = useGraphSettings()
194202
</button>
195203
</template>
196204
</SearchBox>
197-
<div ref="container" flex="1" :class="[isHoveringNode && metaKeyPressed ? 'cursor-pointer' : '']" />
205+
<div ref="container" flex="1" :class="[isHoveringNode ? 'cursor-pointer' : '']" />
198206
<GraphSettings />
199207
</div>
200208
</template>

0 commit comments

Comments
 (0)