تطوير إطارك الخاص والنمو المهني لمبرمج JS

هل تساءلت يوما كيف تعمل الأطر؟ يقول مؤلف المادة ، التي ننشر ترجمتها اليوم ، أنه عندما تعثر منذ سنوات عديدة ، بعد دراسة jQuery ، على Angular.js ، بدا ما رآه معقدًا للغاية وغير مفهوم له. ثم ظهر Vue.js ، والتعامل مع هذا الإطار ، تم إلهامه لكتابة نظامه الخاص لربط البيانات ثنائي الاتجاه. تساهم مثل هذه التجارب في التطوير المهني للمبرمج. تم إعداد هذه المقالة لأولئك الذين يرغبون في توسيع معرفتهم الخاصة في مجال التقنيات التي تستند إليها أطر JS الحديثة. على وجه الخصوص ، سيركز على كيفية كتابة جوهر إطار العمل الخاص بك الذي يدعم السمات المخصصة لعناصر HTML والتفاعل وربط البيانات ثنائي الاتجاه.

الصورة

حول نظام التفاعل


سيكون من الجيد إذا اكتشفنا في البداية كيف تعمل أنظمة التفاعل للأطر الحديثة. في الواقع ، كل شيء هنا بسيط للغاية. على سبيل المثال ، Vue.js ، عند الإعلان عن مكون جديد ، يقوم بتوكيل كل خاصية (getters و locers) باستخدام نمط تصميم " الوكيل ".

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

نمط الوكيل


نمط الوكيل يعتمد على آليات الوصول الزائد إلى كائن. هذا مشابه لكيفية عمل الأشخاص مع حساباتهم المصرفية.

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

var account = {    balance: 5000 } var bank = new Proxy(account, {    get: function (target, prop) {        return 9000000;    } }); console.log(account.balance); // 5,000 ( ) console.log(bank.balance);    // 9,000,000 (   ) console.log(bank.currency);   // 9,000,000 (  ) 

في هذا المثال ، عند استخدام الكائن bank للوصول إلى رصيد الحساب الذي يمثله كائن account ، يتم تحميل وظيفة getter بشكل زائد ، مما يؤدي إلى حقيقة أنه نتيجة لهذا الطلب ، يتم إرجاع القيمة 9،000،000 دائمًا ، بدلاً من قيمة العقار الحقيقي ، حتى إذا هذه الخاصية غير موجودة.

وإليك مثال على التحميل الزائد لوظيفة الضابط.

 var bank = new Proxy(account, {   set: function (target, prop, value) {       //      0       return Reflect.set(target, prop, 0);   } }); account.balance = 5800; console.log(account.balance); // 5,800 bank.balance = 5400; console.log(account.balance); // 0 (  ) 

هنا ، من خلال التحميل الزائد على الوظيفة set ، يمكنك التحكم في سلوكها. على سبيل المثال ، يمكنك تغيير القيمة التي تريد كتابتها إلى خاصية ، أو كتابة البيانات إلى خاصية أخرى ، أو حتى لا تفعل شيئًا.

مثال على نظام التفاعل


الآن بعد أن اكتشفنا نمط الوكيل ، سنبدأ في تطوير إطار عمل JS الخاص بنا.

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

هنا هو رمز القالب.

 <div ng-controller="InputController">   <!-- "Hello World!" -->   <input ng-bind="message"/>     <input ng-bind="message"/> </div> <script type="javascript"> function InputController () {     this.message = 'Hello World!'; } angular.controller('InputController', InputController); </script> 

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

تحليل قالب وإنشاء وحدة تحكم


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

في عملية العمل مع وحدات التحكم ، سيبحث الإطار عن العناصر التي لها سمات ng-controller . إذا كان ما يمكنك العثور عليه يطابق أحد وحدات التحكم المعلنة ، فسيقوم إطار العمل بإنشاء مثيل جديد لوحدة التحكم هذه. مثيل وحدة التحكم هذا مسؤول فقط عن هذا الجزء المعين من القالب.

 var controllers = {}; var addController = function (name, constructor) {   //     controllers[name] = {       factory: constructor,       instances: []   };     // ,     var element = document.querySelector('[ng-controller=' + name + ']');   if (!element){      return; //  ,      }     //         var ctrl = new controllers[name].factory;   controllers[name].instances.push(ctrl);     //   ..... }; addController('InputController', InputController); 

فيما يلي إعلان عن controllers متغيرة "محلية الصنع". لاحظ أن كائن controllers يحتوي على كافة وحدات التحكم التي تم تعريفها في إطار العمل عن طريق استدعاء addController .

 var controllers = {   InputController: {       factory: function InputController(){           this.message = "Hello World!";       },       instances: [           {message: "Hello World"}       ]   } }; 

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

ابحث عن العناصر المشاركة في ربط البيانات


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

 var bindings = {}; //  : element   DOM,   Array.prototype.slice.call(element.querySelectorAll('[ng-bind]'))   .map(function (element) {       var boundValue = element.getAttribute('ng-bind');       if(!bindings[boundValue]) {           bindings[boundValue] = {               boundValue: boundValue,               elements: []           }       }       bindings[boundValue].elements.push(element);   }); 

يظهر تنظيم تخزين جميع عمليات ربط الكائنات باستخدام جدول التجزئة هنا. يحتوي المتغير المدروس على جميع خصائص الربط ، بقيمها الحالية ، وجميع عناصر DOM المرتبطة بخاصية معينة.

إليك ما يبدو عليه إصدار متغير bindings :

     var bindings = {       message: {           //   :           // controllers.InputController.instances[0].message           boundValue: 'Hello World',           // HTML- (   ng-bind="message")           elements: [               Object { ... },               Object { ... }           ]       }   }; 

الربط الثنائي لخصائص جهاز التحكم


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

الكشف عن التغييرات التي تم إجراؤها من التعليمات البرمجية باستخدام وكيل


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

 //  : ctrl -    var proxy = new Proxy(ctrl, {   set: function (target, prop, value) {       var bind = bindings[prop];       if(bind) {           //    DOM,               bind.elements.forEach(function (element) {               element.value = value;               element.setAttribute('value', value);           });       }       return Reflect.set(target, prop, value);   } }); 

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

في هذا المثال ، نحن ندعم فقط ربط عناصر input ، حيث يتم تعيين سمة value فقط هنا.

الرد على أحداث العناصر


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

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

 Object.keys(bindings).forEach(function (boundValue) {   var bind = bindings[boundValue];     //          bind.elements.forEach(function (element) {     element.addEventListener('input', function (event) {       proxy[bind.boundValue] = event.target.value; //  ,          });   }) }); 

الملخص


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

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

Source: https://habr.com/ru/post/ar416021/


All Articles