وب مدرن، رابط کاربری و تجربهی وب · فصل ۳ — Type System، Runtime، Async و Build · 44
Vite Build, Assets و Code Splitting
میتواند «Vite Build, Assets و Code Splitting» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
خلاصهٔ درس
میتواند «Vite Build, Assets و Code Splitting» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
خروجی این درس
میتواند «Vite Build, Assets و Code Splitting» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
موفقیت در dev، مدرک production نیست
یک feature کوچک را در Vite dev server روی مسیر / اجرا میکنید؛ همهچیز درست است. یک تصویر از داخل source وارد شده و یک پنل کماستفاده فقط هنگام کلیک کاربر باید بارگذاری شود. حالا build را زیر مسیر /portal/ منتشر میکنید. صفحه باز میشود اما تصویر نمیآید و پنل lazy در اولین استفاده شکست میخورد. مشکل لزوماً «خراب شدن ناگهانی JavaScript» نیست؛ deployment context فرضهایی را آشکار کرده که dev server پنهان نگه داشته بود.
مالکیت این درس همین مرز است. خروجی تکمیل شما Vite Production Build Evidence Record v1 است. این artifact باید intent در source را به output واقعی وصل کند: public base تنظیمشده، assetهای source-referenced، URLهای تولیدشده در dist، مرز lazy module، یک failure mutation عمدی، build تعمیرشده، transfer در یک بافت تغییرکرده و یک residual limitation. سبز شدن vite build فقط نشان میدهد command build کامل شده است؛ بهتنهایی ثابت نمیکند برنامه ساختهشده در public URL موردنظر درست کار میکند. [S-VITE-BUILD]
سناریو محدود است: برنامه باید زیر /portal/ سرو شود، یک تصویر از source import میشود و یک module اختیاری با import() پویا بار میشود. شما output graph را بازرسی میکنید و built app را تحت همان base بررسی میکنید. هیچ exact chunk filename، تعداد مشخص chunk یا hash داخل نام فایل را به قرارداد correctness تبدیل نمیکنید.
محدوده، مسیر یادگیری و سطح مدرک
WEB-001 پیشنیاز مفهومی سخت است. در صورت نیاز از evidence محدود آن استفاده کنید و foundation کامل HTML/browser را دوباره تدریس نکنید. WEB-003 پیشنیاز نرم است چون dynamic import یک Promise برمیگرداند، اما سیاست runtime concurrency همچنان متعلق به WEB-003 است. WEB-014 مالک production-build context، تبدیل URL assetها، base-path behavior و intentional split boundary است. CDN policy، release infrastructure، cache policy بلندمدت و deployment orchestration در این درس پنهان نمیشوند و به درسهای مجاور واگذار میشوند.
مسیر Foundation: Source-to-Production Output Matrix را جلوی چشم نگه دارید و fixture راهنما را یکبار کامل اجرا کنید. مسیر Repair: فقط یک dimension خراب—build_context، asset_graph، base_path یا split_boundary—را جدا کنید و کوچکترین probe را اجرا کنید. مسیر Experienced: مرور syntax را کوتاه کنید، اما inspection خروجی، اجرای lazy path، mutation و transfer حذف نمیشود.
artifact باید observation را از inference جدا کند. «در dist/index.html مسیر /portal/assets/... مشاهده شد» observation است. «پس هر تنظیم host درست است» inference بدون پشتوانه است. «module اختیاری پس از interaction در preview تستشده load شد» observation است. «Vite همیشه دقیقاً یک chunk با همین نام میسازد» overclaim است.
مدل ذهنی: source graph → build graph → emitted graph → served base → runtime request
این زنجیره را ثابت نگه دارید:
source imports/assets + تنظیم base → Vite build graph → HTML/CSS/JS/assets تولیدشده → سرو شدن در base موردنظر → درخواستهای initial و lazy مرورگر → evidence.
فایلهایی که از source import میشوند وارد build graph میشوند. در production، reference آنها میتواند به URL خروجی تبدیل شود و نام/location فایل تولیدشده با source یکسان نباشد. Vite برای assetهای importشده، url() در CSS و referenceهای asset در HTML رفتار build/base مرتبط تعریف میکند. [S-VITE-BUILD] [S-VITE-ASSETS]
گزینه base یک سؤال public URL را پاسخ میدهد: built application انتظار دارد resourceهای تولیدشده از چه pathای در دسترس باشند؟ در این درس قرارداد صریح است: /portal/. این مقدار تصمیم universal نیست؛ فقط deployment contract همین fixture است. [S-VITE-BUILD]
import() پویا یک loading boundary میسازد و امکان code splitting میدهد، اما قرارداد correctness این نیست که «هر dynamic import دقیقاً برابر یک فایل chunk ثابت است». optimization، shared dependency و نسخه bundler میتواند topology خروجی را تغییر دهد. چیزی که باید verify شود رابطه است: feature اختیاری برای initial path لازم نیست، interaction میتواند آن را load کند، built app آن را از base درست resolve میکند و evidence به filename جادویی وابسته نیست.
پایه: چهار قرارداد مستقل
۱. قرارداد build context
Development و production دو context متفاوتاند. dev server module و asset را برای توسعه resolve میکند؛ vite build bundle تولیدی را میسازد. پس حرکت صحیح این نیست که «build را اجرا کن و اعتماد کن»؛ باید build را اجرا کنید، output را inspect کنید و خود output را exercise کنید. Vite vite preview را برای preview محلی built directory معرفی میکند و آن را production server نمیداند. این برای fixture محدود این درس مفید است، نه نسخه معماری hosting. [S-VITE-BUILD]
۲. قرارداد asset graph
وقتی asset بخشی از رفتار source است، import شدن معمولاً مناسب است:
import badgeUrl from './assets/status-badge.svg'
document.querySelector('#status-badge').src = badgeUrl
در development URL میتواند source path را نشان دهد؛ در production Vite میتواند URL تبدیلشده و asset hashشده تولید کند. قرارداد learner این است که نتیجه را inspect کند، نه اینکه hash را پیشبینی کند. assetهای public قرارداد دیگری دارند: نام خود را نگه میدارند و به root خروجی copy میشوند. راهنمای asset در Vite بهطور کلی import را ترجیح میدهد مگر زمانی که guaranteeهای خاص public واقعاً لازم باشند. [S-VITE-ASSETS]
۳. قرارداد base path
اگر برنامه زیر /portal/ منتشر میشود، build باید برای /portal/ پیکربندی شود. Vite میگوید JS-imported assets، url() در CSS و asset referenceهای HTML هنگام build متناسب با base تنظیم میشوند. یک رشته source مثل '/assets/status-badge.svg' الزاماً همان asset واردشده به build graph نیست. در نتیجه root URL سختکدشده میتواند در dev خوب به نظر برسد و در nested mount قرارداد را بشکند. [S-VITE-BUILD]
۴. قرارداد split boundary
module را به این دلیل lazy کنید که اختیاری است، نه چون «chunk بیشتر همیشه سریعتر است»:
button.addEventListener('click', async () => {
const { mountInspector } = await import('./inspector.js')
mountInspector(document.querySelector('#inspector-host'))
})
evidence باید نشان دهد optional path در built app قابل resolve است و فقط وقتی feature نیاز دارد load میشود. تعداد chunk یک observation است، نه requirement.
توضیح عمیق: shortcutهای ظاهراً منطقی چرا میشکنند؟
اولین shortcut این است که فقط npm run dev را تست کنیم. این مسیر دقیقاً همان نکتهای را مخفی میکند که نیاز داریم: production URLها چگونه render میشوند. اگر app همیشه روی / باز شود، pathی مثل /logo.svg بیخطر به نظر میرسد. وقتی built app زیر /portal/ mount شود، همان رشته هنوز به root سایت اشاره میکند. در مقابل، source import یک edge در build graph میدهد و base context public path را برای build مشخص میکند. این درس ادعا نمیکند import هر deployment problem را حل میکند؛ ادعا میکند این transformation محدود را قابل بازرسی میکند.
shortcut دوم این است که در dist/assets نام hashدار ببینیم و نتیجه بگیریم caching درست است. hash فقط بخشی از نام خروجی انتخابشده توسط build است. cache header، invalidation policy، CDN behavior و atomic release concerns جدا هستند. نام تولیدشده را ثبت کنید اما claim را محدود نگه دارید: «HTML/JS به emitted asset URL اشاره میکند و target در همین build وجود دارد.»
code splitting هم shortcut مشابه دارد. learner فایل inspector-XYZ.js را میبیند و test را به همین exact name قفل میکند. این test brittle است چون filename semantic boundary نیست. probe قویتر initial resources را از output کشف میکند، graph را ثبت میکند و بعد verify میکند feature اختیاری بعد از action موردنیاز load میشود. اگر نسخه bundler naming را تغییر دهد یا shared pieceها را متفاوت بچیند ولی رفتار درست بماند، contract نباید بیدلیل fail شود.
نسخههای فعلی Vite از toolchain یکپارچه Rolldown برای production build استفاده میکنند. چون این implementation detail در نسخههای بعدی قابل تغییر است، درس claimهای factual را به source review فعلی محدود میکند و الگوریتم داخلی chunking را curriculum ثابت تلقی نمیکند. مهارت پایدار، خواندن رابطه source/output و تست رفتار در deployment base است. [S-VITE-OPTIONS]
مثال کامل: /portal/، یک تصویر importشده و یک inspector lazy
fixture شامل src/main.js، src/assets/status-badge.svg و src/inspector.js است. deployment base موردنظر /portal/ است. starter skill را از قبل حل نمیکند؛ فقط محل image، button مربوط به optional inspector و evidence template را فراهم میکند.
پیادهسازی صحیح سه رابطه observable دارد.
اول، image از source reference میشود. بعد از build، HTML/JS/CSS تولیدشده و فایلهای output را inspect کنید. source locator، emitted locator واقعی و وجود target را ثبت کنید. hash فرضی را داخل test ننویسید.
دوم، base برابر /portal/ است. URLهای script/style/resource را در generated HTML ببینید. سپس built output را به شکلی serve/preview کنید که base موردنظر حفظ شود. URL تستشده را در artifact ثبت کنید. buildای که فقط با برگرداندن app به / کار میکند این سناریو را پاس نکرده است.
سوم، inspector lazy است. قبل از کلیک initial state را ثبت کنید. بعد از کلیک ثبت کنید inspector mount شده و resource path تولیدی resolve شده است. exact emitted filename داده evidence است، نه specification.
جدول WEB-014-TAB1 را با dimension، source intent، predicted build relation، observed output، runtime observation، mutation result، repair result و residual limitation پر کنید. عبارت «ظاهرش خوب بود» evidence نیست.
Failure probeها: وقتی قرارداد میشکند نتیجه شما هم باید عوض شود
Mutation A — base اشتباه و root asset سختکدشده
قبل از اجرا prediction بنویسید. build base را از /portal/ دور کنید یا برای assetی که باید با build graph حرکت کند path شروعشونده از / نگه دارید. دوباره build کنید، emitted referenceها را inspect کنید و تحت nested path موردنظر تست کنید. نتیجه درست «root-relative همیشه بد است» نیست؛ نتیجه این است که این URL با deployment/base contract همین fixture سازگار نیست.
Mutation B — eager import به جای مرز اختیاری
Dynamic import را با static top-level import جایگزین کنید. دوباره build کنید و رابطه output/loading را مقایسه کنید. feature ممکن است هنوز کار کند، اما intentional optional-loading contract تغییر کرده است. contract change را بدون اصرار بر exact chunk count تشخیص دهید.
Mutation C — imported asset به public asset تبدیل شود
illustration را به public منتقل کنید و بر اساس قرارداد public directory reference دهید. تغییرات را مشاهده کنید: حفظ filename، تفاوت source-graph relationship و output location. این design میتواند معتبر باشد اگر guaranteeهای public لازم باشد. هدف mutation این است که مجبور شوید قرارداد موردنظر را بیان کنید، نه اینکه rule سطحی «import خوب، public بد» حفظ کنید. [S-VITE-ASSETS]
روش evidence قوی هر mutation را به دلیل درست تشخیص میدهد؛ نباید فقط با تغییر hash بیدلیل fail شود و نباید فقط به خاطر exit code صفر vite build pass بدهد.
مسیر Experienced: معماری را verify کنید، نه trivia را
برای learner باتجربه خطر اصلی overfit کردن به internals build است. مرور syntax را حذف کنید و مستقیم این سؤالها را جواب دهید:
- کدام source reference باید توسط build graph transform شود و کدام عمداً public URL باقی میماند؟
- public base قرارداد release چیست؟
- کدام feature path عمداً lazy است و چه eventای از آن boundary عبور میکند؟
- چه observationهایی ثابت میکند built app initial و lazy resourceها را روی همان base resolve میکند؟
- کدام بخش output صرفاً descriptive است و نباید به exact filename قفل شود؟
بعد یک mutation اجرا کنید. اگر evidence نتواند mutated build را از intended build جدا کند، ضعیف است. اگر فقط به خاطر filename متفاوت یک build سالم را رد کند، brittle است.
گزینهها و trade-offها
Imported asset در برابر public. imported asset وارد source analysis/build transformation میشود. public asset همانطور copy میشود و از public root reference میشود. انتخاب باید تابع semantics موردنیاز باشد. صرفاً برای فرار از فهم transformed URL فایل را به public نبرید. [S-VITE-ASSETS]
Absolute nested base در برابر relative base. وقتی mount معلوم است، /portal/ یک قرارداد روشن میدهد. Vite حالت relative base را هم برای deploymentهایی که base نهایی از قبل معلوم نیست پشتیبانی میکند. این درس /portal/ را ثابت میکند تا verification هدف دقیق داشته باشد؛ این مقدار universal نیست. [S-VITE-BUILD]
Lazy در برابر eager. lazy loading میتواند initial work feature اختیاری را کم کند، اما runtime request path و failure boundary جدید ایجاد میکند. اگر module همیشه لازم است eager ممکن است سادهتر باشد. «splitting بیشتر» هدف نیست؛ رفتار loading آگاهانه هدف است.
Preview در برابر hosting واقعی. preview محلی برای تست built artifact مفید است، اما مدرک هر reverse proxy، CDN، header، service worker یا cache configuration نیست. این residual boundary را صریح ثبت کنید. [S-VITE-BUILD]
تمرین: repair راهنما، سپس transfer مستقل
اجرای راهنما
starter fixture را باز کنید. قبل از edit یک جمله contract بنویسید: «این build زیر /portal/ mount میشود؛ illustration از imported asset graph پیروی میکند؛ inspector اختیاری است و بعد از interaction load میشود.» فقط همان چیزهایی را تغییر دهید که این جمله لازم دارد. build، inspect و preview را اجرا کنید و evidence record را پر کنید.
Mutation A را اجرا کنید. قبل از run، failure قابل مشاهده را پیشبینی کنید. نتیجه واقعی را capture کنید، dimension نقضشده را diagnose کنید، repair را انجام دهید و همان check را دوباره اجرا کنید. تا post-repair evidence ثبت نشده repair بسته نیست.
Transfer مستقل
context جدید بسازید: mount برابر /course/، illustration متفاوت و optional module دیگری lazy است. /portal/، emitted filename قبلی یا module name قبلی را copy نکنید. base و source/output mapping را دوباره derive کنید. transfer row را بدون decisive hint کامل کنید.
Transfer blocking است. بازتولید guided output بدون توانایی re-derive کردن contract در context جدید برای اثبات skill کافی نیست.
Verification: evidence record دقیقاً چه میخواهد؟
Vite Production Build Evidence Record v1 حداقل این موارد را ثبت میکند:
- Vite/version context و build command تستشده؛
- intended public base؛
- source asset reference و emitted reference مشاهدهشده؛
- بررسی وجود/رابطه emitted asset؛
- source boundary برای lazy module و نتیجه loading مشاهدهشده؛
- resource URLهای مهم generated HTML نسبت به base؛
- mutation prediction؛
- mutation observation؛
- diagnosis وابسته به یک critical dimension؛
- repair و post-repair evidence؛
- changed-context transfer؛
- یک residual limitation.
Observation و interpretation را جدا نگه دارید. browser/DOM/network evidence فقط زمانی claim میشود که واقعاً در همان محیط مشاهده شده باشد. این Macro-A بهجای learner یک browser run ساختگی ادعا نمیکند. hidden oracle فقط contract fixture و detection mutationهای source را validate میکند؛ runtime evidence learner در course runtime مجهز به Vite تولید میشود.
build موفق و dist ظاهراً منطقی هنوز کافی نیست اگر lazy interaction هرگز اجرا نشده باشد. در جهت مقابل، یک screenshot مرورگر هم کافی نیست اگر ندانید چه URL تولیدشدهای nested-base load را ممکن کرده است. artifact هر دو نوع evidence، output-graph و runtime-facing، را لازم دارد.
Assessment و تکمیل
WEB-014-S1 — retrieval. چهار dimension اصلی و یک non-goal را بیان کنید. پاسخ صحیح build success را از deployed correctness جدا میکند.
WEB-014-S2 — application (blocking). fixture /portal/ را پیاده کنید و evidence record را با observation قابل trace تکمیل کنید.
WEB-014-S3 — diagnosis (blocking). یک mutation base-path یا asset-reference اجرا کنید، failure را پیشبینی کنید، diagnose و repair کنید و post-repair evidence بدهید.
WEB-014-S4 — decision. بین imported/public asset یا lazy/eager برای یک requirement محدود تصمیم بگیرید و از universal claim پرهیز کنید.
WEB-014-S5 — transfer (blocking). روش را برای base و optional module تغییرکرده بدون copy کردن filename و path قبلی بازسازی کنید.
تکمیل به contract correctness، evidence quality، scope discipline و transfer مستقل نیاز دارد. artifact میتواند limitation داشته باشد؛ مثلاً «با Vite preview محلی verify شده، نه با CDN/proxy هدف». این limitation کیفیت evidence را بالا میبرد چون جلوی unsupported claim را میگیرد.
منابع و مرز freshness
source IDهای فریزشده Bible حفظ میشوند و وضعیت آنها در 2026-09-12 دوباره بررسی شده است:
- S-VITE-BUILD — راهنمای رسمی production build: build، public base path، transformed reference و مرز preview.
- S-VITE-ASSETS — راهنمای رسمی static assets: imported asset URL و قرارداد جداگانه
public. - S-VITE-OPTIONS — گزینههای رسمی build: کنترلهای فعلی production و output configuration.
در این review، صفحه release رسمی Vite شاخه vite@8.3 را current minor دریافتکننده regular patch معرفی میکند و npm نسخه 8.3.0 را current نشان میدهد. این شماره متغیر به semantic guarantee دائمی تبدیل نمیشود. claimها فقط به guidance رسمی محدود هستند و حداکثر تا 2026-10-17 دوباره review میشوند. [S-VITE-BUILD] [S-VITE-ASSETS] [S-VITE-OPTIONS]
خود Vite Production Build Evidence Record v1 requirement طراحی آموزشی MAKNOW است، نه fact مربوط به Vite. این درس ادعا نمیکند build success correctness production را ثابت میکند، هر dynamic import دقیقاً یک chunk ثابت میسازد، hash filename cache correctness را تضمین میکند یا code splitting همیشه performance را بهتر میکند.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
WEB-014-S1 retrieval
چهار dimension مالکیت این درس و یک non-goal را بیان کنید و توضیح دهید چرا build موفق بهتنهایی evidence production نیست.
WEB-014-S2 application
fixture مسیر /portal/ را پیاده کنید، build و output را بررسی کنید، lazy feature را اجرا کنید و evidence record را کامل کنید.
WEB-014-S3 diagnosis
یک mutation مربوط به base/asset را پیشبینی و اجرا کنید، dimension نقضشده را تشخیص دهید، repair کنید و post-repair evidence ثبت کنید.
WEB-014-S4 decision
برای یک requirement محدود، imported در برابر public asset یا lazy در برابر eager را دفاع کنید.
WEB-014-S5 transfer
روش را به /course/ با asset و optional module جدید منتقل کنید و emitted filename قبلی را copy نکنید.
ملاکهای ارزیابی (rubric)
Critical dimensions:
- Build-context correctness — dev-server behavior is not treated as production-build proof; the built output is inspected and exercised.
- Asset/base correctness — asset references and the configured deployment base path are traced through the build rather than assumed from source paths.
- Split-boundary evidence — lazy/dynamic loading is verified as observable behavior without treating an exact emitted chunk filename as a correctness contract.
- Evidence quality — implementation, runtime observation, mutation, diagnosis, repair, transfer and residual limitation remain traceable and observation is separated from inference.
- 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.
منابع و تازگی
مطالعهٔ موارد (case studies)
فایلهای آزمایشگاه
هدف: fixture را برای production mount روی /portal/ درست کنید، تصویر importشده را تا output Vite دنبال کنید، intentional lazy module boundary را حفظ کنید و evidence را بدون وابستگی به exact chunk filename ثبت کنید.
- قبل از edit قرارداد را بنویسید.
- Vite را برای base موردنظر تنظیم کنید.
- بهجای root URL خراب، asset را از source import کنید.
- فقط هنگام استفاده از button،
inspector.jsرا باimport()پویا load کنید. - در course runtime که toolchain پینشده Vite را دارد build و preview را اجرا کنید.
- generated HTML/assets و lazy path را بررسی کنید.
- evidence record را تکمیل کنید.
- یک mutation اجرا و repair کنید.
- fixture transfer روی
/course/را مستقل کامل کنید.
run_checks.py ساختار عمومی و shape evidence را validate میکند و browser evidence ساختگی تولید نمیکند. اگر dependency محلی Vite موجود نباشد، اجرای build بهعنوان environment dependency گزارش میشود، نه اینکه نتیجه جعلی ساخته شود.
قاعده evidence: تا زمانی که built output را واقعاً serve/preview و exercise نکردهاید، runtime_observation.claimed باید false بماند. هنگام claim، environment، tested URL، observation مسیر initial و observation مسیر lazy را ثبت کنید. interpretation.what_observations_support را از interpretation.what_they_do_not_prove جدا نگه دارید. در transfer مسیر /course/ ماژول اختیاری به course-helper.js تغییر میکند؛ از inspector.js اجرای راهنما یا emitted chunk filename کپی نکنید.
رسانه و اسلایدهای درس
فایلهای آموزشی (دانلود)