Skip to content

Commit 9f6f14a

Browse files
committed
refactor: implement buildSmartRegExp utility and optimize regex usage in components
1 parent 5ed649d commit 9f6f14a

File tree

5 files changed

+49
-30
lines changed

5 files changed

+49
-30
lines changed

frontend/src/stores/subscribes.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
stringifyNoFolding,
1717
asyncPool,
1818
eventBus,
19+
buildSmartRegExp,
1920
} from '@/utils'
2021

2122
import type { Subscription } from '@/types/app'
@@ -142,12 +143,17 @@ export const useSubscribesStore = defineStore('subscribes', () => {
142143
}
143144

144145
if (s.type !== 'Manual') {
146+
const r1 = s.include && buildSmartRegExp(s.include)
147+
const r2 = s.exclude && buildSmartRegExp(s.exclude)
148+
const r3 = s.includeProtocol && buildSmartRegExp(s.includeProtocol)
149+
const r4 = s.excludeProtocol && buildSmartRegExp(s.excludeProtocol)
150+
145151
proxies = proxies.filter((v) => {
146-
const flag1 = s.include ? new RegExp(s.include, 'i').test(v.name) : true
147-
const flag2 = s.exclude ? !new RegExp(s.exclude, 'i').test(v.name) : true
148-
const flag3 = s.includeProtocol ? new RegExp(s.includeProtocol, 'i').test(v.type) : true
149-
const flag4 = s.excludeProtocol ? !new RegExp(s.excludeProtocol, 'i').test(v.type) : true
150-
return flag1 && flag2 && flag3 && flag4
152+
const flag1 = r1 ? r1.test(v.name) : true
153+
const flag2 = r2 ? r2.test(v.name) : false
154+
const flag3 = r3 ? r3.test(v.type) : true
155+
const flag4 = r4 ? r4.test(v.type) : false
156+
return flag1 && !flag2 && flag3 && !flag4
151157
})
152158

153159
if (s.proxyPrefix) {

frontend/src/utils/others.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,21 @@ export const stringifyNoFolding = (content: any) => {
285285
// Disable string folding
286286
return stringify(content, { lineWidth: 0, minContentWidth: 0 })
287287
}
288+
289+
const regexCache = new Map()
290+
291+
export const buildSmartRegExp = (pattern: string, flags = '') => {
292+
const key = pattern + '::' + flags
293+
if (regexCache.has(key)) return regexCache.get(key)
294+
295+
let r
296+
try {
297+
r = new RegExp(pattern, flags)
298+
} catch {
299+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
300+
r = new RegExp(escaped, flags)
301+
}
302+
303+
regexCache.set(key, r)
304+
return r
305+
}

frontend/src/views/HomeView/components/GroupsController.vue

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ import { ControllerCloseMode } from '@/enums/app'
1515
import { ProxyGroupType } from '@/enums/kernel'
1616
import { useBool } from '@/hooks'
1717
import { useAppSettingsStore, useKernelApiStore } from '@/stores'
18-
import { ignoredError, sleep, handleUseProxy, message, createAsyncPool } from '@/utils'
18+
import {
19+
ignoredError,
20+
sleep,
21+
handleUseProxy,
22+
message,
23+
createAsyncPool,
24+
buildSmartRegExp,
25+
} from '@/utils'
1926
2027
const expandedSet = ref<Set<string>>(new Set())
2128
const loadingSet = ref<Set<string>>(new Set())
@@ -56,12 +63,7 @@ const groups = computed(() => {
5663
proxies[proxy]?.all ||
5764
proxies[proxy]?.alive
5865
const keywords = filterKeywordsMap.value[group.name]
59-
let condition2 = false
60-
try {
61-
condition2 = keywords ? new RegExp(keywords, 'i').test(proxy) : true
62-
} catch {
63-
condition2 = keywords ? proxy.includes(keywords) : true
64-
}
66+
const condition2 = keywords ? buildSmartRegExp(keywords, 'i').test(proxy) : true
6567
return condition1 && condition2
6668
})
6769
.map((proxy) => {

frontend/src/views/HomeView/components/LogsController.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n'
55
import { LogLevelOptions } from '@/constant'
66
import { useBool } from '@/hooks'
77
import { useKernelApiStore } from '@/stores'
8-
import { isValidIPv4, isValidIPv6, addToRuleSet, message, picker } from '@/utils'
8+
import { isValidIPv4, isValidIPv6, addToRuleSet, message, picker, buildSmartRegExp } from '@/utils'
99
1010
import { type PickerItem } from '@/components/Picker/index.vue'
1111
@@ -14,7 +14,6 @@ import type { Menu } from '@/types/app'
1414
const logType = ref<'error' | 'warning' | 'info' | 'debug'>('info')
1515
const keywords = ref('')
1616
const logs = ref<{ type: string; payload: string }[]>([])
17-
const keywordsRegexp = computed(() => new RegExp(keywords.value))
1817
1918
const LogLevelMap = {
2019
silent: ['silent'],
@@ -27,7 +26,7 @@ const LogLevelMap = {
2726
const filteredLogs = computed(() => {
2827
return logs.value.filter((v) => {
2928
const hitType = LogLevelMap[logType.value].includes(v.type)
30-
const hitName = keywordsRegexp.value.test(v.payload)
29+
const hitName = buildSmartRegExp(keywords.value, 'i').test(v.payload)
3130
return hitName && hitType
3231
})
3332
})

frontend/src/views/SubscribesView/components/ProxiesView.vue

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ import { ClipboardSetText, ReadFile, WriteFile } from '@/bridge'
77
import { DraggableOptions } from '@/constant'
88
import { useBool } from '@/hooks'
99
import { useSubscribesStore } from '@/stores'
10-
import { deepClone, ignoredError, sampleID, stringifyNoFolding, message } from '@/utils'
10+
import {
11+
deepClone,
12+
ignoredError,
13+
sampleID,
14+
stringifyNoFolding,
15+
message,
16+
buildSmartRegExp,
17+
} from '@/utils'
1118
1219
import Button from '@/components/Button/index.vue'
1320
@@ -30,14 +37,6 @@ const sub = ref(deepClone(props.sub))
3037
3138
const [showDetails, toggleDetails] = useBool(false)
3239
33-
const keywordsRegexp = computed(() => {
34-
try {
35-
return new RegExp(keywords.value, 'i')
36-
} catch {
37-
return keywords.value
38-
}
39-
})
40-
4140
const filteredProxyTypeOptions = computed(() => {
4241
const proxyProtocols = sub.value.proxies.reduce((p, c) => {
4342
p[c.type] = (p[c.type] || 0) + 1
@@ -55,12 +54,7 @@ const filteredProxyTypeOptions = computed(() => {
5554
const filteredProxies = computed(() => {
5655
return sub.value.proxies.filter((v) => {
5756
const hitType = proxyType.value ? proxyType.value === v.type : true
58-
let hitName = true
59-
if (typeof keywordsRegexp.value === 'string') {
60-
hitName = v.name.toLowerCase().includes(keywordsRegexp.value.toLowerCase())
61-
} else {
62-
hitName = keywordsRegexp.value.test(v.name)
63-
}
57+
const hitName = buildSmartRegExp(keywords.value, 'i').test(v.name)
6458
return hitName && hitType
6559
})
6660
})

0 commit comments

Comments
 (0)