MSW في SvelteKit

كيفية تنفيذ MSW للتنمية المحلية في SvelteKit

استخدام MSW في SvelteKit

هذا برنامج تعليمي سيوضح لك كيفية تنفيذ مكتبة "Mock Service Worker" ، caleld MSW ، في تطبيق SvelteKit الخاص بك. يرجى ملاحظة أن هذا الدليل لا يوضح كيفية إعداد MSW مع Jest ، بل يوضح كيفية استخدام MSW أثناء التطوير المباشر.

التطبيق الكامل متاح في مستودع spikze.club ، الرابط أيضًا في الملحق في نهاية الصفحة.

لا تقلق إذا كان هذا الدليل قد يبدو ساحقًا في البداية ، فإن التغييرات بسيطة للغاية. يتطلب استخدام MSW في SvelteKit لمحاكاة الطلبات أثناء التطوير المحلي مزيدًا من الجهد.

تثبيت التبعيات

أولاً ، دعنا نثبت جميع التبعيات الضرورية.

npm i -D msw ts-node concurrently

نحن نستخدم "ts-node" أيضًا لبدء خادم mocks. قبل المتابعة ، يرجى الاتصال بالأمر التالي في جذر مشروع SvelteKit الخاص بك.

npx msw init ./static

سيؤدي هذا إلى إنشاء ملف جافا سكريبت باستخدام معالج عامل الخدمة الافتراضي بواسطة MSW. نحن نستخدم "ثابت" لتطبيق SvelteKit الخاص بنا ، لأنه مكافئ للدليل العام. أخيرًا ، دعنا نحدِّث ملف package.json-file بحيث يعرف MSW مكان البحث عن الملف.

{
  "scripts": ...
  ...
  "msw": {
    "workerDirectory": "static"
  }
}

كبرنامج نصي مناسب ، أضفت أيضًا الأمر التالي إلى "البرامج النصية" الخاصة بي - التكوين المطلوب إذا كنت ترغب في بدء تشغيل MSW عبر كل من العميل والخادم (للنسخ من جانب الخادم).

{
  "scripts": {
    "dev": "svelte-kit dev",
    "dev:msw-server": "concurrently \"cd msw && ts-node server.ts\" \"npm run dev\"",
    ...
  },
}

تحضير وحدات MSW

قبل أن نبدأ في كتابة التعليمات البرمجية الفعلية ، نحتاج إلى تحديد دليل العمل الجديد الخاص بنا لـ MSW في تكوين SvelteKit وكذلك Typescript-config. سنبدأ بملف Typescript.

{
  "compilerOptions": {
    ...,
    "paths": {
      "$lib": ["src/lib"],
      "$lib/*": ["src/lib/*"],
      "$msw": ["src/msw"],
      "$msw/*": ["src/msw/*"]
    }
  }
}

بعد ذلك ، لنقم بتحديث ملف SvelteKit-config.

import adapter from "@sveltejs/adapter-auto";
import preprocess from "svelte-preprocess";
import path from "path";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // Consult https://github.com/sveltejs/svelte-preprocess
  // for more information about preprocessors
  preprocess: preprocess(),
  kit: {
    adapter: adapter(),
    vite: {
      ...,
      resolve: {
        alias: {
          $msw: path.resolve("./src/msw"),
          $lib: path.resolve("./src/lib")
        }
      }
    }
  }

إضافة المراكب والمعالجات

حسنًا ، لقد انتهيت من الجزء الأول من الدليل! سنضيف الآن الكود الفعلي الذي يتم تنفيذه عند استخدام MSW. الرجاء ملاحظة أن هذا مثال بسيط ، لذلك أضفت ما هو ضروري لإنجاح كل شيء ، ولكن ليس أكثر.

كما قد تكون خمنت من التغييرات التي أجريناها على SvelteKit-config ، سيتم وضع جميع التعليمات البرمجية في دليل جديد يسمى "msw" ، والذي يوجد مباشرة في "src".

