حبل. تحاول أن تفعل ذلك بشكل تعريفي

في كل مرة أحتاج إلى الجلوس لإنشاء تطبيق جديد ، أقع في ذهول سهل. رأسي يدور من الحاجة لاختيار المكتبة أو الإطار الذي يستغرقه هذا الوقت. آخر مرة كتبت فيها على مكتبة X ، ولكن الآن كبر إطار عمل Y وتم تسليمه ، ولا يزال هناك UI Kit Z رائعة ، وقد تم ترك الكثير من العمل من المشاريع السابقة.


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


في الواقع ، المشكلة ليست أن قرار شخص آخر سيء أو غير فعال. لا. المشكلة هي أن قرار شخص آخر يجعلنا نفكر بطرق قد لا تكون مناسبة لنا. لكن انتظر. ماذا تعني كلمة "مريح غير مريح" وكيف يمكن أن يؤثر ذلك على التنمية؟ أذكر أن هناك شيء مثل DX ، في الواقع - مجموعة من الممارسات الشخصية والمقبولة بشكل عام. من هنا يمكننا القول أنه مناسب لنا عندما يتزامن DX الخاص بنا مع DX لمؤلف المكتبة أو الإطار. وفي حالة الاختلاف ، ينشأ الانزعاج والغضب والبحث عن شيء جديد.


قليلا من التاريخ


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


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


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


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


إدارة التطبيق == إدارة الدولة


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


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


  1. القوالب. نحن نستخدم لغة الترميز المفضلة لدينا ونكملها بتوجيهات التفريع والتكرار.
  2. وظيفة. وصفنا في وظائفنا فروعنا وحلقاتنا بلغة البرمجة المفضلة لدينا.

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


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


createFromConfig({ data: { name: 'Alice' }, tag: 'div', class: 'clickable box', onClick: function () { alert('Click') } }) 

والنتيجة هي تكوين JS يصف حالة واحدة محددة بالكامل. لوصف حالات كثيرة ، سيكون من الضروري تحقيق القابلية للتوسعة لهذا التكوين. وما هي الطريقة الأكثر ملاءمة لجعل مجموعة من الخيارات قابلة للتوسيع؟ لن نخترع أي شيء هنا - توجد خيارات التحميل الزائد لفترة طويلة. يمكن رؤية كيفية عملها في مثال Vue من خلال API Options. ولكن على عكس نفس الأمر ، كنت أتساءل عما إذا كان يمكن وصف الحالة الكاملة ، بما في ذلك البيانات والمستند ، بالطريقة نفسها.


هيكل التطبيق والتصريح


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

بسرعة كبيرة ، توصلت إلى استنتاج مفاده أن العنصر الهيكلي (المكون) ليس عنصر مستند ، بل كيان ما ، وهو:


  1. يجمع بين البيانات والمستندات (الربط والأحداث)
  2. متصل مع كيانات أخرى مماثلة (بنية الشجرة)

كما أشرت سابقًا ، إذا كنت ترى التطبيق كمجموعة من الحالات ، فيجب أن يكون لديك طريقة وصف لهذه الحالات. علاوة على ذلك ، من الضروري إيجاد مثل هذه الطريقة بحيث لا تحتوي على عوامل تشغيل "زائفة". نحن نتحدث عن تلك العناصر المساعدة للغاية التي يتم تقديمها في القوالب - #if و # Alsif و v-for ، إلخ. أعتقد أن العديد من الأشخاص يعرفون الحل بالفعل - من الضروري نقل المنطق إلى النموذج ، وترك مستوى واجهة برمجة التطبيقات يسمح لك بالتحكم في العناصر الهيكلية من خلال أنواع البيانات البسيطة.


من قبل الإدارة ، وأنا أفهم وجود التباين ودورة.


التباين (إن لم يكن غير ذلك)


