إنشاء تطبيقات ويب باستخدام Yuman وPolymer

روِّج لتطبيقات الويب باستخدام أدوات حديثة

Addy Osmani
Addy Osmani

مقدمة

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

يمكن أن تساعد أدوات الواجهة الأمامية الحديثة في تنفيذ الكثير من هذه المهام بشكل مبرمَج، ما يجعلك تركّز على كتابة تطبيق جديد. ستوضح لك هذه المقالة كيفية استخدام Yeoman، وهو سير عمل لأدوات تطبيقات الويب من أجل تسهيل عملية إنشاء التطبيقات باستخدام Polymer، وهو مكتبة من رموز polyfill والسكر لتطوير التطبيقات باستخدام مكوّنات الويب.

Yeoman

تعرَّف على "يو" و"غرانت" و"بوور"

يومان رجل يرتدي قبعة ولديه ثلاث أدوات لتحسين إنتاجيتك:

  • yo هي أداة سقالات توفّر منظومة متكاملة من السقالات المرتبطة بإطار العمل، تُعرف باسم أدوات الإنشاء التي يمكن استخدامها لتنفيذ بعض المهام المملّة التي ذكرتُها سابقًا.
  • يتم استخدام grunt لإنشاء مشروعك ومعاينته واختباره، وذلك بفضل المساعدة في المهام التي ينظمها فريق Yuman وgrunt-contrib.
  • يتم استخدام bower لإدارة التبعية، بحيث لا تحتاج بعد ذلك إلى تنزيل النصوص البرمجية وإدارتها يدويًا.

بتوجيه أمر أو أمرين فقط، يمكن لـ Yuman كتابة رمز نموذجي لتطبيقك (أو أجزاء فردية مثل الطُرز)، وتجميع Sass، وتصغير CSS وJS وHTML والصور وربطها، وتشغيل خادم ويب بسيط في دليلك الحالي. ويمكنه أيضًا إجراء اختبارات الوحدات والمزيد.

يمكنك تثبيت أدوات إنشاء من الوحدات المعبّأة للعُقد (npm)، ويتوفَّر الآن أكثر من 220 مولّدًا، وقد كتب منتدى البرامج المفتوحة المصدر الكثير منها. وتشمل أدوات الإنشاء المشهورة generate-angular وgenerate-home وgener-ember.

الصفحة الرئيسية في يومان

بعد تثبيت إصدار حديث من Node.js، توجّه إلى أقرب وحدة طرفية وشغِّل:

$ npm install -g yo

أكملت هذه الخطوة. أصبح لديك الآن Yo و Grunt و Bower ويمكنك تشغيلهما مباشرة من سطر الأوامر. في ما يلي نتيجة تشغيل yo:

التركيبة في يومان

مولّد بوليمر

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

صفحة مجانية لمولّد البوليمر

منشئ-polymer هو منشئ جديد يساعدك في تنظيم تطبيقات البوليمر باستخدام نظام التشغيل Yuman، ويتيح لك إنشاء عناصر البوليمر (المخصّصة) وتخصيصها بسهولة من خلال سطر الأوامر، واستيرادها باستخدام عمليات استيراد HTML. هذا يوفر لك الوقت عن طريق كتابة التعليمة البرمجية النموذجية لك.

بعد ذلك، عليك تثبيت منشئ البوليمر عن طريق تشغيل:

$ npm install generator-polymer -g

ما مِن إجراءات أخرى مطلوبة. يحتوي تطبيقك الآن على قوى خارقة لمكونات الويب!

يحتوي منشئ الأداة الجديد الذي تم تثبيته حديثًا على بعض وحدات البت المحددة التي يمكنك الوصول إليها:

  • تُستخدم السمة polymer:element لربط عناصر البوليمر الفردية الجديدة. مثلاً: yo polymer:element carousel
  • يُستخدم polymer:app في تقوية أمر index.html الأولي، وهو ملف Gruntfile.js يحتوي على إعدادات وقت الإنشاء لمشروعك، بالإضافة إلى مهام Grunt وبنية المجلدات المقترَحة للمشروع. سيمنحك أيضًا خيار استخدام Sass Bootstrap لأنماط مشروعك.

لنبدأ في إنشاء تطبيق البوليمر

ونحن بصدد إنشاء مدونة بسيطة باستخدام بعض عناصر البوليمر المخصصة بالإضافة إلى منشئ جديد.

تطبيق البوليمر

