Grunt.js جدول مهام موقعك الروتينية!

كتبه هبة فريد 26 فبراير 2014

كم من الوقت تستهلك في مهام مفيدة لتحسين أداء موقعك لكن روتينية وتستهلك كثيراً من الوقت والطاقة، مثل:

  1. تصغير ودمج ملفات CSS وJS..
  2. تعديل وضغط الصور للوصول إلى أقل حجم ممكن بأعلى جودة ممكنة.
  3. التحويل من SASS إلى CSS أو العكس.
  4. اختبار ملفات HTML وCSS وJS والتأكد من خلوها من أخطاء التكويد.

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

وهنا تظهر Grunt.js، الذي يصفها مؤسسيها بأنها أداة إجراء المهام، وهي أداة نصية Command-line based مبنية على منصة node.js، وتساهم كثيراً في تسريع وتيرة العمل على المواقع وتصميمها وتطويرها.

image01
اداة إجراء المهام، وهي أداة نصية Command-line based مبنية على منصة node.js

إذا كنت مصمم ويب أو مدير موقع، فقد تتوجس من إستخدام هذه الأداة النصية متحججاً بأنها مهمة المطورين، لكن لا تقلق، فإن مهامها بسيطة للغاية لا تحتاج متخصصاً في تطوير الويب بل معرفة بسيطة بلغة JS، وسنذكر جميع الأوامر النصية التي قد تحتاجها أثناء عملك بهذه الأداة، ولا تقلق إن لم تكن لديك خبرة سابقة مع منصة node.js إذ إن عملك كله سيكون بأداة Grunt (وإن كنتُ أنصح بالقراءة عنها لما لها من فعالية وكفاءة).

تتميز Grunt.js عن غيرها بأنها تؤدي العديد من المهام المجدولة التي تحددها مسبقاً، وهي مفيدة لفرق العمل لتوحيد طريقة تنفيذ المهام والثبات، ومتجددة بإستمرار بعدد الإضافات التي يضيفها المطورين إليها لتزيد نطاق المهام التي يمكن أداؤها.

تنصيب Grunt وإعدادها

سنتحدث هنا عن طريقة تنصيب هذه الأداة وإمدادها بالإضافات التي تحتاجها لأداء المهام، ويمكن تنصيبها على الخادم المحلي Localhost أو على الخادم الخارجي Server، وتتطلب هذه الأداة وجود node.js ذات إصدار 0.8.0 أو أعلى.

image00
تتطلب هذه الأداة وجود node.js ذات إصدار 0.8.0 أو أعلى

وإذا كانت لديك نسخة node.js منصبة بالفعل، يمكنك التأكد من اصدارها بكتابة الكود التالي في الطرفية terminal :


1
node -v

افتح الطرفية terminal واكتب الأمر التالي لتنصيب البرنامج النصي Grunt Command-Line Interface لتستطيع التحكم في عمليات الأداة (قد تحتاج لكتابة sudo لصلاحيات الروت):


1
npm install -g grunt-cli

للتأكد من التنصيب الصحيح للأداة اكتب الكود التالي:


1
grunt -v

ثم توجه إلى مسار الموقع (مسار قالب التصميم بالأدق، على سبيل المثال إذا كنت تستخدم قالب twenty eleven في WordPress فستتوجه إلى المسار /site-directory/wp-content/themes/twenty-eleven/) في الطرفية من الكود التالي مع استبدال site-directory بمسار الموقع المراد:


1
cd /site-directory/

وأنشيء ملفاً باسم package.json وهو الذي تحدد فيه اسم المشروع (أو الموقع) الذي تعمل عليه والإضافات التي تريد تفعيلها عن طريق كتابة السطور التالية فيه:


1
2
3
4
5
6
7
8
9
10
11
12
{
"name" : "SampleGrunt",

"version" : "0.1.0",

“author" : "Brandon Random",

"
private" : true,
"
devDependencies" : {
"
grunt" : "~0.4.0"
}
}

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

احفظ الملف واكتب الأمر التالي لتنصيب التغييرات في node (في نفس مسار الملف):


1
npm install

ثم ننشيء ملف Gruntfile.js في نفس المسار السابق ذكره (لاحظ أننا لن نغير المسار طيلة عملية التنصيب هنا ما لم أذكر عكس ذلك):


1
2
3
4
5
6
module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
grunt.registerTask('default', []);
};

ومجدداً نحفظ التغييرات عن طريق install npm، هكذا أصبحت أداة Grunt جاهزة للعمل على مشروعك، لكن إذا فتحت الأداة الآن لن تعمل لأننا لم نحدد إضافات بعد.

إضافة grunt-contrib-concat لدمج ملفات JS

سنبدأ بإضافة grunt-contrib-concat لدمج ملفات JS في ملف واحد، أولاً ننصب الإضافة:


1
npm install grunt-contrib-concat --save-dev

أمر –save-dev يضيف الإضافة تلقائياً إلى ملف package.json دون الحاجة إلى التعديل فيه، إذا فتحت الملف الآن ستجد اسم الإضافة الجديدة:


1
2
3
4
5
6
7
8
9
10
{
"name" : "SampleGrunt",
"version" : "0.1.0",
“author" : "Brandon Random",
"
private" : true,
"
devDependencies": {
"
grunt": "~0.4.1",
"
grunt-contrib-concat": "~0.3.0"
}
}

والآن سنعدل في ملف Gruntfile.js لتحديد خيارات الإضافة، أولاً سنضيف السطر التالي في أعلى الملف فوق grunt.initconfig مباشرة لاستدعاء الإضافة:


1
require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);

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

ثم نضيف الخيارات التي تختلف حسب كل إضافة، بعد سطر pkg: grunt.file.readJSON(‘package.json’) ونحفظ الملف:


1
2
3
4
5
6
7
8
9
concat: {
dist: {
src: [
'js/libs/*.js', // All JS in the libs folder
'js/global.js'  // This specific file
],
dest: ‘js/build/production.js’,
}
}

حيث نحدد في src: ملفات الجافاسكريبت التي نريد دمجها، في المثال السابق حددنا كل الملفات الموجودة في مستند libs بالإضافة إلى ملف global.js الموجود في مستند js، والملف الناتج يتحدد مساره في dest: كما نرى، وهو ما سنستدعيه في ملف index.php للموقع بدلاً من استدعاء الملفات الأصلية المدمجة.

إضافة grunt-contrib-mincss لدمج ملفات CSS

لدمج ملفات CSS في ملف واحد سنستعمل Mincss، ننصب الإضافة كما في المرة السابقة عبر الطرفية:


1
npm install grunt-contrib-mincss –save-dev

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


1
2
3
4
5
6
7
mincss: {
compress: {
files: {
"css/output.css": ["css/style.css", "css/mobile/*.css"]
}
}
}

هنا يتم تحديد الملفات، نبدأ أولاً بتحديد اسم ومسار الملف الناتج output.css ثم بين القوسين مسارات الملفات المطلوب دمجها، وكما أسلفنا سابقاً يمكن استخدام * بدلاً من اسماء الملفات للدلالة على اختيار جميع الملفات الموجودة في المسار. طبعاً لا تنسَ استدعاء الملف output.css في الموقع index.php بدلاً من الملفات الأصلية المدمجة.

إضافة grunt-contrib-imagemin لضغط الصور

يضغط الصور من الصيغ المشهورة JPG, PNG وGIF باستخدام أدوات OptiPNG, pngquant, jpegtran وgifsicle.

بعد تنصيب الإضافة كالسابق، نبدأ بتعديل الخيارات وإضافتها إلى ملف gruntfile.js:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
imagemin: {                          // Task
static: {                          // Target
options: {                       // Target options
optimizationLevel: 3,
progressive : true,
},
files: {                         // Dictionary of files
'dist/img.png': 'src/img.png', // 'destination': 'source'
'dist/img.jpg': 'src/img.jpg',
'dist/img.gif': 'src/img.gif'
}
},
dynamic: {                         // Another target
files: [{
expand: true,                  // Enable dynamic expansion
cwd: 'src/',                   // Src matches are relative to this path
src: ['**/*.{png,jpg,gif}'],   // Actual patterns to match
dest: 'dist/'                  // Destination path prefix
}]
}
}

يمكنك  استدعاء الملفات عبر المسار الثابت static أو المسار المتغير dynamic، ويمكنك تعديل كيفية ضغط الصور وتغييرها في options. ويمكنك إلقاء نظرة على مقالة كيف تقلل من حجم الصور مع الحفاظ على جودتها لتحقيق تصفح سريع؟ لمزيد من الفائدة.

تشغيل الإضافات

لكن انتظر، لم تعمل الإضافات بعد ! نعم، إذ أنها لن تعمل إلا بعد تشغيل الأداة في الطرفية Terminal:


1
grunt

هل هذا يعني أنني سأحتاج لتشغيل الأداة في كل مرة أعدل في ملفات الموقع؟

ليس بالضرورة، إذ أن هناك إضافة تعمل على مراقبة التغييرات في الملفات والتحديث، ألا وهي إضافة grunt-contrib-watch:


1
npm install grunt-contrib-watch –save-dev

وخيارات الإضافة في ملف gruntfile.js هي:


1
2
3
4
5
6
7
8
9
10
11
12
13
watch: {
JS: {
files: ['js/*.js'],
tasks: ['concat', 'uglify']
},
CSS:{
files: ['css/*.css'],
tasks: ['mincss']
},
img:{
files: ['images/*.png', 'images/*.jpg', 'images/*.gif'],
tasks: ['Imagemin']
}

نلاحظ أن هناك أقسام مخصصة لكل نوع من الملفات، حيث files هي الملفات التي يتم مراقبة التغييرات فيها وtasks هي المهام التي يتم اجراؤها عند التغيير. لا تنس اجراء الأمر grunt في الطرفية.

في النهاية هذا هو محتوى الملف gruntfile.js بعد الإضافات السابقة.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
module.exports = function(grunt){

require(“matchdep”).filterDev(“grunt-*).forEach(grunt.loadNpmTasks);

grunt.initConfig({

pkg: grunt.file.readJSON(package.json)

concat: {
dist: {
src: [
'js/libs/*.js', // All JS in the libs folder
'js/global.js'  // This specific file
],
dest: ‘js/build/production.js,
}
}

mincss: {
compress: {
files: {
“css/output.css: ["css/style.css", "css/mobile/*.css"]
}
}
}

imagemin: {                          // Task
static: {                          // Target
options: {                       // Target options
optimizationLevel: 3,
progressive : true,
},
files: {                         // Dictionary of files
‘dist/img.png: ‘src/img.png, // ‘destination’: ‘source’
‘dist/img.jpg: ‘src/img.jpg,
‘dist/img.gif: ‘src/img.gif
}
},

dynamic: {                         // Another target

files: [{
expand: true,                  // Enable dynamic expansion
cwd: 'src/',                   // Src matches are relative to this path
src: ['**/*.{png,jpg,gif}'],   // Actual patterns to match
dest: ‘dist/’                  // Destination path prefix
}]
}

}

watch: {
JS: {
files: ['js/*.js'],
tasks: ['concat', 'uglify']
},

CSS:{
files: ['css/*.css'],
tasks: ['mincss']
},
img:{
files: ['images/*.png', 'images/*.jpg', 'images/*.gif'],
tasks: ['Imagemin']
}
});
grunt.registerTask(default, []);
};

لا ننسى أن هناك تشكيلة كبيرة من إضافات Grunt مثل HTMLHint لتصحيح ملفات HTML والتأكد من مطابقتها لمعايير W3C، وgrunt-contrib-uglify لضغط ملفات الجافاسكريبت والعديد من الإضافات توفرها Grunt. ما يميز Grunt أيضاً هو تقرير الأخطاء، إذ لو حدث خطاً ما في تنفيذ المهام المطلوبة تخبرك بالسبب في الطرفية مباشرة دون الحاجة للذهاب إلى ملفات التقارير والبحث عن الخطأ.

المصادر

عن كاتب المقال

هبة فريد
مصممة ومطورة مواقع، مستخدمة ومشجعة كبيرة لـ Linux والبرمجيات المفتوحة، مهتمة بمعرفة كل جديد في عالم تطوير الويب، ولدي خبرة في Wordpress وJoomla، أمارس تصميم الجرافيكس ببرنامج Gimp وامارس هواية الرسم المحببة لي في برنامج الرسم المتجهي Inkscape، أؤمن بأن "سأصير يوماً ما أريد".


كل مقالات الكاتب

اترك تعليقك على المقال 8 تعليقات

  • احمد سعيد

    يا ريت لو فى شرح للـ nod.js مفصل اكتر الاول علشان الناس تقدر تستوعب اسهل
    دة شرح للبلاج إن قبل قبل المنصة نفسها
    شكراااااا جدا

    • هبة فريد

      نقطة جيدة، سيتم أخذها في الاعتبار (:

  • بالتوفيق 🙂

  • مصطفى اسماعيل

    مقال جميل جدا 🙂
    إضافة grunt-contrib-mincss لم تعد تعمل بسبب تغير اسمها إلى grunt-contrib-cssmin

    • هبة فريد

      شكراً لك على التنبيه (: ، جاري التعديل

  • موضوع رائع و جميل بس انا كان عندى سؤال بسيط اوى مش عارف اذا كان ليه علاقة ..لكن انا اجيد HTML/CSS و محتاج اتعلم لغة الجافا سكريبت و اللغات اللى اتعامل بيها مع قواعد البيانات و السيرفرات …. يعنى محتاج اكمل الباكيدج لاى مصمم اعمل ايه و ابدا منين …..تحياتى

  • بصراحة شرحة أكثر من رائع وتسلم يمينك .. كنت معتقد اني مش هلاقي شرح عربي ابداً ..