التوجيه والتنقل

التوجيه في Carats بيانات، لا نظام ملفات. كل مسار مصرَّح عنه في كائن أوجه واحد يشترك فيه العميل والخادم.

المسارات

تنتقل خريطة المسارات من نمط مسار إلى مكوّن صفحة. ويُطابَق النمط بدقة، باستثناء المقاطع التي تبدأ بنقطتين، فهي تلتقط معاملًا.

// src/client/facets.cara.ts
import { defineFacets } from '@carats/render';
import ErrorPage from './pages/_error';
import NotFound from './pages/_not_found';
import Home from './pages/home';
import Market from './pages/market';
import Profile from './pages/profile';
import Trade from './pages/trade';

export default defineFacets({
    routes: {
        '/': Home,
        '/profile': Profile,
        '/market': Market,
        '/trade/:symbol': Trade
    },
    suspense: {
        error: ErrorPage,
        notFound: NotFound
    },
    inAppRouting: true
});

اسم مجلد الصفحة عُرف وليس شرطًا. فالمجلد يُبقي الملفات المترابطة معًا، لكن المسار الذي يخدمه المجلد هو أي سلسلة يُربط بها، لذا يمكن لـ `/market` أن يعيش في مجلد اسمه `browse` إن كان ذلك يقرأ أفضل.

المعاملات الديناميكية

يصبح المقطع ‎:name‎ خاصًا على مكوّن الصفحة، وهو متاح أيضًا كـ params.name داخل دالة خادمية للمسار نفسه. ويجب أن يتطابق الاسم في الجهتين.

// src/client/pages/trade/index.tsx
import { Burnish } from '@carats/render';
import Layout from '../_layout';
import './trade.sass';

export default Burnish<TradeData>(function (data) {
    this.head = <title>Trade {data.symbol}</title>;
    return (
        <Layout>
            <h1>{data.symbol}</h1>
        </Layout>
    );
});

