perf: prevent FPS drops during navigation in FluxNoteView
- Narrow backlinks watcher from entire store to store.files only, reducing trigger count from ~8 to 2 per navigation - Defer computation start by 300ms so it runs after the 250ms view transition animation completes - Yield to the browser between each file iteration using scheduler.yield() (with setTimeout fallback) to avoid blocking frames Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,93 +14,105 @@ import { confirmMessage } from "@/utils/notif"
|
|||||||
|
|
||||||
const isMarkdown = (filename?: string) => filename?.endsWith(".md") ?? false
|
const isMarkdown = (filename?: string) => filename?.endsWith(".md") ?? false
|
||||||
|
|
||||||
|
const yieldToMain = () =>
|
||||||
|
"scheduler" in globalThis
|
||||||
|
? (globalThis as unknown as { scheduler: { yield: () => Promise<void> } }).scheduler.yield()
|
||||||
|
: new Promise<void>((r) => setTimeout(r, 0))
|
||||||
|
|
||||||
export const useComputeBacklinks = () => {
|
export const useComputeBacklinks = () => {
|
||||||
const store = useUserRepoStore()
|
const store = useUserRepoStore()
|
||||||
|
|
||||||
watch(store, async () => {
|
watch(
|
||||||
if (!store.userSettings?.backlink) {
|
() => store.files,
|
||||||
return
|
async () => {
|
||||||
}
|
await new Promise<void>((r) => setTimeout(r, 300))
|
||||||
|
|
||||||
let notifiedForComputation = false
|
if (!store.userSettings?.backlink) {
|
||||||
|
|
||||||
const backlinks: Map<string, Backlink[]> = new Map()
|
|
||||||
|
|
||||||
for (const file of store.files) {
|
|
||||||
if (!isMarkdown(file.path) || !file.sha) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileBacklinkId = data.generateId(DataType.BacklinkNote, file.sha)
|
|
||||||
const fileBacklink = await data.get<DataType.BacklinkNote, BacklinkNote>(
|
|
||||||
fileBacklinkId
|
|
||||||
)
|
|
||||||
if (fileBacklink) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backlinks.has(file.sha)) {
|
|
||||||
backlinks.set(file.sha, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
const { getContent } = useFile(file.sha, false)
|
|
||||||
const note = await getContent()
|
|
||||||
|
|
||||||
if (!note) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = new DOMParser()
|
let notifiedForComputation = false
|
||||||
const htmlDoc = parser.parseFromString(note, "text/html")
|
|
||||||
|
|
||||||
const links = htmlDoc.querySelectorAll("a")
|
const backlinks: Map<string, Backlink[]> = new Map()
|
||||||
|
|
||||||
for (const link of links) {
|
for (const file of store.files) {
|
||||||
const href = link.getAttribute("href") ?? ""
|
await yieldToMain()
|
||||||
|
|
||||||
if (isExternalLink(href) || !isMarkdown(href)) {
|
if (!isMarkdown(file.path) || !file.sha) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = resolvePath(file.path ?? "", href)
|
const fileBacklinkId = data.generateId(DataType.BacklinkNote, file.sha)
|
||||||
const backlinkFile = store.files.find((file) => file.path === path)
|
const fileBacklink = await data.get<DataType.BacklinkNote, BacklinkNote>(
|
||||||
|
fileBacklinkId
|
||||||
if (!backlinkFile?.sha || !backlinkFile?.path) {
|
)
|
||||||
|
if (fileBacklink) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousBacklinks = backlinks.get(backlinkFile.sha) ?? []
|
if (!backlinks.has(file.sha)) {
|
||||||
|
backlinks.set(file.sha, [])
|
||||||
if (previousBacklinks.find((bl) => bl.sha === file.sha)) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notifiedForComputation) {
|
const { getContent } = useFile(file.sha, false)
|
||||||
notifiedForComputation = true
|
const note = await getContent()
|
||||||
confirmMessage("Updating backlinks...")
|
|
||||||
|
if (!note) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
backlinks.set(backlinkFile.sha, [
|
const parser = new DOMParser()
|
||||||
...previousBacklinks,
|
const htmlDoc = parser.parseFromString(note, "text/html")
|
||||||
{
|
|
||||||
sha: file.sha,
|
const links = htmlDoc.querySelectorAll("a")
|
||||||
title: filenameToNoteTitle(file.path ?? "")
|
|
||||||
|
for (const link of links) {
|
||||||
|
const href = link.getAttribute("href") ?? ""
|
||||||
|
|
||||||
|
if (isExternalLink(href) || !isMarkdown(href)) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [sha, fileBacklinks] of backlinks) {
|
const path = resolvePath(file.path ?? "", href)
|
||||||
const fileBacklinkId = data.generateId(DataType.BacklinkNote, sha)
|
const backlinkFile = store.files.find((file) => file.path === path)
|
||||||
const backlinkNote: BacklinkNote = {
|
|
||||||
_id: fileBacklinkId,
|
if (!backlinkFile?.sha || !backlinkFile?.path) {
|
||||||
$type: DataType.BacklinkNote,
|
continue
|
||||||
sha: sha,
|
}
|
||||||
links: fileBacklinks
|
|
||||||
|
const previousBacklinks = backlinks.get(backlinkFile.sha) ?? []
|
||||||
|
|
||||||
|
if (previousBacklinks.find((bl) => bl.sha === file.sha)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notifiedForComputation) {
|
||||||
|
notifiedForComputation = true
|
||||||
|
confirmMessage("Updating backlinks...")
|
||||||
|
}
|
||||||
|
|
||||||
|
backlinks.set(backlinkFile.sha, [
|
||||||
|
...previousBacklinks,
|
||||||
|
{
|
||||||
|
sha: file.sha,
|
||||||
|
title: filenameToNoteTitle(file.path ?? "")
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await data.update(backlinkNote)
|
for (const [sha, fileBacklinks] of backlinks) {
|
||||||
backlinkEventBus.emit({ fileSha: sha })
|
const fileBacklinkId = data.generateId(DataType.BacklinkNote, sha)
|
||||||
|
const backlinkNote: BacklinkNote = {
|
||||||
|
_id: fileBacklinkId,
|
||||||
|
$type: DataType.BacklinkNote,
|
||||||
|
sha: sha,
|
||||||
|
links: fileBacklinks
|
||||||
|
}
|
||||||
|
|
||||||
|
await data.update(backlinkNote)
|
||||||
|
backlinkEventBus.emit({ fileSha: sha })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user