關於 Grunt JS
C 語言有 Make、Java 有 Ant、Node.JS 也需要一個 Task Runner,那就是 Grunt JS 囉。Grunt JS 提供我們一個可以管理與實現自動化工作的框架,而且還提供了很多好用的 Plugins。當我們開發的 Web 產品要上線時,常為了可以將低頻寬與增進效能,都會對程式碼進行一些優化工作。今天我們要介紹如何使用 Grunt JS 幫我們 Web Project 中的 JavaScript 與 CSS 程式碼進行最小化(包含產生 JavaScript Source Maps)。
安裝 Node.JS
安裝請直接到 Node.JS 官方網站下載即可,如果是 Linux 可直接透過套件管理進行安裝,這裡就不多做說明囉,以下是 Windows 的安裝畫面:
透過 NPM 安裝 GruntJS
安裝好 Node.JS 可以直接透過 NPM 安裝 GruntJS,請輸入以下命令安裝 GruntJS Cli:
npm install -g grunt-cli
接著我們建立一支 package.json 專案檔,由於我們待會需要最小化 CSS 與 JavaScript,因此需要引用相關的 Plugins,package.json 內容如下:
{ "name": "my-web-project", "version": "1.0.0", "description": "GruntJS build task", "private": true, "devDependencies": { "grunt": "~0.4.5", "grunt-contrib-uglify": "~0.5.0", "grunt-contrib-cssmin": "~0.10.0" } }
上述檔案中我們引用了 uglify 與 cssmin 這兩個套件,然而「"private": true」是聲明這個 package.json 並不是要進行散佈是用,不這樣宣告執行時會有一些不必要的 Warning Message 產生。
接著輸入以下命令初始化專案,過程中會一併自動下載與安裝相依套件:
npm install
建立 Task Script 執行腳本
C 語言 make 建構腳本透過 makefile 檔案,Java Ant 建構腳本透過 build.xml,一樣的道理 GruntJS 也需要建構腳本檔,也就是 Gruntfile.js 檔案,我們先來看一下我們的檔案內容長什麼樣子,如下:
module.exports = function(grunt) { // Project configuration. grunt.initConfig({ // 引用 package.json 中的參數 pkg: grunt.file.readJSON('package.json'), // 設定 JavaScript 壓縮 task uglify: { myTask: { options: { // 設定壓縮後檔頭要插入的註解 banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n', // 使用 SourceMap 並且將 JS Source 與 Map 檔案放在一起 sourceMap: true, sourceMapIncludeSources: true }, files: [ { expand: true, cwd: 'js', // 將不是 .min.js 的檔案全部進行壓縮 src: ['**/*.js', '!*.min.js'], dest: 'js' } ] } }, // 設定 CSS 壓縮 task cssmin: { minify: { expand: true, cwd: 'css', src: ['**/*.css', '!*.min.css'], dest: 'css' } } }); // Load the plugin. grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin'); // Default task(s). grunt.registerTask('default', ['uglify', 'cssmin']); };
大概說明一下 Gruntfile.js 中的設定,其中有兩個主要的 task uglify 與 cssmin,分別用在處理 JavaScript 與 CSS 的最小化工作。由於一開始透過「pkg: grunt.file.readJSON('package.json')」引入了 package.json 中的參數,因此我們之後可以透過 <%= pkg.XXXX %> 來讀取參數值。在上述的設定中,dest 與 cwd 的目錄相同,表示這些動作會覆蓋原本的檔案,如果您不想覆蓋原檔而想要產生新的檔案,也可以加入「ext: '.min.js'」這樣的設定來達成。上述的 SourceMap 設定中,我們將程式原始碼與 Map File 放在一起,這樣上線的時候只要直接移除 Map File 即可,比較方便。
由於 JavaScript 經過最小化後,程式中的變數與函數名稱會被簡化,程式碼變得難以閱讀。為了除錯方便,產生了 Source Maps 技術。Source Maps 是一種讓最小化後的程式碼可以直接連接原始碼的一種技術,為了讓開發者可以直接在最小化後的程式碼中進行除錯,透過 Map File 可以還原程式碼,在大部分主流的瀏覽器除錯工具都有提供這樣的功能。想要讓瀏覽器載入 Map File 有以下兩種方式:
- 在 JavaScript 中加入特定註解「//# sourceMappingURL=/path/to/file.js.map」
- 透過 HTTP Header 設定 Map File 位置「X-SourceMap: /path/to/file.js.map」
GruntJS Uglify 是使用第一種方法載入 Map File,我們可以看到被最小化後的 JS 尾端會被自動加入上述的註解,好告訴瀏覽器到哪裡載入 Map File,其他 Souce Maps 詳細的資訊可以參考這份文件。Chrome 要開啟 Source Maps 功能只要在開發者工具中的設定選項,將「Enable JavaScript source maps」打勾即可,如下: