أصل
قبل بضعة أيام ، كنت أشاهد تنفيذ بعض أطر MVVM الشهيرة (مثل الأطر أخف وزنا مثل Avalon.js و Vue.js ، بدلاً من الأطر الأثقل مثل AngularJS و EmberJS). تقوم أطر عمل MVVM الحديثة الشهيرة عمومًا بإلغاء ربط البيانات ثنائية الاتجاه (ربط البيانات ثنائية الاتجاهات) ، وهي نقطة بيع للإطار نفسه (يبدو أن ember.js لا تدعم الربط ثنائي الاتجاه للبيانات.) ، وطرق تنفيذ ربط البيانات ثنائية الاتجاه لكل إطار عمل غير متسقة للغاية. على سبيل المثال ، يستخدم Anguarjs الفحص القذر داخليًا ، في حين أن جوهر التنفيذ الداخلي لـ Avalon.js هو تعيين ملحقات الممتلكات.
لا نعتزم مناقشة التنفيذ المحدد لربط البيانات ثنائية الاتجاه من قبل كل إطار هنا. سنتحدث فقط عن العديد من الطرق الشائعة لتنفيذ ربط البيانات ثنائية الاتجاه في الواجهة الأمامية ، والتركيز على الاختيار الفني لـ Avalon.js لتنفيذ ربط البيانات ثنائية الاتجاه.
التنفيذ التقليدي لربط البيانات ثنائية الاتجاه
أولاً ، دعنا نتحدث عن ربط البيانات ثنائية الاتجاه في الواجهة الأمامية. ببساطة ، فإن طبقة وحدة التحكم في الإطار (طبقة وحدة التحكم هنا هي مصطلح عام ، والذي يمكن فهمه على أنه البرامج الوسيطة التي تتحكم في سلوك العرض وتربط طبقة النموذج) وطبقة عرض واجهة المستخدم (طبقة العرض) لإنشاء قناة بيانات ثنائية الاتجاه. عندما يتغير أي من هاتين الطبقتين ، ستقوم الطبقة الأخرى تلقائيًا بإجراء التغييرات المقابلة على الفور (أو يبدو أنها على الفور).
بشكل عام ، لإدراك علاقة ربط البيانات ثنائية الاتجاه (عملية الارتباط بين طبقة وحدة التحكم وطبقة العرض) ، هناك حاليًا ثلاث طرق في الواجهة الأمامية.
1. فحص قذر
2. آلية المراقبة
3. تغليف ملحق الممتلكات
فحص قذر
نقول أن AngularJS (هنا يشير على وجه التحديد إلى إصدار AngularJS 1.xx ، والذي لا يمثل إصدار AngularJS 2.xx) هو تطبيق فني لربط البيانات ثنائية الاتجاه. المبدأ العام هو أن AngularJs ستحافظ على تسلسل ويضع جميع السمات التي تحتاج إلى مراقبة في هذا التسلسل. عند حدوث بعض الأحداث المحددة (لاحظ أن هذا ليس توقيتًا ولكنه يتم تشغيله بواسطة بعض الأحداث الخاصة) ، سوف يتصل AngularJs بطريقة $ Digest. المنطق داخل هذه الطريقة هو اجتياز جميع المراقبين ، ومقارنة السمات المراقبة ، وقارن ما إذا كانت قيمة السمة قد تغيرت قبل وبعد استدعاء الطريقة. إذا تغير ، سيتم استدعاء المعالج المقابل. هناك العديد من المقالات على الإنترنت التي تحلل مبدأ التنفيذ لربط بيانات AngularJS ثنائي الاتجاه ، مثل هذه المقالة ، وما إلى ذلك.
عيوب هذه الطريقة واضحة. يعتبر مراقبو العبور والتدريب مستهلكًا للغاية ، خاصةً عندما يصل عدد مراقبة صفحة واحدة إلى ترتيب من حيث الحجم.
آلية المراقبة
كان للمدون مقالة أعيد طبعها وترجمتها ، وهو تغيير ربط البيانات الذي أحدثه Object.Observe () ، والذي يشير إلى استخدام Object.Observe في ECMASCRIPT7 لمراقبة الكائن ومراقبته (أو خصائصه). بمجرد أن يتغير ، سيتم تنفيذ المعالج المقابل.
هذه هي الطريقة الأكثر مثالية لمراقبة تغييرات بيانات السمة في الوقت الحاضر. الدعم الأصلي اللغة (المتصفح) ليس أفضل من هذا. الأسف الوحيد هو أن اتساع الدعم الحالي ليس جيدًا بما يكفي ويجب الترويج بالكامل.
ملحق خاصية التغليف
هناك مفهوم للطرق السحرية في PHP ، مثل أساليب __get () و __set () في PHP. هناك مفهوم مماثل في JavaScript ، ولكن لا يسمى طريقة سحرية ، ولكن الملحق. دعونا نلقي نظرة على رمز عينة.
var data = {name: "erik" ، getName: function () {return this.name ؛} ، setName: function (name) {this.name = name ؛}} ؛من الكود أعلاه ، يمكننا الحصول على لمحة عن القفزة ، مثل طرق getName () و setName () في البيانات. يمكننا ببساطة اعتباره ملحقًا (أو ملحقًا) من Data.name.
في الواقع ، بالنسبة للرمز أعلاه ، إذا كان أكثر صرامة ، فلا يُسمح له بالوصول مباشرة إلى خاصية Data.name. يجب تمرير جميع القراءة والكتابة إلى Data.name من خلال طرق data.getName () و data.setName (). لذلك ، تخيل أنه بمجرد أن لا تسمح خاصية القراءة المباشرة والكتابة إليها ، ولكن يجب قراءتها وكتابتها من خلال الملحق ، ثم بالطبع أقوم ببعض الإضافات عن طريق إعادة كتابة طريقة الإكسسوارات في العقار ، مثل مراقبة تغيير قيمة العقار. هذا هو مبدأ استخدام ملحقات الخصائص للقيام بربط بيانات ثنائية الاتجاه.
بالطبع ، هذه الطريقة لها عيوبها أيضًا. الشيء الأبرز هو أنه في كل مرة يتم فيها إضافة مراقبة السمة ، يجب إضافة طريقة ملحق مقابلة إلى هذه السمة ، وإلا فلن يتم التقاط تغيير هذه السمة.
Object.DefineProperty طريقة
مبدأ إطار MVVM المحلي Avalon.js تنفيذ البيانات ثنائية الاتجاه هو ملحق الممتلكات. ولكن بالطبع لن يكون أصليًا مثل رمز المثال أعلاه. ويستخدم طريقة Property Compersion.DefineProperty المحددة في ECMASCRIPT 5.1 (ECMA-262). استجابة لظروف السوق المحلية ، لا يدعم البعض Object.defineProperty. تستخدم المتصفحات ذات المستوى المنخفض VBScript لتوافق مثالي ، على عكس أطر عمل MVVM الأخرى التي هجرت تدريجياً للمتصفحات المنخفضة.
دعنا أولاً نحدد طريقة الكائن.
تحدد طريقة Object.DefineProperty () خاصية جديدة مباشرة على كائن ، أو تعديل خاصية موجودة على كائن ، وإرجاع الكائن.
المعنى واضح ، وتوفر طريقة الكائن. النموذج الأولي للطريقة هو كما يلي:
Object.DefineProperty (OBJ ، Prop ، Descriptor)
في،
OBJ ، كائن ليتم تعديله
الدعامة ، مع اسم السمة المعدلة
واصف ، الوصف ذي الصلة للسمات المراد تعديلها
يتطلب الواصف أن يتم نقل كائن ، وقيمته الافتراضية كما يلي
/*** @{param} واصف*/{قابلة للتكوين: خطأ ، تعداد: خطأ ، قابل للكتابة: خطأ ، القيمة: خالية ، تعيين: غير محدد ، الحصول على: غير محدد}قابل للتكوين ، سواء كانت الخاصية قابلة للتكوين. تتضمن المعاني القابلة للتكوين ما يلي: ما إذا كان يمكن حذف السمة ، ما إذا كان يمكن تعديل الخصائص القابلة للكتابة والتعداد والقابلة للتكوين للسمات.
التعداد ، ما إذا كانت السمة تعداد. يشمل معنى التعداد: ما إذا كان يمكن اجتيازه من أجل ... في ، وما إذا كان يمكن الحصول عليه من خلال طريقة الكائن. keys ().
يمكن كتابته ، ما إذا كان يمكن إعادة كتابة السمة. يتضمن معنى إعادة الكتابة: ما إذا كان يمكن إعادة تعيين السمة.
القيمة ، القيمة الافتراضية للخاصية.
Set ، Rewriter rewriter (في الوقت الحالي ، هذا كل شيء). بمجرد إعادة تعيين السمة ، يتم استدعاء هذه الطريقة تلقائيًا.
احصل ، قارئ العقار (في الوقت الحالي ، هذا كل شيء). بمجرد الوصول إلى العقار وقراءته ، يتم استدعاء هذه الطريقة تلقائيًا.
هنا نموذج رمز ،
var o = {} ؛ object.defineProperty (o ، 'name' ، {value: 'erik'}) ؛ console.log (object.getownProperTyDescriptor (o ، 'name')) ؛ // object {value: "erik" ، crandable: false ، enumeries: false ، configable: false} object.defineProperty (o ، 'Age' ، {value: 26 ، configable: true ، crinpable: true}) ؛ console.log (o.age) ؛ // 26o.age = 18 ؛ console.log (o.age) ؛ // 18. لأن خاصية Age هي وحدة تحكم قابلة لإعادة الكتابة (Object.keys (o)) ؛ // []. لا الاسم ولا خاصية العمر هو كائن لا يطاق. // المهمة هنا هي في الواقع غير فعالة console.log (O.Sex) ؛ // 'ذكر' ؛ حذف O.Sex ؛ // false ، فإن إجراء الحذف الخاصية غير صالح أيضًابعد المثال أعلاه ، في ظل الظروف العادية ، يكون استخدام Object.DefinePropert () بسيطًا نسبيًا.
ومع ذلك ، لا يزال هناك شيء واحد يحتاج إلى عناية إضافية. عندما تقوم طريقة الكائن. وهذا يعني أنه إذا تم تعيين خاصية معينة مع سمة قابلة للكتابة أو قيمة ، فلا يمكن أن تعلن هذه الخاصية GET وتعيينها ، والعكس صحيح.
لأنه عندما يعلن الكائن.
نموذج الرمز ،
var o = {} ، myname = 'erik' ؛ object.defineProperty (o ، 'name' ، {value: myName ، set: function (name) {myName = name ؛} ، get: function () {return myname ؛}}) ؛يبدو أن الرمز أعلاه على ما يرام ، لكنه سيبلغ عن خطأ عند تنفيذه بالفعل ، وسيتم الإبلاغ عن الخطأ على النحو التالي.
Typeerror: خاصية غير صالحة. لا يمكن أن يكون لكل من خاصية إمكانات وكونها قابلة للكتابة أو لها قيمة ، #<boung>
نظرًا لأن سمة الاسم هنا تعلن عن سمة القيمة ، فإن المجموعة والحصول على سمات في نفس الوقت ، وكلاهما يوفران عناصر تحكم في القراءة والكتابة على سمة الاسم. إذا لم يتم الإعلان عن ميزة القيمة هنا ، ولكن تم الإعلان عن الميزة القابلة للكتابة ، فستكون النتيجة هي نفسها وسيتم الإبلاغ عن خطأ.