إنشاء تطبيقات الويب باستخدام أدوات حديثة
مقدمة
مرحبًا. يعرف أي شخص يكتب تطبيق ويب مدى أهمية الحفاظ على إنتاجيته. ويكون الأمر صعبًا عندما تضطر إلى القلق بشأن المهام المملة مثل العثور على النص المعياري المناسب، وإعداد سير عمل التطوير والاختبار، وإزالة البيانات غير الضرورية وضغط جميع المصادر.
لحسن الحظ، يمكن أن تساعد أدوات الواجهة الأمامية الحديثة في أتمتة الكثير من هذا، مما يتيح لك التركيز على كتابة تطبيق رائع. ستوضح لك هذه المقالة كيفية استخدام Yeoman، وهو سير عمل من الأدوات لتطبيقات الويب لتبسيط إنشاء التطبيقات باستخدام Polymer، وهي مكتبة من polyfills و sugar لتطوير التطبيقات باستخدام مكونات الويب.
تعرَّف على Yo وGrunt وBower
Yeoman هو رجل يرتدي قبعة ولديه ثلاث أدوات لتحسين إنتاجيتك:
- yo هي أداة لإنشاء الهياكل الأساسية توفّر منظومة متكاملة من الهياكل الأساسية الخاصة بإطار العمل، وتُسمى أدوات الإنشاء التي يمكن استخدامها لتنفيذ بعض المهام المملة التي ذكرتها سابقًا.
- يتم استخدام grunt لإنشاء مشروعك ومعاينته واختباره، وذلك بفضل المساعدة من المهام التي ينظّمها فريق Yeoman وgrunt-contrib.
- يتم استخدام bower لإدارة التبعية، بحيث لا تحتاج بعد ذلك إلى تنزيل النصوص البرمجية وإدارتها يدويًا.
باستخدام أمر أو اثنين فقط، يمكن لـ Yeoman كتابة رمز برمجي نموذجي لتطبيقك (أو أجزاء فردية مثل النماذج)، وتجميع Sass، وتقليل ودمج CSS وJS وHTML والصور وتشغيل خادم ويب بسيط في دليلك الحالي. ويمكنه أيضًا إجراء اختبارات الوحدات والمزيد.
يمكنك تثبيت أدوات الإنشاء من وحدات Node Packaged Modules (npm)، وهناك أكثر من 220 أداة إنشاء متاحة الآن، وقد كتب العديد منها منتدى البرامج المفتوحة المصدر. تشمل أدوات الإنشاء الشائعة generator-angular وgenerator-backbone وgenerator-ember.
بعد تثبيت إصدار حديث من Node.js، انتقِل إلى أقرب وحدة طرفية وشغِّل ما يلي:
$ npm install -g yo
هذا كل شيء! لديك الآن Yo وGrunt وBower ويمكنك تشغيلها مباشرة من سطر الأوامر. إليك نتيجة تشغيل yo:
أداة إنشاء Polymer
كما ذكرت سابقًا، Polymer هي مكتبة من الرموز البرمجية polyfills وsugar التي تتيح استخدام مكوّنات الويب في المتصفّحات الحديثة. يتيح المشروع للمطوّرين إنشاء تطبيقات باستخدام منصة المستقبل وإبلاغ W3C بالمواصفات قيد التطوير التي يمكن تحسينها بشكل أكبر.
generator-polymer هو منشئ جديد يساعدك في إنشاء تطبيقات Polymer باستخدام Yeoman، مما يتيح لك إنشاء عناصر Polymer (المخصصة) وتخصيصها بسهولة عبر سطر الأوامر، واستيرادها باستخدام عمليات استيراد HTML. ويوفّر ذلك الوقت من خلال كتابة الرمز البرمجي النموذجي نيابةً عنك.
بعد ذلك، ثبِّت منشئ Polymer من خلال تشغيل:
$ npm install generator-polymer -g
انتهيت! الآن، أصبح تطبيقك يتمتع بقدرات فائقة لمكوّنات الويب.
يحتوي المولّد الذي تم تثبيته حديثًا على بعض الأجزاء المحدّدة التي ستتمكّن من الوصول إليها:
- يُستخدَم
polymer:elementلإنشاء عناصر Polymer فردية جديدة. مثلاً:yo polymer:element carousel - يُستخدم
polymer:appلإنشاء ملف index.html الأولي، وهو ملف Gruntfile.js يحتوي على إعدادات وقت الإنشاء لمشروعك بالإضافة إلى مهام Grunt وبنية المجلدات المقترَحة للمشروع. سيتيح لك أيضًا خيار استخدام Sass Bootstrap لأنماط مشروعك.
لننشئ تطبيق Polymer
سننشئ مدونة بسيطة باستخدام بعض عناصر Polymer المخصصة وأداة الإنشاء الجديدة.
للبدء، انتقِل إلى الوحدة الطرفية، وأنشئ دليلاً جديدًا، ثم انتقِل إليه باستخدام الأمر mkdir my-new-project && cd $_. يمكنك الآن بدء تطبيق Polymer من خلال تشغيل:
$ yo polymer
يؤدي ذلك إلى الحصول على أحدث إصدار من Polymer من Bower وإنشاء ملف index.html وبنية الدليل ومهام Grunt لسير العمل. ما رأيك في تناول فنجان قهوة بينما ننتظر انتهاء التطبيق من الاستعداد؟
بعد ذلك، يمكننا تشغيل grunt server لمعاينة شكل التطبيق:
يدعم الخادم LiveReload، مما يعني أنه يمكنك تشغيل محرر نصوص وتعديل عنصر مخصص وسيقوم المتصفح بإعادة التحميل عند الحفظ. يمنحك هذا عرضًا رائعًا في الوقت الفعلي للحالة الحالية لتطبيقك.
بعد ذلك، لننشئ عنصر Polymer جديدًا لتمثيل منشور مدوّنة.
$ yo polymer:element post
يطرح علينا Yeoman بعض الأسئلة، مثل ما إذا كنا نريد تضمين دالة إنشائية أو استخدام استيراد 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
يؤدي ذلك إلى إنشاء عنصر Polymer جديد في دليل /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>
ويشتمل على ما يلي:
- الرمز البرمجي النموذجي للعنصر المخصّص، ما يتيح لك استخدام نوع عنصر DOM مخصّص في صفحتك (مثل
<post-element>) - علامة نموذج لإنشاء نماذج من جهة العميل "الأصلية" وأنماط ذات نطاق نموذجية لتضمين أنماط العنصر
- تسجيل العناصر المتكررة وأحداث مراحل النشاط
العمل مع مصدر حقيقي للبيانات
ستحتاج مدونتنا إلى مكان لكتابة وقراءة المشاركات الجديدة. لتوضيح كيفية العمل مع خدمة بيانات حقيقية، سنستخدم Google Apps Spreadsheets API. يتيح لنا ذلك قراءة محتوى أي جدول بيانات تم إنشاؤه باستخدام "مستندات Google" بسهولة.
لنبدأ عملية الإعداد:
في المتصفّح (يُنصح باستخدام Chrome لتنفيذ هذه الخطوات)، افتح جدول بيانات مستندات Google هذا. ويحتوي على نموذج بيانات المنشورات ضمن الحقول التالية:
- رقم التعريف
- العنوان
- المؤلّف
- المحتوى
- التاريخ
- الكلمات الرئيسية
- البريد الإلكتروني (للمؤلف)
- Slug (لعنوان URL الخاص بمنشورك)
انتقِل إلى قائمة ملف واختَر إنشاء نسخة لإنشاء نسختك الخاصة من جدول البيانات. يمكنك تعديل المحتوى في أي وقت، وإضافة منشورات أو إزالتها.
انتقِل إلى قائمة ملف مرة أخرى واختَر النشر على الويب.
انقر على بدء النشر
ضمن الحصول على رابط للبيانات المنشورة، من مربّع النص الأخير، انسخ جزء المفتاح من عنوان URL المقدَّم. ويظهر على النحو التالي: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0
الصِق المفتاح في عنوان 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.
يمكنك لصق عنوان URL في المتصفّح والانتقال إليه لعرض نسخة JSON من محتوى مدونتك. دوِّن عنوان URL ثم اقضِ بعض الوقت في مراجعة تنسيق هذه البيانات لأنّك ستحتاج إلى تكرارها لعرضها على الشاشة لاحقًا.
قد يبدو ناتج JSON في متصفحك مخيفًا بعض الشيء، ولكن لا تقلق. نحن مهتمون فقط بالبيانات الخاصة بمنشوراتك.
تُخرج واجهة برمجة التطبيقات Google Spreadsheets API كل حقل من حقول جدول بيانات مدونتك ببادئة خاصة post.gsx$. على سبيل المثال: post.gsx$title.$t وpost.gsx$author.$t وpost.gsx$content.$t وما إلى ذلك. عندما نكرّر كل "صف" في مخرجات JSON، سنشير إلى هذه الحقول لاسترداد القيم ذات الصلة لكل منشور.
يمكنك الآن تعديل عنصر المنشور الذي تم إنشاؤه حديثًا لربط أجزاء من الترميز بالبيانات في جدول البيانات. لإجراء ذلك، نقدّم سمة 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 لقراءة posts.json. يمكنك الحصول على المحوّل من خلال استنساخ المستودع باستخدام git أو تثبيت 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 التي تراها مستخدمة في النموذج الخاص بنا مثيلاً مع [[ bindings ]] لكل عنصر في مجموعة مصفوفة منشوراتنا، عند توفيرها.
الآن، لكي نتمكّن من تعبئة [[route]] الحالي، سنستخدم مكتبة تُسمّى Flatiron director ترتبط بـ [[route]] كلما تغيّر تجزئة عنوان URL.
لحسن الحظ، يتوفّر عنصر Polymer (جزء من حزمة more-elements) يمكننا استخدامه لهذا الغرض. بعد نسخها إلى دليل /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 وتستخدم عنصرين من عناصر Polymer تم إنشاؤهما باستخدام Yeoman.
العمل مع عناصر تابعة لجهات خارجية
لقد شهدت منظومة العناصر المتكاملة حول "مكوّنات الويب" نموًا في الآونة الأخيرة مع بدء ظهور مواقع إلكترونية لمعارض المكوّنات، مثل customelements.io. من خلال الاطّلاع على العناصر التي أنشأها المنتدى، وجدتُ عنصرًا لجلب ملفات Gravatar الشخصية ويمكننا الحصول عليه وإضافته إلى موقعنا الإلكتروني أيضًا.
انسخ مصادر عناصر 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>
لنلقِ نظرة على ما يقدّمه لنا هذا:
رائع!
في وقت قصير نسبيًا، أنشأنا تطبيقًا بسيطًا يتألف من عدة مكوّنات ويب بدون الحاجة إلى القلق بشأن كتابة رمز برمجي نموذجي أو تنزيل التبعيات يدويًا أو إعداد خادم محلي أو إنشاء سير عمل.
تحسين تطبيقك
يتضمّن سير عمل Yeoman مشروعًا آخر مفتوح المصدر يُسمى 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 (أداة من مشروع Polymer) وكيفية عمل المنظومة المتكاملة للمكوّنات مع مدير حِزم مثل Bower.
سنُعلمك عندما تتوفّر لدينا إجابات أفضل عن هذه الأسئلة، ولكن هناك الكثير من الأوقات الممتعة في المستقبل.
تثبيت Polymer بشكل مستقل باستخدام Bower
إذا كنت تفضّل بداية أسهل مع Polymer، يمكنك تثبيته بشكل مستقل مباشرةً من Bower من خلال تشغيل:
bower install polymer
مما سيضيفه إلى دليل bower_components. ويمكنك بعد ذلك الإشارة إليه في فهرس تطبيقك يدويًا وتحقيق النجاح في المستقبل.
ما رأيكم بذلك؟
أنت الآن تعرف كيفية إنشاء تطبيق Polymer باستخدام مكونات الويب مع Yeoman. إذا كانت لديك ملاحظات حول أداة الإنشاء، يُرجى إعلامنا بها في التعليقات أو الإبلاغ عن خطأ أو نشرها في أداة تتبُّع المشاكل في Yeoman. نودّ معرفة ما إذا كان هناك أي شيء آخر تريد أن تؤدّيه أداة الإنشاء بشكل أفضل، لأنّنا لا نستطيع تحسينها إلا من خلال استخدامك لها وملاحظاتك :)