Visual Studio 2015 舊版 MVC 專案的 Minify 議題(使用 Grunt)
- 2015-05-18
- 9117
- 0
在新版的 Visual Studio (VS2015) 中不再使用舊有的 BundleConfig 來優化 CSS 與 Javascript,雖然以往設定在 BundleConfig 的機制依然可以跑在舊有專案上,但有參加 demo 的 ASP.NET MVC 課程或 twMVC 活動的朋友們都聽過我們分析既有的 BundleConfig 設定與功能不足之處,因此我們是完全不用 BundleConfig 來執行 CSS,Javascript Minify ,詳細原因就不需再多談,而且各位客倌看到 VS2015 也拿掉此機制就了解拋棄它絕對是明智的選擇。
不過正所謂「是誰多事種芭蕉」因為當初使用第三方元件完成 CSS,Javascript Minify,所以第三方元件想怎樣就可以怎樣….
就這樣一個殘忍的更新,完全阻斷了我們爽爽用的路,但是升級 VS2015 絕對是必要的,升級後舊有專案的 CSS,Javascript Minify 又該何去何從呢?難道只能在心中咒罵 twMVC 以及 demo ?
出來混總是要還,以往我們一直鼓吹開發者不要用內建的機制現在我想我需要對聽過我課程的朋友們負責,所以本文章的重點就是在於如何讓舊有的 ASP.NET MVC5 專案(ASP.NET 4.X)可以順利的改用 Visual Studio 2015 持續開發。
(因為團隊絕對不會因為這單純的需求去把 ASP.NET MVC5 升級到 ASP.NET MVC6,這兩個版本的改變相當的巨大,升級版本要償還的技術債不少。)
再次強調本文章的前提:
- 已改用 Visual Studio 2015。
- ASP.NET MVC6 以下的專案需要持續維護。
- 當初的 ASP.NET MVC 專案並非使用內建的 CSS,Javascript 最佳化機制。
為了避免範例複雜化,本文直接使用預設的 MVC 範本來做示範,如果尚未安裝寫 Web 的利器 Web Essentials 2015 請立即安裝,安裝後在專案上按滑鼠右鍵選擇「加入」即可看到「Grunt and Bower to Project」
點選後專案會新增三個檔案「bower.json」「gruntfile.js」「package.json」
開啟「package.json」,習慣 .NET 開發的朋友您可以先把這檔案想成 NuGet 的 packages.config 反正就是可以在這檔案內定義需要的 Grunt 套件
因為本範例只有示範 CSS 和 Javascript Minify 功能所以只需要載入「grunt-contrib-uglify」「grunt-contrib-cssmin」兩個套件,如下方的寫法:
{ "name": "package", "version": "1.0.0", "private": true, "devDependencies": { "grunt": "0.4.5", "grunt-contrib-uglify": "0.9.1", "grunt-contrib-cssmin": "0.12.3", "grunt-contrib-watch": "0.6.1", "grunt-contrib-imagemin": "0.9.4" }, }
您可以直接複製貼上,但建議自己實際打看看,您會發現 VS 連 package.json 檔案都有實作 Intellisense 實在太威了
demo 覺得最棒的就是可以直接到套件首頁,開始廣泛使用 Open Source 專案後官網是一個很重要的地方,沒有官網根本不會知道套件怎麼使用。
存檔後 VS 就會自動開始裝套件(您可以開啟輸出視窗看到),並且將裝好的套件放置於專案內的「node_modules」資料夾。
(需重新整理與開啟所有檔案才看的到)
套件安裝是小事,現在遇到的問題比較大,在舊版的 MVC 範本中 CSS , Javascript與圖片會分別放置在「Content」「Scripts」與自定義的「img」資料夾,這種分散式的放法很不適合 grunt 的操作,也不適合現在雲端導向的設計,需要調整這些靜態檔案的存放位置,請先開啟一個「wwwroot」資料夾然後將上述資料夾都搬進來,這個資料夾馬上搖身一變,變成了靜態檔案的原始檔位置,還有記得要去刪除已經沒用的「BundleConfig」檔案。
位置調整完畢後請開啟「gruntfile.js」以下是 grunt 套件 Uglift 設定的方式
module.exports = function (grunt) { grunt.initConfig({ uglify: { build: { src: [ 'wwwroot/Scripts/jquery-1.10.2.js', 'wwwroot/Scripts/bootstrap.js', 'wwwroot/Scripts/respond.js', ], dest: 'assets/build.min.js' } }, }); grunt.loadNpmTasks('grunt-contrib-uglify'); };
白話解釋以上設定的功能,將 src 指定的三個 js 檔案作最小化後合併成 build.min.js 檔案,並且放置在 assets 資料夾中,最後的 grunt.loadNpmTasks 是將 uglify 設定進工作內,請存檔後開啟「工作執行器總管」(Alt + Ctrl + \)
挑選 uglify 按滑鼠右鍵選擇「執行」如果看到「Done, without errors.」就表示設定正確並且執行也成功了,重新整理專案後就可以看到 assets 資料夾內有完成 js minify & bundle 的 js 檔案。
再來要將所有的 CSS 合併成單檔,使用 cssmin 套件,以下為設定檔:
cssmin: { add_banner: { files: { 'assets/all.css': [ 'wwwroot/Content/bootstrap.css', 'wwwroot/Content/Site.css', ] }, } },
本設定的功能是將 bootstrap.css 與 site.css 最小化後合併為 all.cs 當然放置目錄也會是「assets」資料夾,記得設定檔的最後要加上
grunt.loadNpmTasks('grunt-contrib-cssmin');
存檔後開啟「工作執行器總管」就可以看到增加了 cssmin Task,一樣滑鼠右鍵選擇執行就可以觸發工作了。
目前為止我們已經成功的利用 grunt 套件完成 css 和 js 最佳化,總算是解了燃眉之急,但開始使用後就一定會覺得怪了,每次都要手動去按不是很麻煩嗎?是的!這真是超級麻煩,所以有一個套件是「grunt-contrib-watch」您可以設定監視的資料夾與觸發的 Task ,請讀者先在 「package.json」加入 watch 套件後,再前往「gruntfile.js」增加設定
watch: { scripts: { files: '**/*.js', tasks: ['uglify'], }, css: { files: '**/*.css', tasks: ['cssmin'], }, }, grunt.loadNpmTasks('grunt-contrib-watch');
(以上設定是監控所有資料夾內的 js 和 css 檔案,建議讀者修改成只監視 wwwroot 資料夾下的檔案)
然後就是發揮 Visual Studio 神奇的地方了,使用「工作執行器總管」點選「watch」按滑鼠右鍵選擇「繫結」→「專案已開啟」
這樣 watch 就會在專案一開的時候就執行發揮監視的能力(如果想要立即測試請直接執行 watch)
終於我們利用 npm & Grunt 讓舊專案重新得到了 CSS 或 Javascrpt 異動後自動最佳化的功能,你以為你戰勝了全新的前端工具嗎?
很可惜的並沒有,ASP.NET MVC6的範本目前還是預覽版,在之前的版本是使用 Grunt 沒錯,但最新的預覽版已經改成使用 Glup ,也不要以為這樣就停止了,其實現在前端世界開始在流行另一套…..所以說前端的世界很可怕,一個套件的流行和沒落都是一覺起來的事情,如果您想和前端打交道請做好心理準備,或是告訴主管這個事實,請公司或團隊內增加一個「前端工程師」吧,相信我現在前端要做的事情絕對不比後端輕鬆,給予一個職位,一個正常的薪水絕對是必須的,最後讓我對「全端工程師」致上最深的敬意。
最後附上完整的 gruntfile.js 設定
/// <binding ProjectOpened='watch' /> /* This file in the main entry point for defining grunt tasks and using grunt plugins. Click here to learn more. http://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409 */ module.exports = function (grunt) { grunt.initConfig({ uglify: { build: { src: [ 'wwwroot/Scripts/jquery-1.10.2.js', 'wwwroot/Scripts/bootstrap.js', 'wwwroot/Scripts/respond.js', ], dest: 'assets/build.min.js' } }, cssmin: { add_banner: { files: { 'assets/all.css': [ 'wwwroot/Content/bootstrap.css', 'wwwroot/Content/Site.css', ] }, } }, watch: { scripts: { files: '**/*.js', tasks: ['uglify'], }, css: { files: '**/*.css', tasks: ['cssmin'], }, }, }); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('minify', ['uglify', 'cssmin']); };
回應討論