للبدء، انتقِل إلى الوحدة الطرفية وأنشِئ دليلاً جديدًا وقرصًا مضغوطًا فيه باستخدام mkdir my-new-project && cd $_. يمكنك الآن تشغيل تطبيق البوليمر من خلال تشغيل:

$ yo polymer
إنشاء تطبيقات البوليمر

يحصل هذا على أحدث إصدار من البوليمر من Bower ويعمل على إنشاء index.html وبنية الدليل والمهام الشيّقة لسير عملك. لماذا لا تتناول قهوة بينما ننتظر انتهاء تجهيز التطبيق؟

إذًا، يمكننا بعد ذلك تشغيل grunt server لمعاينة شكل التطبيق:

خادم الشبكة الخطيرة

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

بعد ذلك، لننشئ عنصر بوليمر جديدًا لتمثيل مشاركة مدونة.

$ yo polymer:element post
إنشاء عنصر مشاركة

يسألنا "يومان" بعض الأسئلة مثل ما إذا كنا نريد تضمين دالة إنشاء أو استخدام استيراد HTML لتضمين عنصر المشاركة في index.html. لنقول لا لأول خيارين في الوقت الحالي واترك الخيار الثالث فارغًا.

$ yo polymer:element post

[?] Would you like to include constructor=''? No

[?] Import to your index.html using HTML imports? No

[?] Import other elements into this one? (e.g 'another_element.html' or leave blank)

    create app/elements/post.html

يؤدّي هذا إلى إنشاء عنصر بوليمر جديد في دليل /elements باسم post.html:

