وب مدرن، رابط کاربری و تجربه‌ی وب · فصل ۳ — Type System، Runtime، Async و Build · 44

Vite Build, Assets, and Code Splitting

Build a bounded feature for production with a deliberate asset path and lazy module boundary, inspect the generated output graph, and verify the built application under its intended base path without treating chunk count or build success as correctness proof. می‌تواند «Vite Build, Assets و Code Splitting» را در یک feature محدود طراحی/پیاده‌سازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.

Vite Build, Assets و Code Splitting — fa-IR

Lesson summary

Build a bounded feature for production with a deliberate asset path and lazy module boundary, inspect the generated output graph, and verify the built application under its intended base path without treating chunk count or build success as correctness proof.

می‌تواند «Vite Build, Assets و Code Splitting» را در یک feature محدود طراحی/پیاده‌سازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.

This lesson produces

می‌تواند «Vite Build, Assets و Code Splitting» را در یک feature محدود طراحی/پیاده‌سازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.

Who it is for

Foundation

Worked baseline, explicit evidence table, safe local fixture and one guided failure probe.

Repair

Reduce to one failing contract, observe evidence, repair, then restore full case.

Experienced

Skip syntax walkthrough; require independent implementation, mutation evidence and residual-risk statement.

Prerequisites

Dev success is not production proof

A small feature works perfectly at / in the Vite development server. It imports an illustration, and a rarely used inspector panel is loaded only when the learner opens it. The application is then deployed under /portal/. The HTML opens, but the illustration is missing and the lazy panel fails on first use. The source code did not suddenly become invalid; the deployment context exposed assumptions that the dev server had hidden.

This lesson owns that boundary. Your completion artifact is Vite Production Build Evidence Record v1. It must connect source intent to built output: the configured public base path, source-referenced assets, the emitted URLs found in dist, the lazy module boundary, a deliberate failure mutation, the repaired build, a changed-context transfer case, and one residual limitation. A green vite build is evidence that the build command completed. It is not, by itself, proof that the built application works at its intended public URL. [S-VITE-BUILD]

The working scenario is bounded: the app is intended for /portal/, one image is imported from source, and one optional module is loaded with dynamic import(). You will inspect the output graph and verify the built application under that base. You will not treat an exact chunk filename, a particular number of chunks, or a hash in a filename as a correctness contract.

Scope, route, and evidence bar

WEB-001 is the hard conceptual prerequisite. Reuse its bounded browser/HTML evidence when needed; do not re-teach the whole foundation. WEB-003 is a soft prerequisite because the lazy module returns a Promise, but runtime task-concurrency policy remains owned by WEB-003. This lesson owns production-build context, asset URL transformation, base-path behavior, and an intentional split boundary. CDN policy, release infrastructure, long-term cache configuration, and deployment orchestration are adjacent concerns rather than hidden requirements here.

Foundation route: keep the source-to-output matrix visible and follow the guided fixture once. Repair route: isolate one failed dimension—build context, asset graph, base path, or split boundary—then rerun the smallest probe. Experienced route: skip introductory syntax, but still inspect built URLs, exercise the lazy path, run a mutation, and record transfer evidence. The evidence bar does not change with the route.

The completion artifact must distinguish observation from inference. “dist/index.html contains /portal/assets/...” is an observation. “Therefore every host configuration is correct” is an unsupported inference. “The lazy module was requested only after interaction in the tested preview” is an observation. “Vite will always emit exactly one chunk with this filename” is an overclaim.

Mental model: source graph → build graph → emitted graph → served base → runtime request

Keep one causal chain in view:

source imports/assets + base config → Vite build graph → emitted HTML/CSS/JS/assets → server mounted at the intended base → browser requests initial and lazy resources → evidence.

Vite treats files referenced through source imports as part of the build graph. In production, those references can become emitted URLs whose names and locations are build outputs rather than source filenames. CSS url() references and HTML asset references participate in the same base-path adjustment described by Vite's build guidance. [S-VITE-BUILD] [S-VITE-ASSETS]

The base option answers a public-URL question: from which path should the built application expect its generated resources to be reachable? The current shared option accepts an absolute URL pathname such as /portal/, a full URL, or relative forms for embedded deployment. In this lesson the contract is explicit: /portal/. [S-VITE-BUILD]

Dynamic import() introduces a loading boundary. It creates an opportunity for code splitting, but the correctness contract is not “one dynamic import equals one stable file.” Bundler optimization, shared dependencies, and version changes can affect output topology. Verify behavior and relationships: the optional module is not needed for the initial feature path; interaction can load it; the built application can resolve it from the intended base; and your evidence does not depend on a magic filename.

Foundation: four contracts that must stay separate

1. Build-context contract

Development and production are different execution contexts. The dev server resolves modules and assets for development; vite build produces a production bundle. The first required move is therefore not “run build and trust it,” but “run build, inspect output, and exercise that output.” Vite documents vite preview as a local way to preview a built directory and explicitly does not position it as a production server. That is enough for a bounded local verification fixture; it is not a hosting architecture recommendation. [S-VITE-BUILD]

2. Asset-graph contract

Prefer an imported asset when it belongs to source behavior:

import badgeUrl from './assets/status-badge.svg'
document.querySelector('#status-badge').src = badgeUrl

During development the URL can reflect the source path. During production build, Vite can emit a transformed URL and hashed asset. The learner contract is to inspect the result rather than predict the exact hash. Public-directory assets have a different contract: they keep their filename and are copied to the output root. Vite's asset guide recommends source imports in general unless the special guarantees of public are actually needed. [S-VITE-ASSETS]

3. Base-path contract

If the application will be served under /portal/, configure the build for /portal/. Vite states that JS-imported assets, CSS url() references, and asset references in HTML are adjusted to respect the configured base during build. A source string such as '/assets/status-badge.svg' is not automatically the same thing as an imported build-graph asset. A hard-coded root URL can therefore survive source review and still be wrong for a nested mount. [S-VITE-BUILD]

4. Split-boundary contract

Use a lazy boundary because the module is optional, not because “more chunks is always faster.” For example:

button.addEventListener('click', async () => {
  const { mountInspector } = await import('./inspector.js')
  mountInspector(document.querySelector('#inspector-host'))
})

The evidence question is whether the built graph preserves an intentional optional path and whether the browser can load that path under /portal/. Chunk count is descriptive output, not the requirement.

Deep explanation: why plausible shortcuts fail

A common shortcut is to test only npm run dev. That path can hide the exact thing you need to know: how production URLs are rendered. If the app is always opened at /, a root-relative path such as /logo.svg looks harmless. When the same built app is mounted under /portal/, that string still points to the site root. By contrast, a source import gives Vite a graph edge it can transform, and configured base gives the build a public-path context. The lesson does not claim imported assets solve every deployment; it claims they make this bounded transformation inspectable.

Another shortcut is to look at dist/assets and infer that a filename containing a hash proves caching is correct. The hash only tells you something about the emitted name chosen by the build. Correct cache headers, invalidation policy, CDN behavior, and release atomicity are separate deployment concerns. Record the emitted filename if useful, but keep the evidence statement narrow: “the HTML/JS references the emitted asset URL that exists in this build.”

Code splitting produces another seductive shortcut. A learner sees inspector-XYZ.js and writes a test that requires that exact name. That test is brittle because the output name is not the semantic boundary. A stronger probe discovers the initial entry scripts from generated HTML, records the emitted graph, then verifies that the optional feature is loaded after the action that requires it. If a bundler version changes naming or combines shared pieces differently while preserving behavior, your contract should still survive.

Current Vite releases use a unified Rolldown-based production build toolchain. That implementation detail can change across major/minor releases, so the lesson binds factual claims to the current official source review rather than teaching internal chunk algorithms as stable curriculum. The stable learner skill is reading the source/output relationship and testing the deployed-base behavior. [S-VITE-OPTIONS]

Worked example: /portal/, one imported image, one lazy inspector

The fixture begins with src/main.js, src/assets/status-badge.svg, and src/inspector.js. The intended deployment base is /portal/. The starter deliberately makes the contract visible rather than solving it for you: it includes a place for the image, a button for the optional inspector, and an evidence template.

