mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
perf(ui): cut initial-load bundle via lazy model-viewer and preload fix
This commit is contained in:
parent
5db8fb14a4
commit
1318a32b71
@ -537,6 +537,7 @@ import {
|
||||
} from '@element-plus/icons-vue'
|
||||
import { useStreamingMarkdown } from '@/composables/useStreamingMarkdown'
|
||||
import { buildGeneratedFileNameMap, linkifyGeneratedFileUrls } from '@/utils/generatedFileLinks'
|
||||
import { ensureModelViewer } from '@/utils/lazyModelViewer'
|
||||
import { useAuthenticatedAttachment } from '@/composables/useAuthenticatedAttachment'
|
||||
import { useToolLabel } from '@/composables/useToolLabel'
|
||||
import { http } from '@/api'
|
||||
@ -901,9 +902,14 @@ watch(audioAttachments, (atts) => {
|
||||
if (atts.length > 0) loadAllAudios(atts)
|
||||
}, { immediate: true })
|
||||
// 3D models also need the auth-blob loader — <model-viewer src> doesn't carry
|
||||
// the Authorization header any more than <img>/<audio> do.
|
||||
// the Authorization header any more than <img>/<audio> do. The heavy
|
||||
// @google/model-viewer Web Component is lazy-loaded here (only when a bubble
|
||||
// actually has a 3D attachment) instead of eagerly in main.ts.
|
||||
watch(model3dAttachments, (atts) => {
|
||||
if (atts.length > 0) loadAllModels(atts)
|
||||
if (atts.length > 0) {
|
||||
ensureModelViewer()
|
||||
loadAllModels(atts)
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// --- 时间 ---
|
||||
|
||||
@ -9,10 +9,10 @@ import router from './router'
|
||||
import './assets/main.css'
|
||||
import { i18n, initializeLocale } from './i18n'
|
||||
|
||||
// Side-effect import: registers the <model-viewer> Web Component globally so
|
||||
// generated 3D assets (.glb) can be previewed inline in chat bubbles. Vue's
|
||||
// compiler is told to treat the tag as a custom element via vite.config.ts.
|
||||
import '@google/model-viewer'
|
||||
// Note: the heavy <model-viewer> Web Component is no longer imported here. It is
|
||||
// lazy-registered on demand (see src/utils/lazyModelViewer.ts) the first time a
|
||||
// chat bubble renders a 3D (.glb) asset, keeping it out of the initial bundle.
|
||||
// Vue's compiler still treats <model-viewer> as a custom element via vite.config.ts.
|
||||
|
||||
async function bootstrap() {
|
||||
await initializeLocale()
|
||||
|
||||
22
mateclaw-ui/src/utils/lazyModelViewer.ts
Normal file
22
mateclaw-ui/src/utils/lazyModelViewer.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Lazy registrar for the <model-viewer> Web Component.
|
||||
*
|
||||
* @google/model-viewer is a heavy dependency (~hundreds of KB) that used to be
|
||||
* imported eagerly in main.ts, landing it in the initial entry chunk on every
|
||||
* first paint even though 3D (.glb) previews are rare. This defers the import to
|
||||
* the first time a chat bubble actually renders a 3D attachment. The module-level
|
||||
* promise makes it a process-wide singleton — the component registers exactly
|
||||
* once no matter how many bubbles request it.
|
||||
*
|
||||
* Vue's compiler still treats <model-viewer> as a custom element via the
|
||||
* isCustomElement option in vite.config.ts, so the tag resolves whether or not
|
||||
* the definition has loaded yet; once this import resolves the element upgrades.
|
||||
*/
|
||||
let loadPromise: Promise<unknown> | null = null
|
||||
|
||||
export function ensureModelViewer(): Promise<unknown> {
|
||||
if (!loadPromise) {
|
||||
loadPromise = import('@google/model-viewer')
|
||||
}
|
||||
return loadPromise
|
||||
}
|
||||
@ -88,18 +88,38 @@ export default defineConfig({
|
||||
// chunk lets each Worker on the chunk run with a smaller
|
||||
// working set, which is what blew up the Docker build at
|
||||
// 1.3.0 (needed --max-old-space-size=6144 as a band-aid).
|
||||
manualChunks: {
|
||||
'vendor-monaco': ['monaco-editor', '@guolao/vue-monaco-editor'],
|
||||
'vendor-vue-flow': [
|
||||
'@vue-flow/core',
|
||||
'@vue-flow/background',
|
||||
'@vue-flow/controls',
|
||||
'@vue-flow/minimap',
|
||||
],
|
||||
'vendor-mermaid': ['mermaid'],
|
||||
'vendor-echarts': ['echarts'],
|
||||
'vendor-element': ['element-plus', '@element-plus/icons-vue'],
|
||||
'vendor-markdown': ['marked', 'marked-highlight', 'highlight.js', 'dompurify'],
|
||||
//
|
||||
// Function form (not the object map) so we can ISOLATE the CommonJS
|
||||
// interop helpers into their own tiny chunk. With the object form,
|
||||
// Rollup hoisted the shared `commonjsHelpers` module into
|
||||
// vendor-mermaid / vendor-markdown; because the eager entry chunk needs
|
||||
// that helper, it gained a *static* import of both ~1 MB vendor chunks,
|
||||
// and Vite then `modulepreload`-ed them on first paint even though
|
||||
// mermaid/markdown are only used on the (lazy) chat route. Splitting the
|
||||
// helper into its own leaf chunk lets the entry depend on a few-hundred-
|
||||
// byte module instead, so the heavy vendors stay purely on-demand.
|
||||
manualChunks(id) {
|
||||
// Isolate shared runtime helpers FIRST — before the node_modules gate.
|
||||
// The Vite preload helper (\0vite/preload-helper, exported as the
|
||||
// __vitePreload used by every lazy route import) and the CommonJS
|
||||
// interop helpers are virtual modules with no node_modules path. If one
|
||||
// lands inside a heavy vendor chunk, the entry's route-lazy imports pull
|
||||
// that whole chunk in as a static dependency and Vite preloads it. A
|
||||
// dedicated leaf chunk keeps them out of the vendors.
|
||||
if (id.includes('vite/preload-helper') || id.includes('commonjsHelpers')
|
||||
|| id.includes('commonjs-dynamic-modules') || id.includes('\0commonjs')) {
|
||||
return 'vendor-runtime'
|
||||
}
|
||||
if (!id.includes('node_modules')) return undefined
|
||||
if (id.includes('monaco-editor') || id.includes('vue-monaco-editor')) return 'vendor-monaco'
|
||||
if (id.includes('@vue-flow')) return 'vendor-vue-flow'
|
||||
if (id.includes('/mermaid/')) return 'vendor-mermaid'
|
||||
if (id.includes('/echarts/') || id.includes('zrender')) return 'vendor-echarts'
|
||||
if (id.includes('element-plus') || id.includes('@element-plus')) return 'vendor-element'
|
||||
if (id.includes('/marked') || id.includes('highlight.js') || id.includes('dompurify')) {
|
||||
return 'vendor-markdown'
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user