دعونا نرى كيف يمكنك التحكم في خيارات العرض باستخدام مثال مكون البطاقة في Chorda:


 const isHeaderOnly = true const card = new Html({ $header: { /*  */ }, $footer: { /*  */ }, components: {header: true, footer: !isHeaderOnly} //    }) 

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


دورة (من أجل)


يتطلب العمل مع البيانات التي تُعرف كميتها فقط في وقت التشغيل التكرار على القوائم.


 const drinks = ['Coffee', 'Tea', 'Milk'] const html = new Html({ html: 'ul', css: 'list', defaultItem: { html: 'li', css: 'list-item' }, items: drinks }) 

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


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


 //   const state = { struct: { header: true, footer: false, }, drinks: ['Coffee', 'Tea', 'Milk'] } //  const card = new Html({ $header: { /*  */ }, $content: { html: 'ul', css: 'list', defaultItem: { html: 'li', css: 'list-item' }, items: state.drinks }, $footer: { /*  */ }, components: state.struct }) 

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


عندما اخترع كل شيء بالفعل بالنسبة لنا


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


لقد حاولت بصدق أن أجعل وظيفتي التقديرية في وقت ما ، لكن بعد فترة استسلمت ، لأنني لا أستطيع الرسم بشكل أسرع من VanillaJS (للأسف!). من المألوف الآن استخدام VDOM للتقديم ، وربما تكون تطبيقاته متوفرة بكثرة. لذلك ، بالإضافة إلى تطبيق آخر للشجرة الافتراضية ، قررت عدم إضافته إلى البنك الخنزير لجيثب - فقط الإطار التالي يكفي.


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


لتوصيل VDOM بالعناصر الهيكلية ، تحتاج إلى شيء مثل التصميم . يمكن أن يسمى وظيفة وثيقة العنصر الهيكلي. المهم هو وظيفة نقية.


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


 function orderedByKeyLayout (h, type, props, components) { return h(type, props, components.sort((a, b) => a.key - b.key).map(c => c.render())) } const html = new Html({ $header: {}, $content: {}, $footer: {}, layout: orderedByKeyLayout //     }) 

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


رشة من التفاعل


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


عند العمل مع البيانات ، لم يعجبني شيئان:


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

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


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


مثال على كيفية توزيع البيانات السياقية عبر شجرة مكون:


 const html = new Html({ //     scope: { drink: 'Coffee' }, $component1: { scope: { cups: 2 }, $content: { $myDrink: { //      ,    drinkChanged: function (v) { //    drink   text this.opt('text', v) } }, $numCups: { cupsChanged: function (v) { this.opt('text', v + ' cups') } } } }, $component2: { scope: { drink: 'Tea' //      drink }, drinkChanged: function (v) { //    drink   text this.opt('text', v) } } }) //    // <div> // <div> // <div> // <div>Coffee</div> // <div>2 cups</div> // </div> // </div> // <div>Tea</div> // </div> 

أصعب لحظة لفهمها هي أن كل مكون له سياقه الخاص ، وليس السياق المعلن في الجزء العلوي من الهيكل ، كما نفعل عادة عند العمل باستخدام القوالب.


ماذا عن خيارات التحميل الزائد؟


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


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


دعونا نرى كيف تعمل الطبقات مع التكوين في Chorda.


 //      class Card extends Html { config () { return { css: 'box', $header: {}, $content: {}, $footer: {} } } } const html = new Html({ css: 'panel', $card: { as: Card, $header: { //       title $title: { css: 'title', text: 'Card title' } } } }) 

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


في Chorda ، يعد الفصل حاويًا أساسيًا للتكوين ويلعب دور نوع خاص من الشوائب.


لماذا إطار آخر؟


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


في الوقت الحالي ، Chorda في تطور نشط. الاتجاهات الرئيسية مرئية بالفعل ، لكن التفاصيل تتغير باستمرار.


شكرا لك على القراءة حتى النهاية. سأكون سعيدا للمراجعات.


أين يمكنني أن أرى؟


الوثائق


مصادر جيثب


أمثلة CodePen

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


All Articles