<polymer-element name="post-element"  attributes="">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

    <span>I'm <b>post-element</b>. This is my Shadow DOM.</span>

    </template>

    <script>

    Polymer('post-element', {

        //applyAuthorStyles: true,

        //resetStyleInheritance: true,

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

يحتوي على:

العمل باستخدام مصدر حقيقي للبيانات

ستحتاج مدونتنا إلى مكان لكتابة وقراءة مشاركات جديدة. لإثبات العمل باستخدام خدمة بيانات حقيقية، سنستخدم Google Apps Sheets API. هذا يسمح لنا بقراءة محتوى أي جدول بيانات تم إنشاؤه باستخدام مستندات Google بسهولة.

لنبدأ إعداد هذا:

  1. في متصفحك (لهذه الخطوات، يوصى بـ Chrome) افتح جدول بيانات مستندات Google هذا. يحتوي على نماذج بيانات المشاركات ضمن الحقول التالية:

    • رقم التعريف
    • العنوان
    • المؤلّف
    • المحتوى
    • التاريخ
    • الكلمات الرئيسية
    • البريد الإلكتروني (للمؤلف)
    • البَزَّاق (لعنوان URL المؤدي إلى المشاركة)
  2. انتقِل إلى قائمة ملف واختَر إنشاء نسخة لإنشاء نسختك الخاصة من جدول البيانات. يمكنك تعديل المحتوى في وقت فراغك، وإضافة مشاركات أو إزالتها.

  3. انتقِل إلى القائمة ملف مرة أخرى واختَر النشر على الويب.

  4. انقر على بدء النشر.

  5. ضمن الحصول على رابط إلى البيانات المنشورة، انسخ الجزء المفتاح من عنوان URL المقدَّم من مربّع النص الأخير. يظهر على النحو التالي: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0

  6. الصِق المفتاح في عنوان URL التالي الذي يظهر فيه النص your-key-goes-here: https://spreadsheets.google.com/feeds/list/your-key-goes-here/od6/public/values?alt=json-in-script&callback=. ومثال على استخدام المفتاح أعلاه: https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc/od6/public/values?alt=json-in-script

  7. يمكنك لصق عنوان URL في المتصفح والانتقال إليه لعرض نسخة JSON من محتوى مدونتك. دوِّن عنوان URL ثم اقضِ بعض الوقت في مراجعة تنسيق هذه البيانات لأنّك ستحتاج إلى تكراره من أجل عرضه على الشاشة لاحقًا.

قد تبدو مُخرجات JSON في المتصفّح صعبة بعض الشيء، ولكن لا داعي للقلق. نحن مهتمون حقًا ببيانات مشاركاتك.

تعمل واجهة برمجة تطبيقات Google Sheets API على إخراج كل حقل من الحقول الموجودة في جدول بيانات المدونة باستخدام بادئة خاصة post.gsx$. على سبيل المثال: post.gsx$title.$t وpost.gsx$author.$t وpost.gsx$content.$t وما إلى ذلك عندما نكرر كل "صف" في إخراج JSON، سنشير إلى هذه الحقول لاستعادة القيم ذات الصلة لكل مشاركة.

يمكنك الآن تعديل عنصر المشاركة المرتبط حديثًا bind أجزاء من الترميز بالبيانات في جدول البيانات. ولإجراء ذلك، نقدّم السمة post، التي ستتم قراءتها لعنوان المشاركة والمؤلف والمحتوى والحقول الأخرى التي أنشأناها سابقًا. تُستخدم السمة selected (التي ستتم تعبئتها لاحقًا) لعرض المشاركة فقط إذا انتقل المستخدم إلى الدود البزاق الصحيح لها.

<polymer-element name="post-element" attributes="post selected">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

        <div class="col-lg-4">

            <template if="[[post.gsx$slug.$t === selected]]">

            <h2>
                <a href="#[[post.gsx$slug.$t]]">
                [[post.gsx$title.$t  ]]
                </a>
            </h2>

            <p>By [[post.gsx$author.$t]]</p>

            <p>[[post.gsx$content.$t]]</p>

            <p>Published on: [[post.gsx$date.$t]]</p>

            <small>Keywords: [[post.gsx$keywords.$t]]</small>

            </template>

        </div>

    </template>

    <script>

    Polymer('post-element', {

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

بعد ذلك، لنقم بإنشاء عنصر مدونة يحتوي على مجموعة من المشاركات وتنسيق مدونتك من خلال تشغيل yo polymer:element blog.

$ yo polymer:element blog

[?] Would you like to include constructor=''? No

[?] Import to your index.html using HTML imports? Yes

[?] Import other elements into this one? (e.g 'another_element.html' or leave blank) post.html

    create app/elements/blog.html

هذه المرة نستورد المدونة إلى index.html باستخدام عمليات استيراد HTML كما نريد أن تظهر في الصفحة. بالنسبة إلى الطلب الثالث على وجه التحديد، نحدّد post.html باعتباره العنصر الذي نرغب في تضمينه.

كما في السابق، يتم إنشاء ملف عنصر جديد (blog.html) وإضافته إلى /elements، وهذه المرة استيراد post.html وتضمين <post-element> داخل علامة النموذج:

<link rel="import" href="post.html">

<polymer-element name="blog-element"  attributes="">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

    <span>I'm <b>blog-element</b>. This is my Shadow DOM.</span>

        <post-element></post-element>

    </template>

    <script>

    Polymer('blog-element', {

        //applyAuthorStyles: true,

        //resetStyleInheritance: true,

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

عندما طلبنا استيراد عنصر المدونة باستخدام عمليات استيراد HTML (وهي طريقة لتضمين مستندات HTML وإعادة استخدامها في مستندات HTML أخرى) إلى فهرسنا، يمكننا أيضًا التحقق من أنّه تمت إضافته بشكل صحيح إلى المستند <head>:

<!doctype html>
    <head>

        <meta charset="utf-8">

        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title></title>

        <meta name="description" content="">

        <meta name="viewport" content="width=device-width">

        <link rel="stylesheet" href="styles/main.css">

        <!-- build:js scripts/vendor/modernizr.js -->

        <script src="bower_components/modernizr/modernizr.js"></script>

        <!-- endbuild -->

        <!-- Place your HTML imports here -->

        <link rel="import" href="elements/blog.html">

    </head>

    <body>

        <div class="container">

            <div class="hero-unit" style="width:90%">

                <blog-element></blog-element>

            </div>

        </div>

        <script>
        document.addEventListener('WebComponentsReady', function() {
            // Perform some behaviour
        });
        </script>

        <!-- build:js scripts/vendor.js -->

        <script src="bower_components/polymer/polymer.min.js"></script>

        <!-- endbuild -->

</body>

</html>

رائع.

إضافة التبعيات باستخدام Bower

بعد ذلك، هيا نعدِّل العنصر لاستخدام العنصر Polymer JSONP للقراءة في المشاركات.json. ويمكنك الحصول على المحوّل من خلال نسخ المستودع أو تثبيت polymer-elements عبر Bower عن طريق تشغيل bower install polymer-elements.

تبعيات باور

بمجرد حصولك على الأداة، ستحتاج إلى تضمينها كاستيراد في عنصر blog.html مع:

<link rel="import" href="../bower_components/polymer-jsonp/polymer-jsonp.html">

بعد ذلك، ضمِّن العلامة وامنح url إلى جدول بيانات مشاركات المدونة سابقًا، مع إضافة &callback= إلى النهاية:

<polymer-jsonp auto url="https://spreadsheets.google.com/feeds/list/your-key-value/od6/public/values?alt=json-in-script&callback=" response="[[posts]]"></polymer-jsonp>

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

<!-- Table of contents -->

<ul>

    <template repeat="[[post in posts.feed.entry]]">

    <li><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></li>

    </template>

</ul>

ويعرض الثانية مثيلاً واحدًا من post-element لكل إدخال يتم العثور عليه، مع تمرير محتوى المشاركة إليه وفقًا لذلك. نشير إلى أنّنا نمرر السمة post التي تمثّل محتوى المشاركة لصف واحد من جدول البيانات والسمة selected التي سنضيفها بمسار.

<!-- Post content -->

<template repeat="[[post in posts.feed.entry]]">

    <post-element post="[[post]]" selected="[[route]]"></post-element>

</template>

إنّ السمة repeat التي تراها مستخدَمة في النموذج تنشئ مثيلاً يحتوي على [[ linkeds ]] لكل عنصر في مجموعة الصفيفة في مشاركاتنا عند توفير هذه السمة.

تطبيق البوليمر

لكي نتمكّن الآن من تعبئة [[route]] الحالي، سنلجأ إلى الاستخفاف واستخدام مكتبة اسمها Flatiron Director تربط بـ [[route]] كلما تغيرت تجزئة عنوان URL.

لحسن الحظ، يمكننا الحصول على عنصر البوليمر (جزء من حزمة المزيد من العناصر). بعد النسخ إلى دليل /elements، يمكننا الرجوع إليه باستخدام <flatiron-director route="[[route]]" autoHash></flatiron-director>، مع تحديد route على أنّها السمة التي نرغب في الربط بها وطلب قراءة قيمة أي تغييرات في التجزئة تلقائيًا (autoHash).

وبوضع كل شيء معًا، نحصل الآن على:

    <link rel="import" href="post.html">

    <link rel="import" href="polymer-jsonp/polymer-jsonp.html">

    <link rel="import" href="flatiron-director/flatiron-director.html">

    <polymer-element name="blog-element"  attributes="">

      <template>

        <style>
          @host { :scope {display: block;} }
        </style>

        <div class="row">

          <h1><a href="/#">My Polymer Blog</a></h1>

          <flatiron-director route="[[route]]" autoHash></flatiron-director>

          <h2>Posts</h2>

          <!-- Table of contents -->

          <ul>

            <template repeat="[[post in posts.feed.entry]]">

              <li><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></li>

            </template>

          </ul>

          <!-- Post content -->

          <template repeat="[[post in posts.feed.entry]]">

            <post-element post="[[post]]" selected="[[route]]"></post-element>

          </template>

        </div>

        <polymer-jsonp auto url="https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdHVQUGd2M2Q0MEZnRms3c3dDQWQ3V1E/od6/public/values?alt=json-in-script&callback=" response="[[posts]]"></polymer-jsonp>

      </template>

      <script>

        Polymer('blog-element', {

          created: function() {},

          enteredView: function() { },

          leftView: function() { },

          attributeChanged: function(attrName, oldVal, newVal) { }

        });

      </script>

    </polymer-element>
تطبيق البوليمر

لا يوجد المزيد. لدينا الآن مدونة بسيطة تقرأ البيانات من تنسيق JSON وتستخدم عنصرَي بوليمر يتم دمجهما مع Yuman.

العمل مع عناصر الجهات الخارجية

كان المنظومة المتكاملة للعناصر حول مكونات الويب تنمو مؤخرًا مع بدء ظهور مواقع معرض المكوّنات مثل customelements.io. من خلال استعراض العناصر التي أنشأها المنتدى، عثرت على عنصر لجلب الملفات الشخصية على Gravatar ويمكننا جلبه وإضافته إلى موقع مدوّنتنا أيضًا.

الصفحة الرئيسية للعناصر المخصصة

انسخ مصادر عناصر الرسومات إلى دليل /elements، وأدرِجها من خلال عمليات استيراد HTML في post.html، ثم أضِف إلى النموذج، مع تمرير حقل البريد الإلكتروني من جدول البيانات كمصدر لاسم المستخدم. رائع

<link rel="import" href="gravatar-element/src/gravatar.html">

<polymer-element name="post-element" attributes="post selected">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

        <div class="col-lg-4">

            <template if="[[post.gsx$slug.$t === selected]]">

            <h2><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></h2>

            <p>By [[post.gsx$author.$t]]</p>

            <gravatar-element username="[[post.gsx$email.$t]]" size="100"></gravatar-element>

            <p>[[post.gsx$content.$t]]</p>

            <p>[[post.gsx$date.$t]]</p>

            <small>Keywords: [[post.gsx$keywords.$t]]</small>

            </template>

        </div>

    </template>

    <script>

    Polymer('post-element', {

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

لنلقِ نظرة على ما يقدمه لنا هذا:

تطبيق بوليمر مزوّد بعناصر مخصّصة

جميل

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

تحسين التطبيق

يتضمن سير عمل Yuman مشروعًا آخر مفتوح المصدر يسمى Grunt، وهو أداة تنفيذ للمهام يمكنها تشغيل عدد من المهام الخاصة بإصدار (يتم تحديدها في ملف Gruntfile) لإنتاج نسخة محسَّنة من تطبيقك. سيؤدي تشغيل grunt بنفسه إلى تنفيذ مهمة default التي أعدّها المنشئ للتحليل والاختبار والإنشاء:

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

ستتحقّق مهمة jshint أعلاه من ملف .jshintrc لمعرفة إعداداتك المفضّلة، ثم شغلها على جميع ملفات JavaScript في مشروعك. للاطّلاع على كامل خياراتك باستخدام JSHint، يمكنك الاطّلاع على المستندات.

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

grunt.registerTask('test', [

    'clean:server',

    'createDefaultTemplate',

    'jst',

    'compass',

    'connect:test',

    'mocha'

]);

ونظرًا لأن تطبيقنا في هذه الحالة بسيط إلى حد ما، سنترك لك اختبارات كتابة البيانات باعتبارها تمرينًا منفصلاً. هناك بعض الإجراءات الأخرى التي يجب التعامل معها بشأن عملية التصميم، لذا لنلقِ نظرة على المهام التي ستنفّذها مهمة grunt build المحدّدة في Gruntfile.js:

grunt.registerTask('build', [

    'clean:dist',    // Clears out your .tmp/ and dist/ folders

    'compass:dist',  // Compiles your Sassiness

    'useminPrepare', // Looks for <!-- special blocks --> in your HTML

    'imagemin',      // Optimizes your images!

    'htmlmin',       // Minifies your HTML files

    'concat',        // Task used to concatenate your JS and CSS

    'cssmin',        // Minifies your CSS files

    'uglify',        // Task used to minify your JS

    'copy',          // Copies files from .tmp/ and app/ into dist/

    'usemin'         // Updates the references in your HTML with the new files

]);

ما عليك سوى تشغيل grunt build وإنشاء إصدار من تطبيقك جاهز للاستخدام في قناة الإصدار العلني. لنجربها.

إصدار الخواتم

اكتمال عملية النقل بنجاح

إذا واجهتك مشكلة، يتوفّر لك إصدار مُنشأ مسبقًا من مدونة polymer-blog للاطّلاع على https://github.com/addyosmani/polymer-blog.

ماذا لدينا أيضًا في المتجر؟

لا تزال مكونات الويب في حالة تطور وكذلك الأدوات المحيطة بها.

نبحث حاليًا عن كيفية إجراء سلسلة من عمليات استيراد HTML لتحسين أداء التحميل من خلال مشاريع مثل Vulcanize (أداة من مشروع البوليمر) وكيفية عمل المنظومة المتكاملة للمكونات مع مدير حِزم مثل Bower.

سنُعلمك عندما تتوفّر لدينا إجابات أفضل عن هذه الأسئلة، ولكن لا تزال هناك العديد من الأوقات المشوّقة في المستقبل.

تثبيت البوليمر المستقل مع Bower

إذا كنت تفضل بدء استخدام البوليمر بشكل أخف، يمكنك تثبيته بشكل مستقل مباشرةً من Bower من خلال تشغيل:

bower install polymer

والتي ستقوم بإضافتها إلى دليل Bower_components. ويمكنك بعد ذلك الرجوع إليه في فهرس التطبيقات يدويًا وبدء استخدامه في المستقبل.

ما رأيك؟

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