|- app/
|-- src/
|--- pages/
|--- msw/
|---- fixtures/
...

إليك رمز الوحدات التالية التي يجب إضافتها. يجب أن تكون قادرًا على نسخها ولصقها ببساطة ، يتم كتابة اسم الملف + مسار الدليل كتعليق في كل كتلة في الأعلى.

//
// app/src/msw/handlers.server.ts
//

import { rest } from "msw";
import { values } from "./fixtures/msw-demo";

export const handlers = [
  // Here, you can mock absolute URL requests,
  // e.g. to a database. For the current implementation,
  // no data is mocked in this place.
  //
  // Note: This is also the place to mock absolute
  // SSR-imports. Everything in 'handlers.workers.ts'
  // is mocked client-side.
];
//
// app/src/msw/handlers.worker.ts
//

import { rest } from "msw";
import { values } from "./fixtures/msw-demo";

// Mock relative URLs that map to your
// routes' data endpoints. This mock only
// happens for client-side requests.
//
// Note that if you use shadow endpoints, this still works
// as the endpoint gets created by SvelteKit.
export const handlers = [
  rest.get("/msw/demo/__data.json", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json({ values }));
  })
];
//
// app/src/msw/server.ts
//

import { setupServer } from "msw/node";
import { handlers } from "./handlers.server";

export const server = setupServer(...handlers);
//
// app/src/msw/worker.ts 
//

import { setupWorker } from "msw";
import { handlers } from "./handlers.worker";

export const worker = setupWorker(...handlers);
//
// app/src/msw/fixtures/msw-demo.ts
//

export const values = ["foo", "bar"];
//
// app/src/msw/index.ts 
//

import { browser, dev } from "$app/env";

/**
 * Lazy-inject the MSW handler
 * so that no errors happen during
 * build/runtime due to invalid
 * imports from server/client.
 */
export async function inject() {
  if (dev && browser) {
    const { worker } = await import("../msw/worker");
    // For live development, I disabled all warnings
    // for requests that are not mocked. Change how
    // you think it best fits your project.
    return worker.start({ onUnhandledRequest: "bypass" }).catch(console.warn);
  }
  if (dev && !browser) {
    const { server } = await import("../msw/server");
    // Same as in worker-mock above.
    return server.listen({ onUnhandledRequest: "bypass" });
  }
}

بدء تشغيل MSW

يتمثل أحد التحديات عند استخدام MSW أثناء التطوير المباشر في أنه يتعين علينا التأكد من عدم وجود حالة سباق. يتعين علينا تحديد أمر التنفيذ ، وإلا فقد يصبح عامل الخدمة من MSW نشطًا بعد تقديم جميع الطلبات بالفعل.

لتحقيق هذا الهدف ، نقوم بتعديل ملف تخطيط الجذر الخاص بنا. نظرًا لأنه يتم تحميل هذا الملف لكل صفحة ، فإنه يعد مكانًا جيدًا لمنع أي عمليات تنفيذ أخرى حتى انتهاء MSW.

<script>
  import "../app.css";
  import { dev } from "$app/env";
  
  // Loaded from .env.local, guide covers this
  // step in a moment.
  const isMswEnabled = dev && import.meta.env.VITE_MSW_ENABLED === "true";
  // Flag to defer rendering of components
  // until certain criteria are met on dev,
  // e.g. MSW init.
  let isReady = !isMswEnabled;
  
  if (isMswEnabled) {
    import("$msw")
      .then((res) => res.inject())
      .then(() => (isReady = true));
  }
</script>

{#if isReady}
  <slot />
{/if}

إضافة صفحة تجريبية

تعرض مقتطفات التعليمات البرمجية التالية محتويات لصفحتين تجريبيتين ونقطة النهاية المستخدمة في هذا البرنامج التعليمي.

<!-- app/src/routes/msw/index.svelte -->

<script>
  import DisplayProse from "$lib/display/views/DisplayProse.svelte";
  import ProminentDisplayTitle from "$lib/display/views/ProminentDisplayTitle.svelte";
  import PageLayout from "$lib/layout/views/PageLayout.svelte";
  import SectionLayout from "$lib/layout/views/SectionLayout.svelte";
</script>

<PageLayout>
  <SectionLayout withContentTopSpacing withHeaderSpacing>
    <ProminentDisplayTitle slot="header" color="primary">MSW Landing Page</ProminentDisplayTitle>
    <DisplayProse>
      <p>
        This compoonent has no purpose other than being part of an MSW demo implementation. See <a
          href="https://flaming.codes"
          alt="Link to flaming.codes with blog posts">flaming.codes</a
        > for more details.
      </p>
      <p>
        This page doesn't fetch any data for shows how client-side fetches are mocked with MSW in
        SvelteKit.
      </p>
      <p>Simply click the link below to access the page with data.</p>
      <p>
        <a href="/msw/demo" alt="Link to demo page with data">msw/demo</a>
      </p>
    </DisplayProse>
  </SectionLayout>
</PageLayout>
<!-- app/src/routes/msw/demo.svelte -->

<script lang="ts">
  import ProminentDisplayTitle from "$lib/display/views/ProminentDisplayTitle.svelte";
  import PageLayout from "$lib/layout/views/PageLayout.svelte";
  import SectionLayout from "$lib/layout/views/SectionLayout.svelte";
  export let values: string[];
</script>

<PageLayout>
  <SectionLayout withHeaderSpacing withContentTopSpacing>
    <ProminentDisplayTitle slot="header" color="primary">MSW Demo</ProminentDisplayTitle>
    <p>
      This compoonent has no purpose other than being part of an MSW demo implementation. See <a
        href="https://flaming.codes"
        alt="Link to flaming.codes with blog posts">flaming.codes</a
      > for more details.
    </p>
    <p>
      Values: {values}
    </p>
  </SectionLayout>
</PageLayout>
//
// app/src/routes/msw/demo.ts
//

import type { RequestHandler } from "@sveltejs/kit";

// Just for demo purposes.
export const get: RequestHandler = async () => ({
  status: 200,
  body: {
    values: ["production", "data", "not", "msw"]
  }
});

إضافة متغير البيئة

نحن على وشك الانتهاء. ما هو مفقود هو إضافة علامة "VITE_MSW_ENABLED" إلى بيئتنا. نحن نستخدم ".env.local" كملفنا للاحتفاظ بالعلامة ، حيث سيستهلك Vite هذا ولن تتم إضافته إلى git.

VITE_MSW_ENABLED=true

تشغيل التطبيق

حسنًا ، كل شيء يجب أن يكون جاهزًا للاستخدام الآن! لتمكين السخرية من العميل ، ما عليك سوى التأكد من تعيين العلم. قم بتشغيل الأمر "dev" المشترك ثم لعمل محاكاة لطلبات العميل.

npm run dev

للسخرية أيضًا من الطلبات من جانب الخادم ، ما عليك سوى تشغيل الأمر الجديد الذي أضفناه في البداية.

npm run dev:msw-server

أنت الآن جاهز للسخرية من نقاط النهاية أثناء التطوير المحلي. كما هو مذكور في مثال الكود ، لا تتضمن هذه الصفحة التجريبية نماذج من جانب الخادم ، وإن كان كل شيء مُعدًا لها. هذا يعني أنه محليًا ، لا يتم تشغيل مكالمة MSW إلا إذا انتقلت من صفحة الفهرس الخاصة بالموقع التجريبي إلى الصفحة التجريبية الفعلية من خلال نقرة على الرابط.

لمحاكاة طلبات الخادم ، سيكون لديك نقاط نهاية أكثر تعقيدًا تجلب أيضًا البيانات من قواعد البيانات. يمكن بعد ذلك الاستهزاء بهذه الطلبات في معالج الخادم. لاحظ أنه بالنسبة لخوادم mocks ، تكون عناوين URL المطلقة فقط هي الصالحة.