سوپرشارژ کردن فایل gruntfile شما

چگونه از پیکربندی ساخت خود حداکثر استفاده را ببرید

معرفی

اگر دنیای Grunt برای شما جدید است، یک مکان ایده آل برای شروع مقاله عالی کریس کویر " Grunt برای افرادی که فکر می کنند چیزهایی مانند Grunt عجیب و غریب و سخت هستند " است. پس از معرفی کریس، شما پروژه Grunt خود را راه اندازی کرده اید و بخشی از قدرت ارائه شده توسط Grunt را چشیده اید.

در این مقاله، ما بر روی کارهایی که افزونه‌های Grunt متعدد با کد پروژه واقعی شما انجام می‌دهند تمرکز نمی‌کنیم، بلکه بر روی خود فرآیند ساخت Grunt تمرکز می‌کنیم. ما به شما ایده های عملی در مورد:

  • چگونه Gruntfile خود را مرتب و مرتب نگه دارید،
  • چگونه زمان ساخت خود را به طور چشمگیری بهبود بخشید،
  • و نحوه اطلاع رسانی هنگام وقوع ساخت.

زمان برای یک سلب مسئولیت سریع: Grunt تنها یکی از بسیاری از ابزارهایی است که می توانید برای انجام کار استفاده کنید. اگر Gulp بیشتر سبک شماست، عالی است! اگر پس از بررسی گزینه‌های موجود، همچنان می‌خواهید زنجیره ابزار خود را بسازید، این نیز خوب است! به دلیل اکوسیستم قوی و پایگاه کاربری قدیمی آن، ما برای این مقاله تمرکز بر Grunt را انتخاب کردیم.

سازماندهی Gruntfile خود

چه تعداد زیادی پلاگین Grunt داشته باشید و چه مجبور باشید کارهای دستی زیادی را در Gruntfile خود بنویسید، می تواند به سرعت بسیار سخت و سخت شود. خوشبختانه، تعداد زیادی افزونه وجود دارد که دقیقاً بر روی این مشکل تمرکز می کنند: Gruntfile خود را دوباره مرتب و مرتب کنید.

Gruntfile، قبل از بهینه سازی

قبل از انجام هر گونه بهینه سازی روی آن، Gruntfile ما چگونه به نظر می رسد:

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      dist: {
        src: ['src/js/jquery.js','src/js/intro.js', 'src/js/main.js', 'src/js/outro.js'],
        dest: 'dist/build.js',
      }
    },
    uglify: {
      dist: {
        files: {
          'dist/build.min.js': ['dist/build.js']
        }
      }
    },
    imagemin: {
      options: {
        cache: false
      },

      dist: {
        files: [{
          expand: true,
          cwd: 'src/',
          src: ['**/*.{png,jpg,gif}'],
          dest: 'dist/'
        }]
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-imagemin');

  grunt.registerTask('default', ['concat', 'uglify', 'imagemin']);

};

اگر اکنون می گویید «هی! انتظار خیلی بدتری داشتم! این در واقع قابل نگهداری است!»، احتمالاً حق با شماست. برای سادگی، ما فقط سه افزونه را بدون سفارشی سازی زیاد اضافه کرده ایم. استفاده از Gruntfile تولیدی واقعی برای ساخت یک پروژه با اندازه متوسط ​​نیاز به پیمایش بی‌نهایت در این مقاله دارد. پس بیایید ببینیم چه کاری می توانیم انجام دهیم!

پلاگین های Grunt خود را خودکار بارگیری کنید

هنگام اضافه کردن یک افزونه Grunt جدید که می خواهید به پروژه خود استفاده کنید، باید آن را به فایل package.json خود به عنوان وابستگی npm اضافه کنید و سپس آن را در Gruntfile بارگذاری کنید. برای افزونه " grunt-contrib-concat " ممکن است به شکل زیر باشد:

// tell Grunt to load that plugin
grunt.loadNpmTasks('grunt-contrib-concat');

اگر اکنون افزونه را از طریق npm حذف کنید و package.json خود را به روز کنید، اما فراموش کنید Gruntfile خود را به روز کنید، بیلد شما خراب می شود. اینجاست که افزونه های جذاب load-grunt-tasks به کمک می آیند.

در حالی که قبلا، ما مجبور بودیم پلاگین های Grunt خود را به صورت دستی بارگیری کنیم، مانند:

grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-imagemin');

با load-grunt-tasks، می توانید آن را در یک خط زیر جمع کنید:

require('load-grunt-tasks')(grunt);

پس از نیاز به افزونه، فایل package.json شما را تجزیه و تحلیل می کند، مشخص می کند که کدام یک از وابستگی ها پلاگین های Grunt هستند و همه آنها را به طور خودکار بارگذاری می کند.

تقسیم کردن پیکربندی Grunt به فایل‌های مختلف

load-grunt-tasks فایل Grunt شما را از نظر کد و پیچیدگی اندکی کوچک می کند، اما با پیکربندی یک برنامه بزرگ، همچنان به یک فایل بسیار بزرگ تبدیل می شود. اینجاست که load-grunt-config وارد عمل می شود. load-grunt-config به شما امکان می دهد پیکربندی Gruntfile خود را بر اساس وظیفه تقسیم کنید. علاوه بر این، وظایف load-grunt و عملکرد آن را در بر می گیرد!

اما مهم است: تقسیم Gruntfile شما ممکن است همیشه برای هر موقعیتی کارساز نباشد. اگر تنظیمات مشترک زیادی بین وظایف خود دارید (یعنی از الگوی Grunt زیادی استفاده می کنید)، باید کمی مراقب باشید.

با load-grunt-config ، Gruntfile.js شما به شکل زیر خواهد بود:

module.exports = function(grunt) {
  require('load-grunt-config')(grunt);
};

بله، واقعاً همین است، کل پرونده! اکنون تنظیمات وظیفه ما در کجا زندگی می کنند؟

یک پوشه به نام grunt/ در دایرکتوری Gruntfile خود ایجاد کنید. به طور پیش‌فرض، این افزونه شامل فایل‌هایی در آن پوشه است که با نام کاری که می‌خواهید استفاده کنید مطابقت دارد. ساختار دایرکتوری ما باید به شکل زیر باشد:

- myproject/
-- Gruntfile.js
-- grunt/
--- concat.js
--- uglify.js
--- imagemin.js

بیایید اکنون پیکربندی وظیفه هر یک از وظایف خود را مستقیماً در پرونده های مربوطه قرار دهیم (می بینید که اینها عمدتاً فقط کپی و چسباندن از Gruntfile اصلی در یک ساختار جدید هستند):

grunt/concat.js

module.exports = {
  dist: {
    src: ['src/js/jquery.js', 'src/js/intro.js', 'src/js/main.js', 'src/js/outro.js'],
    dest: 'dist/build.js',
  }
};

grunt/uglify.js

module.exports = {
  dist: {
    files: {
      'dist/build.min.js': ['dist/build.js']
    }
  }
};

grunt/imagemin.js

module.exports = {
  options: {
    cache: false
  },

  dist: {
    files: [{
      expand: true,
      cwd: 'src/',
      src: ['**/*.{png,jpg,gif}'],
      dest: 'dist/'
    }]
  }
};

اگر بلوک‌های پیکربندی جاوا اسکریپت واقعاً مورد توجه شما نیستند، load-grunt-tasks حتی به شما امکان می‌دهد به جای آن از نحو YAML یا CoffeeScript استفاده کنید. بیایید فایل مورد نیاز نهایی خود را در YAML بنویسیم - فایل " مستعار ". این یک فایل ویژه است که نام مستعار وظیفه را ثبت می کند، کاری که قبلاً باید به عنوان بخشی از Gruntfile از طریق تابع registerTask انجام می دادیم. این مال ماست:

grunt/aliases.yaml

default:
  - 'concat'
  - 'uglify'
  - 'imagemin'

و بس! دستور زیر را در ترمینال خود اجرا کنید:

$ grunt

اگر همه چیز کار کرد، اکنون به کار "پیش فرض" نگاه می کند و همه چیز را به ترتیب اجرا می کند. اکنون که Gruntfile اصلی خود را به سه خط کد کاهش دادیم، هرگز نیازی به لمس کردن و خارجی‌سازی هر پیکربندی وظیفه نداریم، اینجا تمام شده است. اما مرد، هنوز هم ساختن همه چیز بسیار کند است. بیایید ببینیم چه کاری می توانیم برای بهبود آن انجام دهیم.

زمان ساخت خود را به حداقل برسانید

اگرچه زمان اجرا و عملکرد زمان بارگذاری برنامه وب شما بسیار مهمتر از زمان لازم برای اجرای یک ساخت است، ساخت آهسته همچنان مشکل ساز است. اجرای بیلدهای خودکار با افزونه‌هایی مانند grunt-contrib-watch یا پس از انجام یک Git به اندازه کافی سریع دشوار می‌شود، و یک پنالتی برای اجرای واقعی بیلد معرفی می‌کند – هر چه زمان ساخت سریع‌تر باشد، گردش کار شما چابک‌تر می‌شود. اگر ساخت بیلد شما بیش از 10 دقیقه طول بکشد، فقط زمانی که کاملاً مجبور باشید ساخت را اجرا خواهید کرد و در حین کار برای گرفتن قهوه سرگردان خواهید بود. این یک قاتل بهره وری است. ما چیزهایی برای سرعت بخشیدن داریم.

فقط فایل هایی بسازید که واقعاً تغییر کرده اند: grunt-newer

پس از ساخت اولیه سایت خود، این احتمال وجود دارد که شما فقط چند فایل را در پروژه لمس کرده باشید که دوباره به ساختن بپردازید. بیایید بگوییم که در مثال ما، شما یک تصویر را در دایرکتوری src/img/ تغییر دادید - اجرای imagemin برای بهینه سازی مجدد تصاویر منطقی است، اما فقط برای آن تصویر واحد - و البته، اجرای مجدد concat و uglify فقط هدر می رود. چرخه های CPU با ارزش

البته، همیشه می‌توانید $ grunt imagemin به جای $ grunt از ترمینال خود اجرا کنید تا فقط به صورت انتخابی یک کار در دستتان را اجرا کنید، اما راه هوشمندتری وجود دارد. بهش میگن grunt-niwer .

Grunt-newer یک کش محلی دارد که در آن اطلاعات مربوط به فایل هایی که واقعاً تغییر کرده اند را ذخیره می کند و فقط وظایف شما را برای فایل هایی که در واقع تغییر کرده اند، اجرا می کند. بیایید نگاهی به نحوه فعال کردن آن بیندازیم.

فایل aliases.yaml ما را به خاطر دارید؟ آن را از این تغییر دهید:

default:
  - 'concat'
  - 'uglify'
  - 'imagemin'

به این:

default:
  - 'newer:concat'
  - 'newer:uglify'
  - 'newer:imagemin'

به سادگی اضافه کردن «جدیدتر:» به هر یک از وظایف خود ، ابتدا فایل‌های مبدأ و مقصد شما را از طریق افزونه grunt-newer هدایت می‌کند، که سپس تعیین می‌کند، در صورت وجود، برای کدام فایل‌ها، وظیفه اجرا شود .

چندین کار را به صورت موازی اجرا کنید: grunt-concurrent

grunt-concurrent پلاگینی است که وقتی کارهای زیادی داشته باشید که مستقل از یکدیگر هستند و زمان زیادی را صرف می کنند واقعاً مفید می شود. از تعداد پردازنده های موجود در دستگاه شما استفاده می کند و چندین کار را به صورت موازی انجام می دهد.

بهترین از همه، پیکربندی آن بسیار ساده است. با فرض استفاده از load-grunt-config، فایل جدید زیر را ایجاد کنید:

grunt/concurrent.js

module.exports = {
  first: ['concat'],
  second: ['uglify', 'imagemin']
};

ما فقط روی آهنگ‌های اجرای موازی با نام‌های « اول » و « دوم » تنظیم می‌کنیم. وظیفه concat باید ابتدا اجرا شود، و در مثال ما هیچ چیز دیگری برای اجرا در این بین وجود ندارد. در آهنگ دوم ما، هم uglify و هم imagemin را قرار دادیم، زیرا این دو از یکدیگر مستقل هستند و هر دو زمان قابل توجهی می‌برند.

این به خودی خود هنوز کاری انجام نمی دهد. ما باید نام مستعار وظیفه پیش فرض خود را تغییر دهیم تا به جای کارهای مستقیم، به کارهای همزمان اشاره کنیم. در اینجا محتوای جدید grunt/aliases.yaml آمده است:

default:
  - 'concurrent:first'
  - 'concurrent:second'

اگر اکنون بیلد grunt خود را دوباره اجرا کنید، پلاگین همزمان ابتدا وظیفه concat را اجرا می کند و سپس دو رشته را روی دو هسته CPU مختلف ایجاد می کند تا هم imagemin و هم uglify را به صورت موازی اجرا کند. آری

البته یک توصیه: به احتمال زیاد در مثال اصلی ما، grunt-concurrent سرعت ساخت شما را به میزان قابل توجهی افزایش نمی دهد. دلیل این امر، سربار ایجاد شده توسط تخم ریزی نمونه های مختلف Grunt در رشته های مختلف است: در مورد من، حداقل +300ms pro spawn است.

چقدر زمان برد؟ زمان خرخر

اکنون که ما در حال بهینه سازی هر یک از وظایف خود هستیم، درک اینکه هر کار فردی چقدر زمان نیاز دارد تا اجرا شود، واقعا مفید خواهد بود. خوشبختانه، یک افزونه برای آن نیز وجود دارد: time-grunt .

time-grunt یک پلاگین grunt کلاسیک نیست که آن را به عنوان وظیفه npm بارگیری کنید، بلکه افزونه‌ای است که مستقیماً در آن قرار می‌دهید، شبیه به load-grunt-config. ما یک نیاز برای time-grunt را به Gruntfile خود اضافه می کنیم، درست مانند کاری که با load-grunt-config انجام دادیم. Gruntfile ما اکنون باید به این شکل باشد:

module.exports = function(grunt) {

  // measures the time each task takes
  require('time-grunt')(grunt);

  // load grunt config
  require('load-grunt-config')(grunt);

};

و متأسفم که ناامید می‌شوم، اما همین - سعی کنید Grunt را از ترمینال خود دوباره اجرا کنید و برای هر کار (و همچنین کل ساخت)، باید یک پانل اطلاعات با فرمت زیبا در زمان اجرا ببینید:

زمان غرغر کردن

اعلان های خودکار سیستم

اکنون که یک بیلد Grunt به شدت بهینه شده دارید که به سرعت اجرا می شود، و به شرطی که به روشی آن را به صورت خودکار بسازید (یعنی با تماشای فایل ها با grunt-contrib-watch، یا پس از commits)، عالی نمی شد اگر سیستم شما می توانست هنگامی که ساخت جدید شما آماده مصرف است یا زمانی که اتفاق بدی افتاده است، به شما اطلاع می دهد؟ با grunt-notify ملاقات کنید.

به‌طور پیش‌فرض، grunt-notify اعلان‌های خودکار را برای همه خطاها و هشدارهای Grunt با استفاده از هر سیستم اعلان موجود در سیستم عامل شما ارائه می‌کند: Growl برای OS X یا Windows، Mountain Lion's and Mavericks' Notification Center، و Notify-send. به طور شگفت انگیزی، تنها چیزی که برای دریافت این قابلیت نیاز دارید، نصب پلاگین از npm و بارگذاری آن در Gruntfile است (به یاد داشته باشید، اگر از grunt-load-config در بالا استفاده می کنید، این مرحله خودکار است!).

بسته به سیستم عامل شما چگونه به نظر می رسد:

اعلام کردن

علاوه بر خطاها و هشدارها، اجازه دهید آن را طوری پیکربندی کنیم که پس از اتمام آخرین کار ما اجرا شود. با فرض اینکه از grunt-load-config برای تقسیم وظایف در بین فایل ها استفاده می کنید، این فایلی است که به آن نیاز داریم:

grunt/notify.js

module.exports = {
  imagemin: {
    options: {
      title: 'Build complete',  // optional
        message: '<%= pkg.name %> build finished successfully.' //required
      }
    }
  }
}

در سطح اول شیء پیکربندی ما، کلید باید با نام کاری که می‌خواهیم آن را به آن متصل کنیم مطابقت داشته باشد. این مثال باعث می شود که پیام بلافاصله پس از اجرای وظیفه imagemin ظاهر شود که آخرین مورد در زنجیره ساخت ما است.

همه را جمع کردن

اگر از بالا دنبال کرده اید، اکنون صاحب مفتخر فرآیند ساختی هستید که بسیار مرتب و منظم است، به دلیل موازی سازی و پردازش انتخابی فوق العاده سریع است و در صورت بروز مشکل به شما اطلاع می دهد.

اگر گوهر دیگری پیدا کردید که Grunt و افزونه‌های آن را بیشتر بهبود می‌بخشد، لطفاً به ما اطلاع دهید! تا آن زمان، غرغر مبارک!

به روز رسانی (2/14/2014): برای گرفتن یک کپی از نمونه کامل پروژه Grunt، اینجا را کلیک کنید .