Vue 3 Composition API: المرجع أو رد الفعل





الآن ، بينما أكتب هذا المقال ، نقترب أكثر من إصدار Vue 3. في رأيي ، الشيء الأكثر إثارة للاهتمام هو ملاحظة كيفية إدراك المطورين الآخرين له واستخدامه. لقد أتيحت لي الفرصة للعب مع Vue 3 في الأشهر القليلة الماضية ، لكنني أعرف أن هناك من لم يفعل ذلك.



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







لقد قمت بعمل عرضين تقديميين مؤخرًا ، وكان السؤال الشائع هو متى أستخدم Ref وعندما أستخدم Reactive للإعلان عن خاصية رد الفعل. لم يكن لدي إجابة جيدة واستغرق الأمر أسبوعين للعثور على الإجابة ، وهذه المقالة هي نتيجة بحثي.



أود أيضًا أن أشير إلى أن ما ورد أعلاه هو رأيي ، وأرجو ألا تعتقد أنه من الضروري القيام "بهذه الطريقة فقط ، وليس بطريقة أخرى". أخطط لاستخدام Ref و Reactive بهذه الطريقة حتى ينصح أحدهم بخلاف ذلك ، أو حتى أكتشف طريقة أفضل بنفسي. أعتقد أن الأمر يستغرق بعض الوقت لفهم أي تقنية جديدة ، ومن ثم قد تظهر تقنيات مثبتة.



قبل المتابعة ، أفترض أنك على الأقل تتعرف لفترة وجيزة على تكوين API وتفهم ما تتكون منه. تركز هذه المقالة على الاختلافات بين المرجع والمتفاعل ، وليس آلية تكوين API.



تفاعل Vue 2



للحصول على السرعة ، سألقي نظرة سريعة على كيفية إنشاء خصائص تفاعلية في تطبيق Vue 2. عندما تريد أن يتتبع Vue 2 التغييرات في الخصائص ، فأنت بحاجة إلى إعلانها في الكائن الذي تم إرجاعه بواسطة وظيفة البيانات.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    data() {
      return {
        title: "Hello, Vue!"
      };
    }
  };
</script>


تحت غطاء Vue 2 ، يتم استدعاء Object.defineProperty () لكل خاصية لإنشاء برنامج getter و setter لتتبع التغييرات. هذا أبسط شرح للعملية وأريد أن أنقل الفكرة: لا يوجد سحر فيها. لا يمكنك إنشاء خصائص تفاعلية في أي مكان وتتوقع أن يتتبع Vue التغييرات عليها. تحتاج إلى تعيين الخصائص التفاعلية في وظيفة البيانات.



المرجع والتفاعل



عند العمل مع واجهة برمجة التطبيقات للخيارات ، نحتاج إلى اتباع بعض القواعد عند الإعلان عن الخصائص التفاعلية ، كما هو الحال مع واجهة برمجة تطبيقات التكوين. لا يمكنك فقط إنشاء خاصية وتوقع التفاعل. في المثال التالي ، قمت بالإعلان عن خاصية العنوان وإرجاعها دالة setup () ، مما جعلها متاحة للقالب.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";
      return { title };
    }
  };
</script>




سيعمل هذا ، لكن خاصية العنوان لن تكون تفاعلية. أولئك. إذا قام شخص ما بتغيير العنوان ، فلن تنعكس هذه التغييرات في المجلس. على سبيل المثال ، إذا قمت بتغيير العنوان بعد 5 ثوانٍ ، فلن يغير الكود أدناه الصفحة الرئيسية.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";

      setTimeout(() => {
        title = "THIS IS A NEW TITLE";
      }, 5000);

      return { title };
    }
  };
</script>




يمكننا استيراد المرجع واستخدامه لجعل الخاصية رد الفعل. تحت الغطاء ، سيقوم Vue 3 بإنشاء وكيل .



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const title = ref("Hello, Vue 3!");

      setTimeout(() => {
        //   ,   .value ...
        //   
        title.value = "New Title";
      }, 5000);

      return { title };
    }
  };
</script>




أريد أن أوضح أنه بالحديث عن Ref و Reactive ، أعتقد أن هناك حالتين مختلفتين. الأول هو إنشاء مكون كما في المثال أعلاه وتحتاج إلى خصائص تفاعلية. والثاني عند إنشاء وظائف تسمح باستخدام التكوين في المكونات والوظائف. سنناقش كلا السيناريوهين في هذه المقالة.



المرجع



عند إنشاء خصائص لأنواع بسيطة ، فإن المرجع () هو خيارك الأول. إنها ليست رصاصة فضية ، لكنها تستحق البدء بها. دعني أذكرك بسبعة أنواع بدائية في JavaScript:



  • خيط
  • رقم
  • BigInt
  • قيمة منطقية
  • رمز
  • لا شيء
  • غير معرف




import { ref } from "vue";

export default {
  setup() {
    const title = ref("");
    const one = ref(1);
    const isValid = ref(true);
    const foo = ref(null);
  }
};




في المثال السابق ، عنواننا من النوع String ، لذلك لجعل الخاصية تفاعلية ، نختار ref (). إذا كان الرمز أدناه يسبب لك بعض الأسئلة ، فلا داعي للقلق ، فلدي نفس الأسئلة.



import { ref } from "vue";

export default {
  setup() {
    const title = ref("Hello, Vue 3!");

    setTimeout(() => {
      title.value = "New Title";
    }, 5000);

    return { title };
  }
};




لماذا نستخدم const إذا تغير العنوان؟ لماذا لا تستخدم اسمحوا؟ إذا قمت بطباعة العنوان إلى وحدة التحكم ، فقد تتوقع رؤية Hello ، Vue 3! ، ولكن بدلاً من ذلك سيعرض كائنًا:



{_isRef: true}
value: (...)
_isRef: true
get value: ƒ value()
set value: ƒ value(newVal)
__proto__: Object




ستأخذ الدالة ref () وسيطة وتعيد كائن ref تفاعليًا وقابل للتغيير. الكائن Ref له خاصية واحدة ، قيمة ، تشير إلى الوسيطة. هذا يعني أنك إذا كنت ترغب في الحصول على القيمة أو تغييرها ، فستحتاج إلى استخدام title.value ، وبما أن هذا كائن لن يتغير ، فقد أعلنت أنه ثابت.



استدعاء REF



السؤال التالي هو لماذا لا نسمي title.value في النموذج؟



<template>
  <h1>{{ title }}</h1>
</template>




عندما يتم إرجاع Ref كخاصية في سياق العرض (الكائن الذي يتم إرجاعه بواسطة دالة setup ()) ويتم الرجوع إليه في القالب ، يقوم Ref بإرجاع القيمة تلقائيًا. لست بحاجة إلى إضافة .value في القالب.



تعمل الخصائص المحسوبة بالطريقة نفسها ، داخل وظيفة setup () ، قم بالإشارة إليها على أنها قيمة.




تفاعلي



لقد نظرنا للتو في استخدام المرجع () لضبط تفاعل الخصائص على الأنواع البسيطة. ماذا لو أردنا إنشاء كائن تفاعلي؟ لا يزال بإمكاننا استخدام المرجع () ، ولكن تحت غطاء المحرك ، ستستخدم Vue رد الفعل () ، لذلك سألتزم بالتفاعل ().



من ناحية أخرى ، رد الفعل () لن يعمل مع الأنواع البدائية. تأخذ الوظيفة التفاعلية () شيئًا وتعيد الوكيل التفاعلي للأصل. هذا يعادل .observable () في Vue 2 ، وقد تم تغيير اسم الوظيفة هذا لتجنب الالتباس مع العناصر التي يمكن ملاحظتها في RxJS.



import { reactive } from "vue";

export default {
  setup() {
    const data = reactive({
      title: "Hello, Vue 3"
    });

    return { data };
  }
};




الاختلاف الرئيسي هو كيف نشير إلى الكائن التفاعلي في القالب. في المثال السابق ، البيانات عبارة عن كائن يحتوي على خاصية العنوان. ستحتاج إلى الرجوع إليها في القالب مثل هذا - data.title:



<template>
  <h1>{{ data.title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const data = ref({
        title: "Hello, Vue 3"
      });

      return { data };
    }
  };
</script>




الفرق بين REF والتفاعلية في أحد المكونات



بناءً على ما ناقشناه ، يبدو أن الإجابة توحي بنفسها؟ نستخدم المرجع () للأنواع البسيطة ، والمتفاعل () للأشياء. عندما بدأت في بناء المكونات ، اتضح أن هذا ليس هو الحال دائمًا ، وتقول الوثائق:



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




فكرت في هذه العبارة وتوصلت إلى الاستنتاجات التالية. مع نمو التطبيق ، حصلت على الخصائص التالية:



export default {
  setup() {
    const title = ref("Hello, World!");
    const description = ref("");
    const content = ref("Hello world");
    const wordCount = computed(() => content.value.length);

    return { title, description, content, wordCount };
  }
};




في JavaScript ، أود أن أفهم أن هذه كلها خصائص لصفحتي. في هذه الحالة ، سأجمعهم في كائن صفحة ، فلماذا لا تفعل الشيء نفسه الآن.



<template>
  <div class="page">
    <h1>{{ page.title }}</h1>
    <p>{{ page.wordCount }}</p>
  </div>
</template>

<script>
  import { ref, computed, reactive } from "vue";

  export default {
    setup() {
      const page = reactive({
        title: "Hello, World!",
        description: "",
        content: "Hello world",
        wordCount: computed(() => page.content.length)
      });

      return { page };
    }
  };
</script>




هذا هو توجهي إلى المرجع أو رد الفعل ، ولكن سيكون من الجيد إبداء رأيك. هل تفعل الشيء نفسه؟ ربما ليس هذا هو النهج الصحيح؟ يرجى التعليق.



منطق التكوين



لا يمكنك أن تخطئ عند استخدام المرجع () أو رد الفعل () في مكوناتك. ستنشئ كلتا الوظيفتين بيانات تفاعلية ، وإذا فهمت كيفية الوصول إليها في وظيفة الإعداد () وفي القوالب ، فلا توجد مشكلة.



عندما تبدأ في كتابة وظائف التكوين ، عليك أن تفهم الفرق. أستخدم أمثلة من وثائق RFC ، حيث يوضح البعض الفروق الدقيقة جيدًا.



تحتاج إلى إنشاء منطق لتتبع موضع الماوس. يجب أن تكون أيضًا قادرًا على استخدام نفس المنطق في أي مكون عند الحاجة. تقوم بإنشاء وظيفة تكوين تتعقب إحداثيات x و y ثم تعيدها إلى رمز العميل.



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);

  function update(e) {
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted(() => {
    window.addEventListener("mousemove", update);
  });

  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });

  return { x, y };
}




إذا كنت تريد استخدام هذا المنطق في أحد المكونات واستدعاء هذه الوظيفة ، فقم بإتلاف الكائن المرتجع ثم إعادة إحداثيات x و y إلى القالب.



<template>
  <h1>Use Mouse Demo</h1>
  <p>x: {{ x }} | y: {{ y }}</p>
</template>

<script>
  import { useMousePosition } from "./use/useMousePosition";

  export default {
    setup() {
      const { x, y } = useMousePosition();
      return { x, y };
    }
  };
</script>




سيعمل هذا ، ولكن إذا قررت ، بعد النظر إلى الوظيفة ، إعادة بناء عنصر الموضع وإرجاعه بدلاً من x و y:



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const pos = {
    x: 0,
    y: 0
  };

  function update(e) {
    pos.x = e.pageX;
    pos.y = e.pageY;
  }

  // ...
}




تكمن مشكلة هذا النهج في أن عميل وظيفة التكوين يجب أن يكون لديه دائمًا إشارة إلى الكائن المرتجع من أجل استمرار التفاعل. هذا يعني أنه لا يمكننا تدمير الكائن أو تطبيق عامل الانتشار:



//  
export default {
  setup() {
    //  !
    const { x, y } = useMousePosition();
    return {
      x,
      y
    };

    //  !
    return {
      ...useMousePosition()
    };

    //     
    //   `pos`  ,      x  y : `pos.x`  `pos.y`
    return {
      pos: useMousePosition()
    };
  }
};




لكن هذا لا يعني أنه لا يمكننا استخدام رد الفعل في هذه الحالة. هناك دالة toRefs () التي تحول كائنًا تفاعليًا إلى كائن بسيط ، كل خاصية لها هي المرجع للخاصية المقابلة للكائن الأصلي.



function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0
  });

  // ...
  return toRefs(pos);
}

// x & y  ref!
const { x, y } = useMousePosition();




وبالتالي ، هناك بعض الجوانب التي يجب مراعاتها عند إنشاء وظائف التكوين. إذا فهمت كيف يعمل رمز العميل مع وظائفك ، فسيكون كل شيء على ما يرام.



مجموع



عندما بدأت في استخدام Composition API لأول مرة ، شعرت بالحيرة من السؤال عن موعد تطبيق ref () ومتى يجب رد الفعل (). ربما ما زلت لا أقوم بذلك بشكل صحيح ، وحتى يخبرني أحدهم بذلك ، سألتزم بهذا النهج. نأمل أن تساعد في توضيح بعض المشكلات وتود سماع تعليقاتك.



ترميز سعيد



All Articles