משדרגים את אפליקציות האינטרנט באמצעות כלים מודרניים
מבוא
Gogoley’. כל מי שכותב אפליקציית אינטרנט יודע עד כמה חשוב לשמור על פרודוקטיביוּת. זה מאתגר כשצריך לדאוג למשימות מעייפות כמו מציאת התבנית הנכונה, הגדרת תהליך עבודה של פיתוח ובדיקה והקטנה ודחיסה של כל המקורות.
למרבה המזל, כלים מודרניים לשמירה על ממשק הקצה יכולים לעזור באוטומציה של רוב הבעיות האלה, ולהתמקד בכתיבת אפליקציה מהירה. במאמר הזה נסביר איך להשתמש ב-Yeoman, זרימת עבודה של כלים לאפליקציות אינטרנט, כדי לייעל יצירת אפליקציות באמצעות Polymer, ספרייה של פולימרים וסוכר לפיתוח אפליקציות באמצעות רכיבי אינטרנט.
נעים להכיר: יו, גראנט ובאוור
יאומן הוא גבר בכובע שיש לו שלושה כלים לשיפור הפרודוקטיביות:
- yo הוא כלי פיגומים שמציע מערכת אקולוגית של פיגומים ספציפיים למסגרת, הנקראת מחוללים, שניתן להשתמש בהם לביצוע חלק מהמשימות המייגעות שהזכרתי קודם.
- הקובץ grunt משמש ליצירה, לתצוגה מקדימה ולבדיקה של הפרויקט, בזכות משימות שאספו חברי הצוות של Jeoman ו-grunt-contrib.
- bower משמש לניהול תלות, כך שכבר לא צריך להוריד ולנהל את הסקריפטים באופן ידני.
באמצעות פקודה או שתיים בלבד, Yaoman יכול לכתוב קוד סטנדרטי לאפליקציה שלכם (או חלקים נפרדים כמו דגמים), להדר את ה-Sass, למזער ולשרשר את ה-CSS, ה-JS, ה-HTML והתמונות שלכם ולהפעיל שרת אינטרנט פשוט בספרייה הנוכחית שלכם. הוא יכול גם להריץ בדיקות יחידה ולבצע פעולות נוספות.
אפשר להתקין גנרטורים מ-Node Packaged Modules (npm) ויש יותר מ-220 גנרטורים שזמינים עכשיו, ורבים מהם נכתבו על ידי קהילת הקוד הפתוח. גנרטורים פופולריים כוללים גנרטור זוויתי, generator-backbone ו-generator-ember.
אחרי שמותקנת גרסה עדכנית של Node.js, נכנסים לטרמינל הקרוב ביותר ומריצים את הפקודה:
$ npm install -g yo
זהו! עכשיו יש לך את Yo, GRunt ו-Buwer, ואפשר להריץ אותם ישירות משורת הפקודה. זה הפלט של הפקודה yo
:
גנרטור פולימרים
כפי שהזכרתי קודם, Polymer הוא ספרייה של polyfills וסוכר שמאפשרת להשתמש ברכיבי אינטרנט בדפדפנים מודרניים. הפרויקט מאפשר למפתחים לבנות אפליקציות באמצעות הפלטפורמה של המחר ולהודיע ל-W3C על מקומות שבהם ניתן לשפר עוד יותר את המפרט במהלך הטיסה.
gener-polymer הוא מחולל חדש שעוזר לגבש אפליקציות של פולימרים באמצעות Yoman, ומאפשר ליצור ולהתאים אישית רכיבי פולימר (בהתאמה אישית) בקלות באמצעות שורת הפקודה, ולייבא אותם באמצעות ייבוא HTML. כך תוכלו לכתוב קוד סטנדרטי (בוילרפלייט) ולחסוך לכם זמן.
בשלב הבא, מתקינים את המחולל של Polymer באמצעות:
$ npm install generator-polymer -g
זה הכול. עכשיו יש לאפליקציה שלך כוחות על של רכיבי אינטרנט!
המחולל החדש שהותקן כולל כמה ביטים ספציפיים שתהיה לכם גישה אליהם:
polymer:element
משמש כדי לגבש אלמנטים של פולימר נפרדים. לדוגמה:yo polymer:element carousel
.polymer:app
משמש כדי לגבש את הקובץ index.html הראשוני, קובץ ה-Gruntfile.js שמכיל את תצורת זמן ה-build של הפרויקט וגם משימות של Graunt ומבנה תיקיות המומלץ לפרויקט. נוסף לכך, תהיה לכם אפשרות להשתמש ב-Sass Bootstrap בסגנונות של הפרויקט.
בואו נבנה אפליקציית פולימר
נבנה בלוג פשוט מאלמנטים של פולימר בהתאמה אישית ומהמחולל החדש שלנו.
כדי להתחיל, עוברים לטרמינל, יוצרים ספרייה חדשה וממלאים בה cd באמצעות mkdir my-new-project && cd $_
. עכשיו אפשר להתחיל את אפליקציית Polymer שלך על ידי ריצה:
$ yo polymer
כך תשיגו את הגרסה העדכנית ביותר של Polymer מ-Buwer, ותיצרו אלמנטים של index.html, מבנה ספריות ומשימות גראנט עבור זרימת העבודה שלכם. למה לא לקחת קפה בזמן שאנחנו מחכים שהאפליקציה תסיים להתכונן?
אוקיי, עכשיו נוכל להריץ את grunt server
כדי לראות תצוגה מקדימה של האפליקציה:
השרת תומך ב-LiveReload, כלומר אפשר להפעיל עורך טקסט ולערוך רכיב בהתאמה אישית. לאחר השמירה, הדפדפן ייטען מחדש. כך אפשר לקבל תצוגה יפה של המצב הנוכחי של האפליקציה בזמן אמת.
בשלב הבא ניצור רכיב פולימר חדש שייצג פוסט בבלוג.
$ yo polymer:element post
יאומן שואל אותנו כמה שאלות, למשל אם אנחנו רוצים לכלול בנאי או להשתמש ב-HTML Import כדי לכלול את רכיב הפוסט ב-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>
התוצאה מכילה:
- קוד סטנדרטי עבור אלמנט מותאם אישית, שמאפשר להשתמש בסוג רכיב DOM מותאם אישית בדף (למשל
<post-element>
) - תג תבנית עבור תבניות 'מותאמות' בצד הלקוח וסגנונות בהיקף לדוגמה שתקפים את הסגנונות של הרכיב
- תבניות סטנדרטיות לרישום של רכיבים ואירועים במחזור חיים.
עבודה עם מקור נתונים אמיתי
בבלוג שלנו צריך להיות מקום שבו אפשר יהיה לכתוב ולקרוא פוסטים חדשים. כדי להדגים עבודה עם שירות נתונים אמיתי, נשתמש בממשק ה-API של Google Apps Sheets. כך אנחנו יכולים לקרוא בקלות את התוכן של כל גיליון אלקטרוני שנוצר באמצעות Google Docs.
קדימה, מתחילים:
בדפדפן (כדי לבצע את השלבים האלה, מומלץ להשתמש ב-Chrome) פותחים את הגיליון האלקטרוני הזה של Google Docs. הוא מכיל נתוני פוסטים לדוגמה בשדות הבאים:
- מזהה
- שם הסרטון
- Author
- Content
- תאריך
- מילות מפתח
- כתובת אימייל (של המחבר)
- חילזון (לכתובת ה-URL של החילזון של הפוסט)
בתפריט קובץ, בוחרים באפשרות יצירת עותק כדי ליצור עותק משלכם של הגיליון האלקטרוני. אתם יכולים לערוך את התוכן בזמנכם החופשי, להוסיף או להסיר פוסטים.
עבור לתפריט קובץ שוב ובחר פרסם באינטרנט.
לוחצים על התחלת פרסום
בקטע קבלת קישור לנתונים שפורסמו, בתיבת הטקסט האחרונה, מעתיקים את החלק של המפתח מכתובת ה-URL שסופקה. זה נראה כך: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0
מדביקים את ה-key בכתובת ה-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 של התוכן בבלוג. רשום את כתובת האתר ולאחר מכן הקדישו זמן לבדיקת הפורמט של הנתונים האלה, כי תצטרכו לחזור עליה כדי להציג אותם במסך מאוחר יותר.
פלט ה-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 לקריאה ב-publish.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
שמופיע בתבנית שלנו יוצר ושומר מכונה עם [[ links ]] לכל רכיב באוסף המערכים של הפוסטים שלנו, כשהוא מופיע.
עכשיו, כדי שנוכל לאכלס את ה-[[route]] הנוכחי, נתחיל לרמות ולהשתמש בספרייה שנקראת Flatiron Director (מנהל Flatiron) שמקושרת ל-[[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 ומשתמש בשני אלמנטים של פולימר שדורגו ב-Yeoman.
עבודה עם אלמנטים של צד שלישי
המערכת האקולוגית של הרכיבים סביב רכיבי האינטרנט גדלה לאחרונה, ומופיעים אתרים של גלריית רכיבים כמו customelements.io. לאחר שבדקתי את הרכיבים שנוצרו על ידי הקהילה, מצאתי פרופיל לשליפת פרופילי נגרות ואנחנו יכולים לשלוף אותו ולהוסיף אותו גם לאתר הבלוג שלנו.
מעתיקים את מקורות אלמנט הגרירה לספרייה /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>
בואו נראה מה זה נותן לנו:
יפה!
בתוך זמן קצר יחסית, יצרנו אפליקציה פשוטה שמורכבת מכמה רכיבי אינטרנט, בלי שנצטרך לדאוג לכתוב קוד סטנדרטי (boilerplate), להוריד יחסי תלות באופן ידני או להגדיר שרת מקומי או תהליך עבודה של build.
אופטימיזציה של האפליקציה
תהליך העבודה של Yaoman כולל פרויקט קוד פתוח נוסף בשם Grunt – מפעיל משימות שיכול להריץ מספר משימות ספציפיות ל-build (מוגדרות ב-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'
]);
מאחר שהאפליקציה שלנו במקרה הזה פשוטה למדי, נרשום את כל הבחינה במסגרת פעילות נפרדת. יש עוד כמה דברים שחשוב לנו לטפל בהם בתהליך ה-build, אז נראה מה עושה משימת 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.
נודיע לך כשיהיו לנו תשובות טובות יותר לשאלות האלה, אבל לפני כן נעדכן אותך על זמנים מלהיבים.
התקנה עצמאית של פולימר עם Bower
אם אתם מעדיפים התחלה קלה יותר ל-Polymer, אפשר להתקין אותה בנפרד ישירות מ-Bower על ידי הרצת:
bower install polymer
שיוסיף אותו לספריית boer_components. לאחר מכן תוכלו להפנות אליו באופן ידני באינדקס של האפליקציה ולעצב את העתיד.
מה דעתך?
עכשיו אתם יודעים איך לגבש אפליקציית פולימר באמצעות Web Components (רכיבי אינטרנט) עם Yoman. אם יש לכם משוב על המחולל, נשמח לשמוע על כך בתגובות. לחלופין, תוכלו לדווח על באג או לפרסם הודעה בכלי למעקב אחר בעיות ב-Yeoman. נשמח לדעת אם יש משהו נוסף שהיית רוצה לראות שהמחולל עושה טוב יותר, כי נוכל לשפר רק את השימוש שלך והמשוב שלך :)