وب مدرن، رابط کاربری و تجربهی وب · فصل ۶ — Static-first، SEO و Performance · 53
Astro، محتوای Static-first و Islands
syntax یا framework را میشناسد اما contract، runtime behavior، accessibility و failure boundary را یکپارچه نمیکند. موضوع جاری: «Astro, Static-first Content و Islands». میتواند «Astro, Static-first Content و Islands» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
خلاصهٔ درس
syntax یا framework را میشناسد اما contract، runtime behavior، accessibility و failure boundary را یکپارچه نمیکند. موضوع جاری: «Astro, Static-first Content و Islands».
میتواند «Astro, Static-first Content و Islands» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
خروجی این درس
میتواند «Astro, Static-first Content و Islands» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
پیشنیازها
۱. صفحه اول: مرز اجرا همان تصمیم محصول است
یک محصول مستنداتی را تصور کنید که در یک route سه نیاز متفاوت دارد. متن مقاله فقط هنگام انتشار محتوای جدید تغییر میکند. یک فیلتر کد باید داخل مرورگر واکنش تعاملی داشته باشد. یک badge کوچک حساب کاربر نیز شخصیسازیشده است و نباید باعث شود کل مقاله برای هر درخواست دوباره روی سرور ساخته شود. راهحل ضعیف این است که فقط بگوییم «سایت با Astro ساخته شده» و نتیجه بگیریم که تصمیم performance خودبهخود درست است. راهحل قابل دفاع این است که قبل از افزودن directiveها، contract اجرا را بنویسیم.
در این درس دامنه محدود است: محتوای پایدار در پوسته static/server-rendered باقی میماند؛ فقط ناحیهای که واقعاً به رفتار سمت مرورگر نیاز دارد hydrate میشود؛ و محتوای volatile یا personalized فقط وقتی به server execution منتقل میشود که نیاز freshness یا personalization آن را توجیه کند و runtime استقرار نیز از آن پشتیبانی کند. سپس بهجای استناد به واژههایی مثل Islands یا Static-first، خروجی تولیدشده و رفتار قابل مشاهده را بررسی میکنیم.
artifact اصلی Astro Rendering & Island Boundary Record v1 است. در این record باید Observation از Inference جدا باشد. نمودار معماری یا یک build موفق بهتنهایی کافی نیست؛ route decision، island decision، evidence از خروجی، حداقل یک mutation، repair، transfer به زمینه جدید و یک residual limitation باید ثبت شود.
۲. جهتگیری مسیر یادگیری: توضیح کمتر مجاز است، evidence کمتر نه
در مسیر Foundation ابتدا هر بخش صفحه را به stable content، browser-interactive content یا volatile/personalized content طبقهبندی کنید. قبل از تغییر directiveها، سطرهای اولیه Evidence Matrix را کامل کنید.
در مسیر Repair اگر Astro را میشناسید ولی build شما JavaScript غیرضروری میفرستد یا مرز prerender/on-demand مبهم است، فقط یک contract شکسته را جدا کنید. مشخص کنید کدام region مشکل دارد، کدام rendering mode یا directive آن را ایجاد کرده و چه artifactی باید repair را ثابت کند.
در مسیر Experienced آموزش syntax حذف میشود، اما mutation و evidence حذف نمیشوند. مستقیم record را بسازید و برای هر ناحیه hydrated یا deferred دلیل قابل آزمون ارائه کنید.
پنج dimension در همه مسیرها ثابتاند: rendering_mode، zero_js_default، client_island، server_island و emitted_evidence. عبارت «Astro ذاتاً سریع است» جای هیچکدام را نمیگیرد.
۳. مدل ذهنی: یک صفحه، چند محل اجرا
صفحه Astro را مجموعهای از regionها ببینید که کارشان میتواند در زمان و محل متفاوت انجام شود.
پوسته build-time/static: اگر محتوای مقاله تا انتشار بعدی معتبر است، میتواند از قبل تولید شود. در حالت static پیشفرض، routeها prerender میشوند مگر اینکه یک route را برای on-demand rendering خارج کنید. این یک تصمیم route-level است، نه ادعای اینکه سایت هرگز server code ندارد.
Client island: یک UI-framework component میتواند HTML تولید کند بدون اینکه به یک application مرورگری تبدیل شود. directiveهای client:* درخواست صریح hydration هستند. client:load، client:idle و client:visible زمان شروع کار مرورگر را تغییر میدهند و بخشی از UX/performance contract هستند.
Server island یا on-demand region: داده personalized یا volatile ممکن است به اجرای سرور نیاز داشته باشد. route on-demand به adapter و server runtime متکی است. server island با server:defer یک ناحیه dynamic server-rendered را از پوسته اصلی جدا میکند و آن هم به runtime/adapter سازگار نیاز دارد.
این مدل یک اشتباه رایج را حذف میکند: client island و server island قابل جایگزینی با هم نیستند. اولی رفتار سمت مرورگر را فعال میکند؛ دومی rendering پویا روی سرور را جدا میکند.
۴. لایه پایه: Prerender و پیشفرض بدون JavaScript کلاینت
از freshness شروع کنید. اگر محتوای route تا build یا انتشار بعدی معتبر است، prerender یک default منطقی است. اگر response به cookie، داده لحظهای سرور یا وضعیت request-specific وابسته است، on-demand rendering ممکن است لازم شود. وجود یک ناحیه dynamic بهتنهایی دلیل نمیشود کل سایت request-time شود.
در Astro وجود framework component نیز بهخودیخود به معنی ارسال runtime سمت client نیست. component بدون client:* میتواند HTML ایجاد کند بدون اینکه کد hydration آن در مرورگر اجرا شود. بنابراین «React component داریم» و «React JavaScript به browser ارسال میشود» دو گزاره متفاوتاند.
در lab، StaticSummary هیچ client directive ندارد. CodeFilter به علت interaction از client:visible استفاده میکند. AccountBadge در reference plan بهعنوان server island مدل میشود. evidence model فقط CodeFilter را بهعنوان client bundle candidate ثبت میکند.
محدودیت مهم: این lab deterministic یک مدل آموزشی و validation محلی است و جای build واقعی Astro را نمیگیرد. وقتی publisher runtime واقعی Astro در دسترس باشد، باید dist/، preview و network behavior واقعی نیز بررسی شوند.
۵. توضیح عمیق: Client Island، Server Island و Route Mode
یک client island باید دلیلی داشته باشد که بدون نام framework هم قابل بیان باشد: «این region بعد از initial HTML به interaction در browser نیاز دارد». اگر event handling، client state یا API مخصوص browser ندارید، hydration ممکن است هزینه بدون payoff باشد.
اولویت بارگذاری نیز مهم است. client:load hydration را زود شروع میکند و برای interaction فوری مناسب است. client:idle شروع را به زمان idle موکول میکند. client:visible منتظر میماند تا component به viewport برسد. هیچکدام ذاتاً «بهتر» نیستند؛ نیاز تجربه کاربر تصمیم را تعیین میکند.
On-demand rendering مسئله دیگری را حل میکند. وقتی route از prerender خارج میشود و adapter وجود دارد، میتواند برای هر request اجرا شود. اگر فقط بخش کوچکی volatile است، server island اجازه میدهد پوسته عمدتاً static بماند و همان بخش dynamic جداگانه render شود.
server island constraintهای خودش را دارد: adapter/runtime لازم است؛ props از serialization boundary عبور میکنند؛ fallback باید معنیدار باشد؛ و accessibility در حالت loading نباید شکسته شود. این واقعیتها به معنی «برای personalization همیشه server island بزن» نیست.
در نهایت، performance claim باید bounded بماند. تعداد کمتر client bundle قابل مشاهده است. directive مشخص قابل بررسی است. اما Core Web Vitals خوب یا SEO بهتر بدون measurement مستقل ثابت نمیشود.
۶. مثال کارشده: Route مستندات
در design مرجع، shell مقاله prerendered میماند. StaticSummary بدون client directive render میشود. CodeFilter از client:visible استفاده میکند چون interaction فقط هنگام رسیدن کاربر به widget لازم است. AccountBadge personalized است و با server:defer جدا میشود؛ بنابراین deployment plan باید نیاز به adapter را ثبت کند.
نمونه فشرده:
<StaticSummary items={summary} />
<CodeFilter client:visible items={snippets} />
<AccountBadge server:defer>
<span slot="fallback">Account</span>
</AccountBadge>
اما evidence record نباید فقط همین کد را کپی کند. یک سطر درست میگوید: «CodeFilter — interaction در browser لازم است — client:visible — expected client bundle: yes — در deterministic emitted model مشاهده شد: yes — build واقعی Astro در این acceptance run مشاهده نشد.» همین تفکیک آخر جلوی overclaim را میگیرد.
برای route نیز بنویسید: «freshness مقاله deployment-bound است، پس route prerendered میماند؛ badge personalized جدا شده؛ server island باعث نیاز به adapter میشود.» اگر requirement عوض شود و خود مقاله به entitlement لحظهای وابسته شود، تصمیم route نیز باید دوباره بررسی شود.
۷. Failure Modes: mutation باید evidence را تغییر دهد
Mutation A: به یک component صرفاً presentational، client:load اضافه کنید. مدل خروجی باید client bundle اضافی گزارش کند. اگر نتیجه شما تغییر نکرد، evidence process ضعیف است.
Mutation B: client directive فیلتر تعاملی را حذف کنید. initial markup ممکن است دیده شود، اما interaction contract دیگر برقرار نیست. screenshot اولیه این failure را نمیبیند.
Mutation C: بخش personalized و volatile را کاملاً داخل assumption prerendered قرار دهید. freshness contract میشکند؛ این failure از جنس JavaScript size نیست.
Mutation D: whole-page hydration را فعال کنید. صفحه ممکن است هنوز کار کند، اما bounded-island decision از بین میرود و client runtime surface بزرگ میشود.
Mutation E: server island را نگه دارید ولی adapter support را حذف کنید. قصد معماری در source وجود دارد اما deployment contract ناقص است.
۸. پل حرفهای: decision را optimize کنید، نه slogan را
تیم باتجربه ممکن است syntax directiveها را حفظ باشد، ولی همچنان به decision record نیاز دارد. سؤال خوب این نیست که «Astro میتواند؟» سؤال خوب این است که «کدام region در کجا باید اجرا شود و چه evidenceی این انتخاب را ثابت میکند؟»
هر island جدید را مثل یک diff معماری review کنید. اگر client island اضافه شده، interaction requirement باید مشخص باشد. اگر route به on-demand منتقل شده، request-time dependency باید ثبت شود. اگر client:visible به client:load تغییر کرده، دلیل UX برای hydration زودتر باید وجود داشته باشد.
assetهای client را بودجهای با provenance در نظر بگیرید. هر JavaScript bundle باید به یک interaction مشخص قابل نسبتدادن باشد. این رویکرد به معنی «JavaScript صفر به هر قیمت» نیست؛ به معنی «JavaScript باید دلیل داشته باشد» است.
۹. گزینهها و Trade-offها
route کاملاً prerendered سادهتر cache میشود، اما arbitrary request-time state را بدون مکانیزم dynamic جدا نمیتواند نشان دهد. route کاملاً on-demand دسترسی طبیعی به request state دارد، اما کل route را وابسته به server runtime میکند. server island میتواند برای ناحیه کوچک dynamic حد وسط باشد، ولی request اضافی، fallback و runtime boundary جدید ایجاد میکند.
در client hydration نیز trade-off وجود دارد. client:load کنترل فوری را زودتر آماده میکند اما از منابع early browser استفاده میکند. client:visible ممکن است widget پایین صفحه را برای کاربری که هرگز به آن نمیرسد اصلاً load نکند، ولی interaction قبل از visible شدن و hydration آماده نیست.
SPA embedding نیز برای یک ناحیه واقعاً app-like ابزار معتبری است؛ فقط contract دیگری نسبت به content page عمدتاً static دارد. این درس SPA را ممنوع نمیکند؛ از accidental whole-page hydration جلوگیری میکند.
۱۰. تمرین هدایتشده و مستقل
در fixture learner این دستور را اجرا کنید:
node --test test/public-contract.test.mjs
سپس node src/inspect-boundary.mjs را اجرا کنید تا evidence snapshot محلی دوباره ساخته شود. core checks dependency-free هستند تا reasoning در حالت offline نیز قابل ارزیابی باشد.
ترتیب کار:
- دلیل prerendered بودن documentation shell را بنویسید.
- توضیح دهید چرا
StaticSummaryclient directive ندارد. - directive و priority فیلتر را دفاع کنید.
- مشخص کنید چرا badge یک concern سروری است و adapter compatibility بخشی از contract است.
- mutationها را اجرا و evidence تغییرکرده را ثبت کنید.
- repair کنید و دوباره checks را اجرا کنید.
- transfer مربوط به product catalog را بدون کپی نام componentها حل کنید.
در transfer، product description پایدار است، comparison widget تعاملی است و inventory volatile است. میتوانید برای inventory on-demand route یا server island را انتخاب کنید، اما تصمیم باید از freshness و runtime constraint مشتق شود.
۱۱. Verification و Evidence Record
record شش dimension اجباری دارد: implementation artifact، generated/runtime behavior قابل مشاهده، failure mutation، repair result، changed-context transfer و residual limitation.
در یادداشتها از سه برچسب Observed، Inferred و Not observed استفاده کنید. مثال: «Observed: مدل deterministic یک client bundle candidate برای CodeFilter گزارش کرد. Inferred: build واقعی Astro باید asset مربوط به hydrated framework component ایجاد کند. Not observed: browser network trace و رفتار production adapter.»
وقتی runtime واقعی Astro موجود است، record را با Astro version، adapter version، build command، inspection واقعی dist/، preview و در صورت نیاز browser network evidence توسعه دهید. local deterministic checks را حذف نکنید؛ آنها برای regression isolation مفیدند.
Residual limitation اجباری است. correctness همین fixture، Core Web Vitals، search ranking، CDN behavior یا latency production personalization را ثابت نمیکند.
۱۲. Assessment و Transfer
Retrieval: تفاوت prerendered route، client hydration و server island را بدون استفاده از کلمه «سریع» بهعنوان دلیل توضیح دهید.
Application: route محدود مستندات را پیاده و evidence record را کامل کنید.
Diagnosis: قبل از اجرای یک mutation، اولین failure قابل مشاهده را پیشبینی کنید.
Decision: یک boundary را در برابر alternative قابل قبول دفاع کنید. مثلاً توضیح دهید چرا badge کوچک الزاماً کل مقاله را on-demand نمیکند.
Transfer: در product catalog، توضیحات پایدار را در پوسته static نگه دارید، comparison widget را فقط در صورت نیاز interaction hydrate کنید و برای inventory مکانیزم freshness مناسب انتخاب کنید. پاسخ صحیح یک directive ثابت نیست؛ تصمیم traceable همراه evidence است.
۱۳. منابع و مرز Freshness
factهای تکنیکی این درس به مستندات رسمی Astro محدودند که در 2026-09-13 دوباره بررسی شدند. راهنمای Islands Architecture، HTML/static behavior و hydration صریح با client:* را توضیح میدهد. راهنمای on-demand rendering رفتار prerendered-by-default و نیاز adapter برای request-time rendering را پوشش میدهد. راهنمای server islands نیز server:defer و delayed server rendering با adapter را مستند میکند.
این source review Bible منجمد را گسترش نمیدهد؛ فقط سازگاری claimهای موجود با authority فعلی را تأیید میکند. طبق review window باید حداکثر تا 2026-10-17 یا زودتر در صورت تغییر semantics Astro دوباره بررسی شود.
واژههای معماری را به guarantee تبدیل نکنید. این درس فقط روش bounded decision و evidence محلی را ثابت میکند؛ هر claim تولیدی به observation تولیدی خودش نیاز دارد.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
WEB-015-S1 retrieval
قرارداد route/island در WEB-015 را بیان کنید: چه زمانی route محدود prerender و چه زمانی on-demand است، کدام محتوا بهطور پیشفرض zero-JavaScript میماند و یک non-goal صریح را ذکر کنید.
WEB-015-S2 application
route محدود docs را با ناحیهٔ static article/summary، یک client island تعاملی CodeFilter در مرورگر و یک server island تأخیری AccountBadge پیادهسازی کنید؛ تصمیم route، مرز نواحی، emitted-model evidence و residual limitation را ثبت کنید.
WEB-015-S3 diagnosis
یک mutation عمدی در rendering/island را که route mode، hydration، پشتیبانی server-island/runtime یا سازگاری evidence را مختل میکند پیشبینی، اجرا، Diagnose و Repair کنید.
WEB-015-S4 decision
مرز انتخابشدهٔ static/client/server را در برابر یک گزینهٔ محتمل با تکیه بر freshness، interactivity و محدودیت adapter/runtime دفاع کنید؛ یک residual uncertainty را بیان کنید و بدون اندازهگیری ادعای Core Web Vitals نکنید.
WEB-015-S5 transfer
changed-context مربوط به product catalog را مستقل طراحی کنید: رفتار prerender/on-demand را انتخاب کنید، نواحی static، client-interactive و server-dynamic را مشخص کنید، روش evidence را تعریف کنید و بدون بازاستفاده از شناسههای fixture اصلی یک residual limitation بیان کنید.
ملاکهای ارزیابی (rubric)
Critical dimensions:
- RENDERING_MODE — Choose prerender versus on-demand from freshness/runtime requirements rather than from the Astro label.
- ZERO_JS_AND_CLIENT_ISLAND — Keep unhydrated framework content zero-JavaScript by default and add a client directive only where browser interaction requires it.
- SERVER_ISLAND_RUNTIME_BOUNDARY — Use a server island only for justified dynamic server content and keep adapter/runtime support explicit.
- EMITTED_EVIDENCE_AND_CLAIM_BOUNDARY — Separate emitted/output evidence from inference; internal synthetic evidence is never relabeled as a real Astro build, browser observation, field performance, or SEO proof.
- CHANGED_CONTEXT_TRANSFER — Apply the contract independently to the product-catalog case, including route decision, static/client/server regions, evidence method, and a residual limitation.
Blocking assessments: S2, S3 and S5. S1 and S4 are non-blocking but still require scope discipline.
Forbidden overclaims remain explicit: Astro does not require every route to be static; adding a React component does not automatically ship React JavaScript; more islands do not automatically improve performance; server and client islands are not interchangeable; static-first architecture does not prove Core Web Vitals without measurement.
Publisher-runtime execution remains deferred to the pinned runtime. The internal synthetic oracle is not publisher-runtime observation. Browser/assistive-technology evidence is not claimed.
منابع و تازگی
مطالعهٔ موارد (case studies)
فایلهای آزمایشگاه
این fixture منطق تصمیم rendering/island را بهصورت offline میسنجد. فایلهای source با شکل Astro وجود دارند، اما acceptance عمومی از یک مدل deterministic و بدون dependency استفاده میکند. در این مرحله ادعای build واقعی Astro، preview یا مشاهده browser وجود ندارد.
اجرا:
node --test test/public-contract.test.mjs
node src/inspect-boundary.mjs
سپس evidence record، mutationها، repair و transfer مربوط به product catalog را کامل کنید. وقتی publisher runtime با Astro و adapter پینشده موجود شد، evidence واقعی build/preview را به record اضافه کنید.
رسانه و اسلایدهای درس
فایلهای آموزشی (دانلود)