A correct implementation has three observable relationships.

First, the image is source-referenced. After build, inspect generated HTML/JS/CSS and the output files. Record the source locator, the emitted locator you actually observed, and whether that emitted target exists. Do not type the expected hash into your test.

Second, base is configured for /portal/. Inspect generated HTML for script/style/resource URLs. Then serve or preview the built output in a way that preserves the intended base. Record the tested URL. A build that only works when moved back to / has not met this scenario's contract.

Third, the inspector is lazy. Before clicking the button, record the initial state. After clicking, record that the inspector mounts and that the built resource path resolves. The optional module's exact emitted filename is evidence data, not a specification.

Complete WEB-014-TAB1 with columns for dimension, source intent, predicted build relation, observed output, runtime observation, mutation result, repair result, and residual limitation. Use actual observations. “Looks fine” is not an acceptable cell.

Failure probes: your conclusion must change when the contract breaks

Mutation A — wrong base plus hard-coded root asset

Predict first. Change the build base away from /portal/ or retain a source URL beginning at / for an asset that should follow the build graph. Build again. Inspect the emitted references and test under the intended nested path. The expected lesson is not that root-relative URLs are universally wrong; it is that this URL violates this fixture's declared deployment base and source-asset contract.

Mutation B — eager import replaces the optional boundary

Replace the dynamic import with a static top-level import. Build again and compare the output/loading relationship. The feature may still function. That does not mean the mutation is invisible: the intentional optional-loading contract changed. Diagnose the contract change without insisting on an exact number of chunks.

Mutation C — imported asset becomes a public asset

Move the illustration into public and reference it according to the public-directory contract. Observe what changed: filename preservation, source-graph relationship, and output location. This can be a valid design when you need public-directory guarantees. The mutation is useful because it forces you to state which contract you intended instead of memorizing “imports good, public bad.” [S-VITE-ASSETS]

A robust evidence method detects each mutation for the right reason. It should not fail merely because a hash changed, and it should not pass solely because vite build exited with code zero.

Experienced bridge: verify architecture, not trivia

For an experienced learner, the main risk is often overfitting build internals. Skip the syntax tour and answer these questions directly:

  1. Which source references must be transformed by the build graph, and which deliberately remain public-directory URLs?
  2. What public base is part of the release contract?
  3. Which feature path is intentionally lazy, and what user or application event crosses that boundary?
  4. Which observations prove the built app resolves both initial and lazy resources at that base?
  5. Which part of the output graph is descriptive and therefore must not be locked to an exact filename?

Then run one mutation. If your evidence cannot distinguish the mutated build from the intended build, the evidence is too weak. If the evidence rejects a harmless filename change while behavior and graph relationships remain valid, it is too brittle.

Alternatives and trade-offs

Imported asset vs public. An imported asset participates in source analysis and build transformation. A public asset is copied as-is and referenced from the public root. Choose based on required semantics. Do not move files to public merely to avoid understanding transformed URLs. [S-VITE-ASSETS]

Absolute nested base vs relative base. /portal/ is appropriate when the deployment mount is known. Vite also supports relative base forms for deployments whose final base is not known ahead of time. This lesson fixes /portal/ so verification has a precise target; it does not claim that setting is universal. [S-VITE-BUILD]

Lazy vs eager module. Lazy loading can reduce initial work for an optional feature, but it also creates an additional runtime request path and error boundary. Eager loading can be simpler when the code is always needed. “More splitting” is not a goal; deliberate loading behavior is.

Preview vs real hosting. A local preview is useful for testing the built artifacts, but it is not evidence for every reverse proxy, CDN, header, service-worker, or cache configuration. State that residual boundary instead of silently upgrading a local pass into production certification. [S-VITE-BUILD]

Practice: guided repair, then independent transfer

Guided pass

Use the starter fixture. Write a one-sentence contract before editing code: “This build is mounted at /portal/; the illustration follows the imported asset graph; the inspector is optional and loaded after interaction.” Configure and implement only what that sentence requires. Build, inspect, preview, and fill the evidence record.