‏`/trade/btc` يُصيّر هذه الصفحة مع ‏`data.symbol`‎ المحلولة من الدالة الخادمية المسجَّلة للمسار نفسه، وتُنتج الصفحة عنوان `` مختلفًا لكل رمز. ويقوم الإطار بعملية المطابقة في `getPageComponent` التي تُعيد المكوّن و`params` المستخرجة والمسار `route` المطابَق.</p><blockquote><p>تُطابَق أنماط المسارات بواسطة ‎@carats/url‎ مع matchRoute. لا توجد علامة نجمة، ولا مقطع اختياري، ولا التقاط شامل: مسار غير مطابَق يُصيّر مكوّن تعليق notFound بدلًا من ذلك.</p></blockquote><h2>التوجيه داخل التطبيق</h2><p>مع تفعيل inAppRouting، يتدخّل العميل في نقرات الروابط الداخلية ويبدّل الصفحة دون تحميل كامل. ويتطلب ذلك أيضًا clientRender() في نقطة دخول العميل. أوقفه عندما تعتمد صفحة على تحميل مستند كامل، مثل صفحة يجب أن تُشغّل سكربت طرف ثالث في كل تنقل.</p><pre><code class="hljs language-typescript"><span class="hljs-comment">// src/client/entrypoint.ts</span><span class="hljs-keyword">import</span> { mount, clientRender } <span class="hljs-keyword">from</span> <span class="hljs-string">'@carats/csr'</span>;<span class="hljs-keyword">import</span> facets <span class="hljs-keyword">from</span> <span class="hljs-string">'./facets.cara'</span>;<span class="hljs-title function_">mount</span>(facets);<span class="hljs-title function_">clientRender</span>();</code></pre><p>كلا السطرين مطلوب عندما يكون التوجيه داخل التطبيق مُفعَّلًا. ‏`mount` يسجّل الأوجه في المتصفح، و`clientRender` يبدأ التدخل في التنقل.</p><p>لا تحتاج الروابط إلى مكوّن من الإطار. يُتدخَّل في الوسم المرساة العادي تلقائيًا، ويُتبع الرابط الطبيعي بشكل طبيعي إن عُطِّل التدخل، ما يعني أن تبقى الصفحات قابلة للزحف وتعمل دون JavaScript.</p><h2>التنقل البرمجي</h2><p>ينتقل ‏goTo‎ من ‎@carats/csr‎ بالطريقة نفسها التي ينتقل بها النقر على رابط مُتدخَّل فيه. وهو الطريق الصحيح لإرسال مربع بحث أو أي نموذج آخر لا ينبغي أن يُعيد تحميل المستند.</p><pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { goTo } <span class="hljs-keyword">from</span> <span class="hljs-string">'@carats/csr'</span>;<span class="hljs-keyword">import</span> { afterMount } <span class="hljs-keyword">from</span> <span class="hljs-string">'@carats/hooks'</span>;<span class="hljs-keyword">import</span> <span class="hljs-string">'./search-bar.sass'</span>;<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">SearchInput</span>(<span class="hljs-params"></span>) { <span class="hljs-title function_">afterMount</span>(<span class="hljs-function">() =></span> { <span class="hljs-keyword">const</span> form=<span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'search-bar'</span>); <span class="hljs-keyword">const</span> <span class="hljs-title function_">submit</span>=(<span class="hljs-params"><span class="hljs-attr">e</span>: <span class="hljs-title class_">Event</span></span>) => { e.<span class="hljs-title function_">preventDefault</span>(); <span class="hljs-keyword">const</span> input=<span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'search-input'</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>; <span class="hljs-title function_">goTo</span>(<span class="hljs-string">`/search?q=<span class="hljs-subst">${input.value}</span>`</span>); }; form?.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'submit'</span>, submit); <span class="hljs-keyword">return</span> <span class="hljs-function">() =></span> form?.<span class="hljs-title function_">removeEventListener</span>(<span class="hljs-string">'submit'</span>, submit); }); <span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"search-bar"</span>></span> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"search-input"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"q"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> /></span> <span class="hljs-tag"></<span class="hljs-name">form</span>></span></span> );}</code></pre><p>ابنِ سلسلة الاستعلام باستخدام ‏`URLSearchParams`‎ القياسية التي تتولى الترميز نيابةً عنك. ولقراءة معاملات المسار الحالي من مكوّن، اسأل دالةً خادمية عنها: تتلقى الدالة الخادمية المعاملات المطابَقة في ‏`req.params`‎، وتستقبل الصفحة ما أعادته الدالة الخادمية.</p><h2>حالات تعليق التصيير</h2><p>توفّر خريطة suspense المكونات المستخدمة عندما لا يستطيع مسار إنتاج صفحة: notFound لمسار غير مطابَق، وerror لمكوّن أو دالة خادمية ألقت خطأ. كلاهما اختياري، وerror يتلقى Error المُلقى كوسيطه الوحيد.</p><pre><code class="hljs language-tsx"><span class="hljs-comment">// src/client/pages/_not_found/index.tsx</span><span class="hljs-keyword">import</span> <span class="hljs-title class_">Layout</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'../_layout'</span>;<span class="hljs-keyword">import</span> <span class="hljs-string">'./notfound.sass'</span>;<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">NotFound</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Layout</span>></span> <span class="hljs-tag"><<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"notfound-page container"</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"notfound-code"</span>></span>404<span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>Page not found<span class="hljs-tag"></<span class="hljs-name">h1</span>></span> <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>></span>Return to Dashboard<span class="hljs-tag"></<span class="hljs-name">a</span>></span> <span class="hljs-tag"></<span class="hljs-name">section</span>></span> <span class="hljs-tag"></<span class="hljs-name">Layout</span>></span></span> );}</code></pre><pre><code class="hljs language-tsx"><span class="hljs-comment">// src/client/pages/_error/index.tsx</span><span class="hljs-keyword">import</span> <span class="hljs-title class_">Layout</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'../_layout'</span>;<span class="hljs-keyword">import</span> <span class="hljs-string">'./error.sass'</span>;<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ErrorPage</span>(<span class="hljs-params"><span class="hljs-attr">error</span>: <span class="hljs-title class_">Error</span></span>) { <span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Layout</span>></span> <span class="hljs-tag"><<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error-page container"</span>></span> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>Something went wrong<span class="hljs-tag"></<span class="hljs-name">h1</span>></span> <span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error-message"</span>></span>{error.message}<span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>></span>Return to Dashboard<span class="hljs-tag"></<span class="hljs-name">a</span>></span> <span class="hljs-tag"></<span class="hljs-name">section</span>></span> <span class="hljs-tag"></<span class="hljs-name">Layout</span>></span></span> );}</code></pre><p>غلِّف الاثنين في التخطيط المشترك. فالصفحة الفاشلة التي تُصيَّر دون هيكل الموقع أصعب بكثير في التشخيص من واحدة تحتفظ بالترويسة والتنقل.</p></div> </article> </main> <footer> <span class="footer-mark">◆ carats · التوثيق</span></footer> </div> <script src="/static/js/ui.js"></script> <script type="module" src="/static/js/search.js" defer></script> </body></html>