Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

用 Grunt 排定壓縮靜態資源任務

這次公司的網站更新,同時又接觸了新技能:Grunt。

以前在執行靜態檔案最佳化,都是透過快取外掛 W3 Total Cache 協助執行,但是有時最小化之後會出錯 (可能在最小化 JavaScript 時,變數或函式互相干擾),因此想要自己可以控制最小化的任務。這次執行最小化後,將原本的 CSS 檔案縮小了 33% (70 kb → 47kb),JavaScript 檔案也都縮小了 30-50%。

如同官方網站的次標題:the JavaScript Task Runner,Grunt 是一款奠基於 Node.js 之上的任務管理套件。在使用 Grunt 前,需要先能夠從 NPM 中安裝套件。關於 NPM 的使用方法,可以參考這篇文章,這裡就不再贅述。

安裝 Grunt

在開始使用 Grunt 以前,需要先透過 NPM 安裝 Grunt。打開終端機/命令提示字元,並輸入 NPM 的安裝指令,在全域安裝 Grunt CLI。

npm install -g grunt-cli

接著在你的專案資料夾中,編輯你的 package.json 檔案,加入以下相依套件:

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-jshint": "~0.10.0",
    "grunt-contrib-nodeunit": "~0.4.1",
    "grunt-contrib-uglify": "~0.5.0"
  }
}

執行 npm install 指令,將相依套件安裝至專案資料夾中。

編輯 Gruntfile.js 檔案

module.exports = function(grunt) {
	// Project configuration.
	grunt.initConfig( {
		pkg: grunt.file.readJSON( 'package.json' ),
	} );
};

grunt.file.readJSON('package.json') 這項指令是用以讀取 package.json 中的套件中繼資料。

接著加入任務名稱,並載入任務套件。以這次的範例為例,我想要最小化我的靜態話資源 (CSS 與 JavaScript 檔案),因此我使用了 terser、autoprefixer、sass 與 cssmin。

module.exports = function ( grunt ) {
	// Project configuration.
	grunt.initConfig( {
		pkg: grunt.file.readJSON( 'package.json' ),
	} );

	// Load the plugin that provides the "uglify" task.
	grunt.loadNpmTasks( 'grunt-terser' );
	grunt.loadNpmTasks( 'grunt-autoprefixer' );
	grunt.loadNpmTasks( 'grunt-contrib-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
};

同時記得在終端機/命令提示字元中執行 NPM 指令,安裝上述套件。

npm install --save-dev grunt-terser grunt-autoprefixer grunt-contrib-sass grunt-contrib-cssmin

加入任務組態

Terser 是一款最小化 JavaScript 檔案的套件,在 Gruntfile.js 中加入選項與組態:

module.exports = function ( grunt ) {
	// Project configuration.
	grunt.initConfig( {
		pkg: grunt.file.readJSON( 'package.json' ),
		terser: {
			options: {
				mangle: {
					toplevel: true,
				},
			},
			build: {
				files: [
					{
						expand: true,
						src: [ 'js/*.js', '!js/*.min.js' ],
						ext: '.min.js',
					},
				],
			},
		},
	} );

	// Load the plugin that provides the "uglify" task.
	grunt.loadNpmTasks( 'grunt-terser' );
	grunt.loadNpmTasks( 'grunt-autoprefixer' );
	grunt.loadNpmTasks( 'grunt-contrib-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );

	// Default task(s).
	grunt.registerTask( 'default', [
		'terser',
		'sass',
		'autoprefixer',
		'cssmin',
	] );
};

mangle 是用來將 JavaScript 中的變數與屬性改以單個字母表現,例如 let findMyCookie 會被壓縮為 let f。這種作法可以有效的減少 JavaScript 的檔案大小。

toplevel 則是讓 mangle 的適用範圍只及於最上層的變數與函式名稱。

build 中則是指定要適用的檔案範圍,expand: true 會去自動偵測路徑,js/*.js 代表要包含 js 這個資料夾中的所有副檔名為 js 的檔案,而 !js/*.min.js 代表不要再去壓縮是壓縮檔的檔案,例如已經壓縮過的第三方套件。ext 是檔案匯出後使用的副檔名。

接著加入其他任務的組態:

module.exports = function ( grunt ) {
	// Project configuration.
	grunt.initConfig( {
		pkg: grunt.file.readJSON( 'package.json' ),
		terser: {
			options: {
				mangle: {
					toplevel: true,
				},
			},
			build: {
				files: [
					{
						expand: true,
						src: [ 'js/*.js', '!js/*.min.js' ],
						ext: '.min.js',
					},
				],
			},
		},
		sass: {
			dist: {
				files: {
					'style.css': 'sass/style.scss',
				},
			},
		},
		autoprefixer: {
			your_target: {
				files: [
					{
						expand: true,
						src: [
							'style.css',
							'assets/css/*.css',
							'!**/*.min.css',
							'!assets/css/*.min.css',
						],
						ext: '.min.css',
					},
				],
			},
		},
		cssmin: {
			targets: {
				files: [
					{
						expand: true,
						src: [
							'style.css',
							'assets/css/*.css',
							'!**/*.min.css',
							'!assets/css/*.min.css',
						],
						ext: '.min.css',
					},
				],
			},
		},
	} );

	// Load the plugin that provides the "uglify" task.
	grunt.loadNpmTasks( 'grunt-terser' );
	grunt.loadNpmTasks( 'grunt-autoprefixer' );
	grunt.loadNpmTasks( 'grunt-contrib-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
};

最後再利用 grunt.registerTask(); 安排任務順序:

module.exports = function ( grunt ) {
	// Project configuration.
	grunt.initConfig( {
		pkg: grunt.file.readJSON( 'package.json' ),
		terser: {
			options: {
				mangle: {
					toplevel: true,
				},
			},
			build: {
				files: [
					{
						expand: true,
						src: [ 'js/*.js', '!js/*.min.js' ],
						ext: '.min.js',
					},
				],
			},
		},
		sass: {
			dist: {
				files: {
					'style.css': 'sass/style.scss',
				},
			},
		},
		autoprefixer: {
			options: {},
			your_target: {
				files: [
					{
						expand: true,
						src: [
							'style.css',
							'assets/css/*.css',
							'!**/*.min.css',
							'!assets/css/*.min.css',
						],
						ext: '.min.css',
					},
				],
			},
		},
		cssmin: {
			options: {},
			targets: {
				files: [
					{
						expand: true,
						src: [
							'style.css',
							'assets/css/*.css',
							'!**/*.min.css',
							'!assets/css/*.min.css',
						],
						ext: '.min.css',
					},
				],
			},
		},
	} );

	// Load the plugin that provides the "uglify" task.
	grunt.loadNpmTasks( 'grunt-terser' );
	grunt.loadNpmTasks( 'grunt-autoprefixer' );
	grunt.loadNpmTasks( 'grunt-contrib-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );

	// Default task(s).
	grunt.registerTask( 'default', [
		'terser',
		'sass',
		'autoprefixer',
		'cssmin',
	] );
};

最後,只要在終端機/命令提示字元中,執行 grunt 指令,接下來就會由 Grunt 執行剩下的任務工序。

結語

Grunt 在 Developer Roadmap 中並沒有被提到,但其中有提到類似概念的 Gulp。

透過 Grunt 的方法,可以將本來最小化 JavaScript 與 CSS 的工作變得更輕鬆。但是如果要真的將整個流程更有組織、更全面,看樣子還是需要多點一些 Webpack 的技能。

Eric Chuang
Eric Chuang

正職是廣告行銷人員,因為 Google Tag Manager 的關係開始踏入網站製作的領域,進一步把 WordPress 當成 PHP + HTML + CSS + JavaScript 的學習教材。此外,因為工作的關係,曾經用 Automattic 的 Underscores (_s) 替客戶與公司官網進行全客製化佈景主題開發。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料