Run Mutation A. Before execution, write the failure you expect to observe. Then capture the actual result, diagnose the violated dimension, repair it, and rerun the same check. The repair is not complete until post-repair evidence is recorded.

Independent transfer

Create a changed context: the mount becomes /course/, the imported illustration changes, and a different optional module becomes the lazy path. Do not copy /portal/, a previous emitted filename, or the old module name into the evidence. Re-derive the base and source/output mapping. Complete the transfer row with no decisive hint.

Transfer is blocking. A learner who can reproduce the guided output but cannot re-derive the contract in the changed context has not yet demonstrated the owned skill.

Verification: what the evidence record must contain

Vite Production Build Evidence Record v1 contains at least these fields:

  • tested Vite/version context and build command;
  • intended public base;
  • source asset reference and observed emitted reference;
  • existence/relationship check for the emitted asset;
  • lazy module source boundary and observed loading result;
  • generated HTML/resource references relevant to the base;
  • mutation prediction;
  • mutation observation;
  • diagnosis tied to one critical dimension;
  • repair and post-repair evidence;
  • changed-context transfer result;
  • one residual limitation.

Keep observation and interpretation separate. Browser/DOM/network observations may only be claimed if actually observed in that environment. This Macro-A package does not claim a real browser run on behalf of the learner. Its hidden oracle validates the fixture contract and detects deliberate source mutations; learner runtime evidence is produced when the lab is executed in a Vite-capable course runtime.

A passing build plus a structurally plausible dist is still not enough when the lazy interaction was never exercised. Conversely, one browser screenshot is not enough when you never inspected which generated URLs made the nested-base load succeed. The artifact requires both output-graph and runtime-facing evidence.

Assessment and completion

WEB-014-S1 — retrieval. State the four owned dimensions and one non-goal. A correct answer separates build success from deployed correctness.

WEB-014-S2 — application (blocking). Implement the /portal/ fixture and complete the evidence record with traceable source/output observations.

WEB-014-S3 — diagnosis (blocking). Run a deliberate base-path or asset-reference mutation, predict the observable failure, diagnose it, repair it, and record post-repair evidence.

WEB-014-S4 — decision. Defend imported asset vs public asset or lazy vs eager loading for one bounded requirement. Avoid universal claims.

WEB-014-S5 — transfer (blocking). Rebuild the method for a changed base and changed optional module without copying exact filenames or previous paths.

Completion requires contract correctness, evidence quality, scope discipline, and independent transfer. The artifact can still contain a limitation—for example, “verified with local Vite preview, not the target CDN/proxy configuration.” That limitation increases evidence quality because it prevents an unsupported claim.

Sources and freshness boundary

This lesson keeps the frozen source IDs defined by its Bible and refreshes their current status on 2026-09-12:

  • S-VITE-BUILD — official Vite production-build guidance: production build, public base path, transformed references, and preview boundary.
  • S-VITE-ASSETS — official Vite static-asset guidance: imported asset URLs and the distinct public directory contract.
  • S-VITE-OPTIONS — official Vite build options: current production controls and output-related configuration.

At this review point Vite's supported-release page identifies vite@8.3 as the current minor receiving regular patches, and the npm package reports 8.3.0 as current. The lesson does not convert that moving version number into a permanent semantic guarantee. Source facts remain bounded to the official guidance and are scheduled for re-review no later than 2026-10-17. [S-VITE-BUILD] [S-VITE-ASSETS] [S-VITE-OPTIONS]

The MAKNOW evidence record itself is an instructional-design requirement, not a Vite fact. The lesson does not claim that build success proves production correctness, that every dynamic import maps to one stable chunk, that hashed names prove cache correctness, or that code splitting always improves performance.

Assessment

There is no answer key in this lesson pack; answer it yourself and mark honestly. "Needs review" sends the lesson to your review queue.

WEB-014-S1 retrieval

State the four owned dimensions and one explicit non-goal. Explain why a successful build is not sufficient production evidence.

lesson ✗AI ✗notes ✗

WEB-014-S2 application

Implement the /portal/ fixture, build it, inspect generated references, exercise the lazy feature, and complete the evidence record.

lesson ✗AI ✗notes ✗

WEB-014-S3 diagnosis

Predict and run one base/asset mutation, diagnose the violated dimension, repair it, and record post-repair evidence.

lesson ✗AI ✗notes ✗

WEB-014-S4 decision

Defend imported vs public asset handling or lazy vs eager loading for one bounded requirement.

lesson ✗AI ✗notes ✗

WEB-014-S5 transfer

Transfer the method to /course/ with a changed asset and changed optional module without copying emitted filenames.

lesson ✗AI ✗notes ✗
Assessment rubric

Critical dimensions:

  1. Build-context correctness — dev-server behavior is not treated as production-build proof; the built output is inspected and exercised.
  2. Asset/base correctness — asset references and the configured deployment base path are traced through the build rather than assumed from source paths.
  3. Split-boundary evidence — lazy/dynamic loading is verified as observable behavior without treating an exact emitted chunk filename as a correctness contract.
  4. Evidence quality — implementation, runtime observation, mutation, diagnosis, repair, transfer and residual limitation remain traceable and observation is separated from inference.
  5. Transfer independence — the /course/ changed-context transfer is re-derived with changed asset/module inputs rather than copied from the guided /portal/ case.

Blocking assessments: S2, S3, S5. S1 and S4 are non-blocking but still require contract/scope discipline. Browser/DOM evidence must come from learner observation; machine checks must not fabricate it.

References & freshness

·
production build, public base path, transformed asset references, preview boundary
·
imported static assets and public-directory distinction
·
current build-related output/configuration controls
·
current supported branch vite@8.3; freshness context only
·
current package version observed as 8.3.0; freshness context only

Case studies

SIMPLE

REALISTIC

EDGE_OR_FAILURE

TRANSFER_NEAR

Lab files

Goal: make the fixture correct for a /portal/ production mount, trace the imported image through the Vite build, preserve an intentional lazy module boundary, and record evidence without depending on an exact chunk filename.

  1. State the contract before editing.
  2. Configure Vite for the intended base.
  3. Import the source asset instead of relying on the broken root URL.
  4. Load inspector.js with dynamic import() only when the button is used.
  5. In a course runtime that already provides the pinned Vite toolchain, run the build and preview commands.
  6. Inspect generated HTML/assets and exercise the lazy path.
  7. Complete the evidence record.
  8. Run one mutation and repair it.
  9. Complete the /course/ transfer fixture independently.

run_checks.py validates public structure and evidence shape. It does not fabricate browser observations. If the local Vite dependency is unavailable, build execution is reported as an environment dependency rather than faked.

Evidence rule: runtime_observation.claimed stays false until you actually serve/preview and exercise the built output. When claimed, record the environment, tested URL, initial-path observation, and lazy-path observation. Keep interpretation.what_observations_support separate from interpretation.what_they_do_not_prove. The /course/ transfer changes the optional module to course-helper.js; do not reuse the guided inspector.js or any emitted chunk filename.

Lesson media & slides

MAKNOW_WEB-014_DECISION_EVIDENCE_TABLE_v1.0.0.svg
DECISION EVIDENCE TABLE v1.0.0.svg
MAKNOW_WEB-014_FAILURE_REPAIR_v1.0.0.svg
FAILURE REPAIR v1.0.0.svg
MAKNOW_WEB-014_PRIMARY_MODEL_v1.0.0.svg
PRIMARY MODEL v1.0.0.svg
MAKNOW_WEB-014_WORKED_EXAMPLE_v1.0.0.svg
WORKED EXAMPLE v1.0.0.svg
course-badge.svg
course-badge.svg
status-badge.svg
status-badge.svg

Teaching files (download)

XLSXVite Production Build Evidence Record v1 v1.0.0.xlsxTeaching files (download)
DOCXVite Production Build Evidence Record v1 WORKSHEET v1.0.0.docxTeaching files (download)
PDFVite Production Build Evidence Record v1 WORKSHEET v1.0.0.pdfTeaching files (download)