Skip to content

perf: split shared Vue/@nextcloud/vue chunks across entry-points#119

Merged
rubenvdlinde merged 1 commit into
developmentfrom
perf/widget-bundle-size
May 5, 2026
Merged

perf: split shared Vue/@nextcloud/vue chunks across entry-points#119
rubenvdlinde merged 1 commit into
developmentfrom
perf/widget-bundle-size

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Same pattern that just landed on pipelinq and procest. Extracts shared Vue + @nextcloud/vue + pinia into stable-filename chunks so each entry keeps only entry-specific code.

Bundle sizes after npm run build

Bundle Before After
docudesk-main.js 6.06 MB 3.37 MB
docudesk-settings.js 5.97 MB 3.30 MB
docudesk-dashboard.js 3.63 MB 1.89 MB
docudesk-shared-nc-vue.js 2.71 MB (loaded once)
docudesk-shared-vendor.js 0.31 MB (loaded once)

Trade-off on /apps/mydash/

Docudesk only has one widget bundle (docudesk-dashboard.js), so unlike pipelinq/procest there is nothing to dedupe at the widget level today. On the mydash dashboard page the docudesk payload actually grows from 3.63 MB to 4.91 MB on cold cache (+1.28 MB) because the shared chunks are new bytes that previously did not exist. On warm cache (a returning user, or a user who has visited any docudesk page first) the shared chunks are already cached and only the 1.89 MB widget delta loads (-1.74 MB net).

Shipping this anyway because:

  • Docudesk is expected to grow more dashboard widgets (per discussion). Each new widget then adds only its widget-specific delta on top of the already-cached shared baseline, which is what the pattern was designed for.
  • /apps/docudesk/ and /apps/docudesk/admin benefit immediately from the cross-page caching.
  • Keeping the same shape across pipelinq / procest / docudesk is easier to reason about.

Validation

  • Built locally with NODE_OPTIONS=--max-old-space-size=8192 npm run build
  • Reloaded /apps/mydash/ and confirmed shared chunks load before docudesk-dashboard.js
  • Console: same baseline errors, no new errors

Test plan

  • CI build passes
  • Open /apps/mydash/, confirm docudesk widgets render (file entities, anonymization)
  • Open /apps/docudesk/, confirm main app renders

Mirror of the same fix that landed on pipelinq and procest, applied to
docudesk's main / settings / dashboard entry-points.

Each entry was inlining its own copy of Vue, @nextcloud/vue,
@conduction/nextcloud-vue, pinia and the material-design-icon set,
costing ~3 MB of duplicated framework JS per bundle.

Webpack 'optimization.splitChunks' now extracts two stable-filename
shared chunks. Each entry keeps only entry-specific code; the shared
chunks are loaded once per page and cached across navigations between
docudesk's own pages. Per-widget delta (currently 1.89 MB for
docudesk-dashboard.js, was 3.63 MB) is what every future widget will
add on top of the shared baseline.

The two existing dashboard widget classes call Util::addScript() for
the two shared chunks before the dashboard bundle. Util::addScript
dedupes by (app, file) so emitting both widget classes' scripts on
the same render still produces each chunk in the HTML exactly once.

After 'npm run build':

  docudesk-shared-nc-vue.js : (new)        2.71 MB
  docudesk-shared-vendor.js : (new)        0.31 MB
  docudesk-main.js          : 6.06 MB  →   3.37 MB
  docudesk-settings.js      : 5.97 MB  →   3.30 MB
  docudesk-dashboard.js     : 3.63 MB  →   1.89 MB

Note the trade-off on /apps/mydash/: the dashboard payload increases
from 3.63 MB to 4.91 MB on cold cache (+1.28 MB) because the shared
chunks are new bytes. On warm cache the user already has the shared
chunks and only the 1.89 MB widget delta loads. The pattern primarily
pays off as docudesk grows additional dashboard widgets — each new
widget then adds only ~1-2 MB of widget-specific code on top of the
shared baseline that's already cached.

Validated locally via mydash page load; no new console errors versus
baseline.
@rubenvdlinde rubenvdlinde merged commit 63aab60 into development May 5, 2026
13 checks passed
@rubenvdlinde rubenvdlinde deleted the perf/widget-bundle-size branch May 5, 2026 06:17
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Quality Report — ConductionNL/docudesk @ 58a76b4

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 108/108
npm ✅ 529/529
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-05 06:20 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde pushed a commit that referenced this pull request May 19, 2026
PR #119 split Vue / @nextcloud/vue / @conduction/nextcloud-vue / pinia /
vue-material-design-icons into shared chunks (docudesk-shared-vendor.js,
docudesk-shared-nc-vue.js), and updated the dashboard-widget loaders to
addScript them. The two page templates (templates/index.php and
templates/settings/admin.php) were missed: only the per-page entry was
loaded, so webpack's runtime sat forever in chunkOnLoad waiting for
chunks that never arrived. Result: blank Anonymisation page, blank
admin settings page, blank main app — no console error, just nothing.

Add the two shared chunks to both templates so the entry's
chunkOnLoad callback resolves. Order matters only to the extent that
the shared chunks self-register on a shared global before the entry
runs; loading them first is the conventional path.

Pipelinq and procest received the same split-chunks treatment in #119
and likely have the same broken template — worth a cross-check.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant