التفاعلية في Vue
نشرت: 2022-03-10في هذه المقالة ، سنلقي نظرة على التفاعلية في Vue ، وكيف تعمل ، وكيف يمكننا إنشاء متغيرات تفاعلية باستخدام طرق ووظائف تم إنشاؤها حديثًا. تستهدف هذه المقالة المطورين الذين لديهم فهم جيد لكيفية عمل Vue 2.x ويتطلعون إلى التعرف على Vue 3 الجديد.
سنقوم ببناء تطبيق بسيط لفهم هذا الموضوع بشكل أفضل. يمكن العثور على رمز هذا التطبيق على GitHub.
بشكل افتراضي ، جافا سكريبت ليست تفاعلية . هذا يعني أننا إذا أنشأنا المتغير boy
وقمنا بالإشارة إليه في الجزء A من تطبيقنا ، ثم تابع تعديل boy
في الجزء B ، فلن يتم تحديث الجزء A بالقيمة الجديدة لـ boy
.
let framework = 'Vue'; let sentence = `${framework} is awesome`; console.log(sentence) // logs "Vue is awesome" framework = 'React'; console.log(sentence) //should log "React is awesome" if 'sentence' is reactive.
المقتطف أعلاه هو مثال ممتاز للطبيعة غير التفاعلية لجافا سكريبت - وبالتالي ، لماذا لا ينعكس التغيير في متغير sentence
.
في Vue props
، كانت العناصر الخاصة computed
data()
جميعها تفاعلية بشكل افتراضي ، باستثناء الخصائص التي لم تكن موجودة في data
عند إنشاء هذه المكونات. هذا يعني أنه عند إدخال مكون في DOM ، فإن الخصائص الموجودة في كائن data
المكون فقط هي التي ستؤدي إلى تحديث المكون إذا تغيرت هذه الخصائص ومتى تغيرت.
داخليًا ، يستخدم Vue 3 كائن Proxy
(ميزة ECMAScript 6) للتأكد من أن هذه الخصائص تفاعلية ، لكنها لا تزال توفر خيار استخدام Object.defineProperty
من Vue 2 لدعم Internet Explorer (ECMAScript 5). تحدد هذه الطريقة خاصية جديدة مباشرة على كائن ما ، أو تعدل خاصية موجودة على كائن ما ، وتقوم بإرجاع الكائن.
للوهلة الأولى وبما أن معظمنا يعرف بالفعل أن التفاعلية ليست جديدة في Vue ، فقد يبدو من غير الضروري الاستفادة من هذه الخصائص ، ولكن واجهة برمجة التطبيقات Options لها حدودها عندما تتعامل مع تطبيق كبير مع وظائف قابلة لإعادة الاستخدام في عدة أجزاء من التطبيق. تحقيقا لهذه الغاية ، تم تقديم Composition API الجديد للمساعدة في استخلاص المنطق من أجل تسهيل قراءة قاعدة التعليمات البرمجية وصيانتها. أيضًا ، يمكننا الآن بسهولة جعل أي متغير تفاعليًا بغض النظر عن نوع بياناته باستخدام أي من الخصائص والطرق الجديدة.
عندما نستخدم خيار setup
، الذي يعمل كنقطة دخول لـ Composition API ، يتعذر الوصول إلى كائن data
والخصائص computed
methods
لأن مثيل المكون لم يتم إنشاؤه بعد عند تنفيذ setup
. هذا يجعل من المستحيل الاستفادة من التفاعلية المضمنة في أي من هذه الميزات في setup
. في هذا البرنامج التعليمي ، سوف نتعرف على كل الطرق التي يمكننا بها القيام بذلك.
الطريقة التفاعلية
وفقًا للوثائق ، يمكن أن تكون الطريقة reactive
، التي تعادل Vue.observable()
في Vue 2.6 ، مفيدة عندما نحاول إنشاء كائن تكون جميع خصائصه تفاعلية (مثل كائن data
في الخيارات API). تحت الغطاء ، يستخدم كائن data
في Options API هذه الطريقة لجعل جميع الخصائص الموجودة فيه تفاعلية.
لكن يمكننا إنشاء كائن تفاعلي خاص بنا مثل هذا:
import { reactive } from 'vue' // reactive state let user = reactive({ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" }, "cars": { "number": 0 } })
هنا ، قمنا باستيراد الطريقة reactive
من Vue ، ثم أعلنا متغير user
الخاص بنا عن طريق تمرير قيمته إلى هذه الوظيفة كوسيطة. عند القيام بذلك ، جعلنا user
متفاعلًا ، وبالتالي ، إذا استخدمنا user
في القالب الخاص بنا وإذا كان يجب تغيير الكائن أو خاصية هذا الكائن ، فسيتم تحديث هذه القيمة تلقائيًا في هذا القالب.
ref
مثلما لدينا طريقة لجعل الكائنات تفاعلية ، نحتاج أيضًا إلى واحدة لجعل القيم الأولية الأخرى المستقلة (سلاسل ، منطقية ، قيم غير محددة ، أرقام ، إلخ) والمصفوفات تفاعلية. أثناء التطوير ، سنعمل مع أنواع البيانات الأخرى هذه بينما نحتاج إليها أيضًا لتكون تفاعلية. النهج الأول الذي قد نفكر فيه هو استخدام reactive
وتمرير قيمة المتغير الذي نريد جعله رد الفعل.
import { reactive } from 'vue' const state = reactive({ users: [], });
نظرًا لأن reactive
يحتوي على تحويل تفاعلي عميق ، سيكون user
أيضًا كخاصية رد الفعل ، وبالتالي تحقيق هدفنا ؛ ومن ثم ، user
دائمًا بالتحديث في أي مكان يتم استخدامه في نموذج هذا التطبيق. ولكن باستخدام خاصية ref
، يمكننا جعل أي متغير بأي نوع بيانات تفاعليًا عن طريق تمرير قيمة هذا المتغير إلى ref
. تعمل هذه الطريقة أيضًا مع الكائنات ، ولكنها تتداخل مع الكائن بمستوى واحد أعمق مما كانت عليه عند استخدام الطريقة reactive
.
let property = { rooms: '4 rooms', garage: true, swimmingPool: false } let reactiveProperty = ref(property) console.log(reactiveProperty) // prints { // value: {rooms: "4 rooms", garage: true, swimmingPool: false} // }
تحت الغطاء ، يأخذ ref
هذه الحجة التي تم تمريرها إليه ويحولها إلى كائن ذي مفتاح ذي value
. هذا يعني أنه يمكننا الوصول إلى المتغير الخاص بنا عن طريق استدعاء variable.value
Value ، ويمكننا أيضًا تعديل قيمته من خلال استدعائه بنفس الطريقة.
import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2
باستخدام هذا ، يمكننا استيراد ref
إلى مكوننا وإنشاء متغير تفاعلي:
<template> <div class="home"> <form @click.prevent=""> <table> <tr> <th>Name</th> <th>Username</th> <th>email</th> <th>Edit Cars</th> <th>Cars</th> </tr> <tr v-for="user in users" :key="user.id"> <td>{{ user.name }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td> <input type="number" name="cars" v-model.number="user.cars.number" /> </td> <td> <cars-number :cars="user.cars" /> </td> </tr> </table> <p>Total number of cars: {{ getTotalCars }}</p> </form> </div> </template> <script> // @ is an alias to /src import carsNumber from "@/components/cars-number.vue"; import axios from "axios"; import { ref } from "vue"; export default { name: "Home", data() { return {}; }, setup() { let users = ref([]); const getUsers = async () => { let { data } = await axios({ url: "data.json", }); users.value = data; }; return { users, getUsers, }; }, components: { carsNumber, }, created() { this.getUsers(); }, computed: { getTotalCars() { let users = this.users; let totalCars = users.reduce(function(sum, elem) { return sum + elem.cars.number; }, 0); return totalCars; }, }; </script>
هنا ، قمنا باستيراد ref
من أجل إنشاء متغير users
تفاعلي في مكوننا. ثم استوردنا axios
لجلب البيانات من ملف JSON في المجلد public
، carsNumber
مكون carsNumber ، والذي سننشئه لاحقًا. الشيء التالي الذي قمنا به هو إنشاء متغير users
تفاعلي باستخدام طريقة ref
، بحيث يمكن users
التحديث متى تغيرت الاستجابة من ملف JSON الخاص بنا.
أنشأنا أيضًا دالة getUser
التي تجلب مصفوفة users
من ملف JSON باستخدام محاور ، وقمنا بتعيين القيمة من هذا الطلب إلى متغير users
. أخيرًا ، أنشأنا خاصية محسوبة تحسب العدد الإجمالي للسيارات التي يمتلكها مستخدمونا كما عدلناها في قسم النموذج.
من المهم ملاحظة أنه عند الوصول إلى خصائص ref
التي يتم إرجاعها في قسم القالب أو خارج setup()
، يتم تفكيكها تلقائيًا بشكل سطحي. هذا يعني أن refs
التي هي كائن ستظل تتطلب .value
. ليتم الوصول إليها. نظرًا لأن users
عبارة عن مصفوفة ، يمكننا ببساطة استخدام users
وليس users.value
في getTotalCars
.
في قسم النموذج ، قمنا بعرض جدول يعرض معلومات كل مستخدم ، جنبًا إلى جنب مع مكون <cars-number />
. يقبل هذا المكون خاصية cars
التي يتم عرضها في صف كل مستخدم على أنها عدد السيارات التي يمتلكونها. يتم تحديث هذه القيمة كلما تغيرت قيمة cars
في كائن المستخدم ، وهو بالضبط كيف سيعمل كائن data
أو الخاصية computed
إذا كنا نعمل مع Options API.
toRefs
عندما نستخدم Composition API ، تقبل وظيفة setup
وسيطين: props
و context
. يتم تمرير هذه props
من المكوّن إلى setup()
، وهي تجعل من الممكن الوصول إلى الخاصيّات التي يمتلكها المكوّن من داخل واجهة برمجة التطبيقات الجديدة هذه. هذه الطريقة مفيدة بشكل خاص لأنها تسمح بتدمير الأشياء دون فقد تفاعلها.
<template> <p>{{ cars.number }}</p> </template> <script> export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { console.log(props); // prints {gender: "female", cars: Proxy} }, }; </script> <style></style>
لاستخدام قيمة كائن من props
في Composition API مع التأكد من أنها تحافظ على تفاعلها ، فإننا نستخدم toRefs
. تأخذ هذه الطريقة كائنًا تفاعليًا وتحوله إلى كائن عادي حيث تصبح كل خاصية من خصائص الكائن التفاعلي الأصلي ref
. ما يعنيه هذا هو أن cars
تدعم ...
cars: { number: 0 }
... سيصبح الآن هذا:
{ value: cars: { number: 0 }
باستخدام هذا ، يمكننا الاستفادة من cars
داخل أي جزء من واجهة برمجة تطبيقات الإعداد مع الحفاظ على تفاعلها.
setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },
يمكننا مشاهدة هذا المتغير الجديد باستخدام watch
Composition API والرد على هذا التغيير ولكننا قد نرغب في ذلك.
setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }
toRef
حالة استخدام شائعة أخرى يمكن أن نواجهها هي تمرير قيمة ليست بالضرورة كائنًا بل هي أحد أنواع البيانات التي تعمل مع ref
(مصفوفة ، رقم ، سلسلة ، منطقية ، إلخ). باستخدام toRef
، يمكننا إنشاء خاصية تفاعلية (أي ref
) من كائن تفاعلي المصدر. سيؤدي القيام بذلك إلى ضمان بقاء الخاصية متفاعلة وسيتم تحديثها كلما تغير المصدر الأصلي.
const cars = reactive({ Toyota: 1, Honda: 0 }) const NumberOfHondas = toRef(state, 'Honda') NumberOfHondas.value++ console.log(state.Honda) // 1 state.Honda++ console.log(NumberOfHondas.value) // 2
هنا ، أنشأنا جسمًا تفاعليًا باستخدام طريقة reactive
، مع خصائص Toyota
و Honda
. استخدمنا أيضًا toRef
لإنشاء متغير تفاعلي من Honda
. من المثال أعلاه ، يمكننا أن نرى أنه عندما نقوم بتحديث Honda
باستخدام كائن cars
التفاعلية أو NumberOfHondas
، يتم تحديث القيمة في كلتا الحالتين.
هذه الطريقة متشابهة ولكنها مختلفة تمامًا عن طريقة toRefs
التي تناولناها أعلاه بمعنى أنها تحافظ على ارتباطها بمصدرها ويمكن استخدامها للسلاسل والمصفوفات والأرقام. على عكس toRefs
، لا داعي للقلق بشأن وجود الخاصية في مصدرها وقت الإنشاء ، لأنه إذا لم تكن هذه الخاصية موجودة في الوقت الذي تم فيه إنشاء هذا ref
وعوضًا عن ذلك ، null
مخزنة كخاصية صالحة ، مع وضع شكل watcher
، بحيث عندما تتغير هذه القيمة ، سيتم أيضًا تحديث ref
الذي تم إنشاؤه باستخدام toRef
.
يمكننا أيضًا استخدام هذه الطريقة لإنشاء خاصية تفاعلية من props
. سيبدو هذا كالتالي:
<template> <p>{{ cars.number }}</p> </template> <script> import { watch, toRefs, toRef } from "vue"; export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { let { cars } = toRefs(props); let gender = toRef(props, "gender"); console.log(gender.value); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }, }; </script>
هنا ، أنشأنا ref
يستند إلى خاصية gender
التي تم الحصول عليها من props
. يكون هذا مفيدًا عندما نريد إجراء عمليات إضافية على دعامة مكون معين.
خاتمة
في هذه المقالة ، نظرنا في كيفية عمل التفاعلية في Vue باستخدام بعض الأساليب والوظائف التي تم تقديمها حديثًا من Vue 3. بدأنا بالنظر في ماهية التفاعل وكيف يستخدم Vue كائن Proxy
وراء الكواليس لتحقيق ذلك. نظرنا أيضًا في كيفية إنشاء كائنات تفاعلية باستخدام reactive
وكيفية إنشاء خصائص تفاعلية باستخدام ref
.
أخيرًا ، نظرنا في كيفية تحويل الكائنات التفاعلية إلى كائنات عادية ، كل من خصائصها عبارة عن ref
يشير إلى الخاصية المقابلة للكائن الأصلي ، ورأينا كيفية إنشاء ref
لخاصية على كائن مصدر تفاعلي.
مزيد من الموارد
- "وكيل" (كائن) ، MDN Web Docs
- "أساسيات التفاعل" ، Vue.js
- "الحكام" ، Vue.js
- "Lifecycle Hook Registration Inside
setup
" ، Vue.js