June 14, 2017

Frontend Intermediate Course - 作業九

作業九的 GitHub 連結:節省 Request 的極致:一為全,全為一

這次作業要使用 Gulp 來自動化工作流程,作業要求是要將 CSS 跟 JavaScript 檔案都 inline 進 index.html 裡面,不能發出 CSS 跟 JavaScript 的 Request。雖然手動可以做到這件事情,但每次修改 CSS 跟 JavaScript 檔案就要手動 inline,這樣的確很麻煩。

有了上次做 Webpack 作業的經驗,這次讀了兩篇學習資源的文章,知道如何安裝之後就開始動手做作業了。最主要是參考 Gulp 官網 跟胡立老師的 可以幫我自動化嗎,拜託:Gulp。大概是有了上次 Webpack 作業的經驗,這次的 Gulp 作業上手很快,也沒有感覺那麼困難了,除了中間因為 Webpack uglifyJS plugin 卡住了一些時間,大致上都算順利。

Gulp 跟 Webpack 一樣需要用 Node.js 支援,上次的作業後已經比較熟悉 Node.js 跟 npm 的操作,也比較熟悉 command line 的操作,所以這個部分就不會像上次一樣卡關。

Gulp 到底是做什麼用的呢?簡單來說就是幫你把原本需要手動完成的工作,全部自動化。舉例來說,原本我用 Sublime 的套件幫我把 Sass 編譯成 CSS,然後再用另一個套件把 CSS 檔案加入 prefixer,有 Gulp 之後,我就不用再手動處理這些東西,安裝 Gulp plugins 然後寫好程式就可以一次幫我處理好。 而且 Gulp 有各式各樣的 plugins 可以使用,所以當要手動處理的事情越來越多的時候,使用 Gulp 就變得好方便!


這次寫作業的流程:

1. 開啟 cmd.exe 之後,cd 切換到專案根目錄,安裝 Webpack 跟 Webpack modules 需要的東西,還有創建 package.json。這邊就快速帶過,跟上週的步驟一樣。
安裝 Webpack:
npm install --save-dev webpack

創建 package.json:
npm init -y

安裝 jQuery:
npm install jquery --save

安裝 json-loader:
npm install json-loader --save

webpack.config.js 的檔案延用上次作業的檔案,所以直接複製過來這次作業的資料夾內,其他檔案也都是直接複製過來。不過上次是用 Webpack plugin 引入 jQuery,這次則取消 plugin 改成直接在 index.js 裡面寫 var $ = require('jquery');。這樣 Webpack 的設定大致完成,接下來要安裝 Gulp 跟 Gulp plugins。

2. 安裝 Gulp。
npm install --save-dev gulp

3. Gulp 流程。
Gulp 官網 上可以搜尋有哪些 plugins,我想要 Gulp 幫我處理的流程是:編譯 Sass 成 CSS -> CSS 加入 prefixer -> minify CSS -> 執行 Webpack -> uglify JS -> Inline CSS 跟 JS。

依照關鍵字去找對應的 plugins,然後安裝,最後再把 plugins 都 require 進 gulpfile.js 裡面,然後寫好執行的程式碼就完成了。

原本以為 Webpack 跟 Gulp 要分開執行,問胡立老師 Webpack 是否應該利用 Gulp plugins 一起處理,他說用 Gulp plugins 處理比較好。

4. 安裝 Gulp-sass,用來編譯 Sass 成 CSS。
npm install gulp-sass --save

5. 安裝 Gulp-autoprefixer,幫 CSS 加入 prefixer。
npm install --save gulp-autoprefixer

6. 安裝 Gulp-clean-css,用來 minify CSS。
npm install gulp-clean-css --save

7. 安裝 Gulp-rename,可以幫檔案重新命名。
npm install gulp-rename --save

8. 安裝 Gulp-webpack,用來執行 Webpack。
npm install gulp-webpack --save

9. 安裝 Gulp-inline,用來 inline CSS 跟 JS 檔案到 index.html 裡面。
npm install gulp-inline --save

10. 安裝 Gulp-sequence,用來指定執行 Gulp plugins 的順序。
npm install gulp-sequence --save

Gulp-rename 跟 Gulp-sequence 這兩個不是可以直接聯想到需要安裝的 plugins,我也是看了胡立老師的 可以幫我自動化嗎,拜託:Gulp 這篇才知道的。以上就把所有需要的 Gulp plugins 都安裝好了。

11. 建立 gulpfile.js。概念跟 Webpack 的 webpack.config.js 有點類似。
先把所有需要的東西都 require 進 gulpfile.js:
var gulp = require('gulp');
var sass = require('gulp-sass');
var rename = require("gulp-rename");
var autoprefixer = require('gulp-autoprefixer');
var cleanCSS = require('gulp-clean-css');
var webpack = require('gulp-webpack');
var inline = require('gulp-inline');
var gulpSequence = require('gulp-sequence');

Gulp pluings 通常都會附上範例,參考範例就可以大概了解要如何寫出程式碼。
依序寫出程式碼:

1) 編譯 Sass 成 CSS:
gulp.task('sass', function () {
  return gulp.src('./src/sass/**/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(rename('style.css'))
    .pipe(gulp.dest('./src/css'));
});

這裡大概講解一下上面的程式碼在做什麼。
gulp.task 就是定義一個任務,後面加上自訂的任務名稱 'sass' 跟 function。

gulp.src 指定來源檔案的路徑,讓 Gulp 讀到你要他處理的檔案。這裡是說,讀取我目前所在的資料夾 ./ 下的 sass 資料夾下的所有資料夾 ** 裡面的所有 *.sass 檔案。雖然我只有一個 sass 檔案,直接指定檔名也可以,不過一般大型專案會有許多 sass 檔案,所以就保留了這個寫法。

通過 .pipe() 把你的指定傳下去,這裡傳給 sass() 編譯 Sass(後面是錯誤訊息的處理,我只是複製範例上的),然後再傳給 rename() 把檔名改成 style.css,最後傳給 gulp.dest 用來指定輸出檔案的儲存位置。

.pipe() 裡面的 plugins 名稱要跟一開始 require 進來的變數名稱一樣。譬如說,我原本是 var sass = require('gulp-sass');,變數是小寫 sass,那就不能變成用大寫 .pipe(SASS().on('error', sass.logError))
一開始的任務名稱 'sass' 則跟 .pipe() 裡面的指令沒有關係,而是跟最後要執行順序的指令有關。

之後的程式碼都大同小異,一樣的邏輯去寫,我大部分都是直接複製 Gulp plugins 裡的範例再修改。

其中 Gulp-sass 範例裡面有 sass:watch
gulp.task('sass:watch', function () {
  gulp.watch('./src/sass/**/*.scss', ['sass']);
});

原本不太確定 'sass''sass:watch' 在使用時機上怎麼區分,後來問胡立老師之後才知道 'sass' 是用在檔案確定都弄好要上傳到伺服器時,而 'sass:watch' 則是用在開發時隨時監看 sass 檔案,有變動就隨時編譯成 CSS。以這次作業的來說,幾乎不會再變動 CSS 了,所以就直接用 'sass' 做編譯。

2) 在 CSS 加入 prefixer,並且 minify CSS:
gulp.task('minify-css', function(){
  return gulp.src('./src/css/style.css')
  .pipe(autoprefixer({
        browsers: ['last 2 versions'],
        cascade: false
    }))
  .pipe(cleanCSS())
  .pipe(rename('style.min.css'))
  .pipe(gulp.dest('./dist'));
});

這個部分其實結合了 Gulp-autoprefixerGulp-clean-css,如果要分成兩個部分來寫也是可以,不過我想要讓程式碼簡潔一點,所以就選擇把兩個 plugins 功能結合在一起,然後直接輸出 style.min.css。

3) 執行 Webpack,並用 Webpack plugin uglify JS。
gulp.task('webpack', function() {
  return gulp.src('./src/js/index.js')
    .pipe(webpack( require('./webpack.config.js')))
    .pipe(gulp.dest('./dist'));
});

這裡是我卡關最久的部分,原本使用 Webpack 內建的 uglifyJS plugin,在 webpack.config.js 直接引入:
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

然後 moduels 裡面加入 plugins:
plugins: [
        new uglifyJsPlugin() // uglifyJS
    ]

可是不斷的出現錯誤訊息:

後來找到一篇 Stack Overflow 的文章:Uglify Syntaxerror Unexpected Token Punc,依照上面的方法安裝了 Webpack-uglifyjs 跟 uglify-es:
npm install uglifyjs-webpack-plugin --save
npm install uglify-js@github:mishoo/UglifyJS2#harmony --save

也在 webpack.config.js 把引入改成:
var uglifyJsPlugin = require('uglifyjs-webpack-plugin');

但還是一樣出現錯誤訊息,始終無法成功 uglify JavaScript 檔案。似乎是 ES 語法的問題,可是我幾乎沒有用到 ES6 語法,所以想說只要把有問題的地方找出來,不想要另外安裝 Babel-loader (後來直播中,胡立老師有用 Babel-loader 也是無法用 Webpack plugin 成功 uglify JavaScript 檔案,所以應該是 Webpack uglifyJS plugin 的問題。如果有使用 ES6 語法,可能要用 Gulp-minify 來 uglify JavaScript 檔案比較保險)。

後來依照錯誤訊息找到在 utils.js 有問題的一段程式碼:
module.exports = {
  getLocalString
};

原本以為這段應該是 ES5 語法,後來丟到 Babel 官網 的 Try it out 裡面去編譯,後來才知道原來這段是 ES6 語法,編譯後變成:
module.exports = {
  getLocalString: getLocalString
};

改成編譯後的 ES5 語法後就正常了!

當時這段是上週寫 Webpack 作業參考胡立老師的 我也想要模組化開發:Webpack 這篇寫的,加上我自己對 ES6 語法不熟悉,所以才會發生這次的錯誤。之後要找時間好好研究一下 ES6 語法了。

4) Inline CSS 跟 JS 到 index.html 裡面:
gulp.task('inline', function() {
  return gulp.src('./index.html')
      .pipe(inline({
        base: './'
      }))
      .pipe(gulp.dest('./dist'));
});

Gulp-inline 裡面的範例寫得很複雜,我的寫法是是最基本的寫法(也是問胡立老師之後才知道原來可以簡化成這樣 XD),gulp.src 一樣是指定來源檔案的路徑,base: 是寫來源檔案的相對路徑在哪裡,最後 gulp.dest 輸出到 dist 資料夾裡面,把原本的 html 檔案跟輸出後的 html 檔案分開資料夾存放比較好。

5) 指定 Gulp 執行順序,'default' 代表預設會執行這一個:
gulp.task('default', gulpSequence('sass', 'minify-css', 'webpack', 'inline'));

12. 在 package.json 裡修改 scripts 下的指令。
"scripts": {
    "build": "gulp"
  },

13. 在 index.html 裡面修改 CSS 跟 JS 檔案的路徑。
<link href="./dist/style.min.css" rel="stylesheet" type="text/css">
<script src="./dist/bundle.min.js" type="text/javascript"></script>

14. 修改 Sass 檔案裡面的圖片檔案路徑。
我的背景圖片是用相對路徑,輸出後的 style.min.css 已經跑到 dist 資料夾裡面,而不是原本的 src/css 裡,所以圖片路徑也要修改才會讀取到圖片檔案。

15. 在 cmd.exe 裡面執行 Gulp。
npm run build

成功的話就會把所有的事情都一次完成,但看我上面的作業過程就知道事情總是不會那麼順利的 XD。

其實真正做作業過程通常是,安裝一個(或多個) plugin 之後 -> 然後寫那一段程式碼 -> 然後執行 Gulp 測試可不可以正常執行。中間要是有出錯,就要去找出錯誤在哪裡。

作業過程中除了 Webpack uglifyJS 有出錯,inline 的時候也因為忘記修改 CSS 路徑沒讀到正確檔案,背景圖片也是忘記改檔案路徑而讀不到圖片檔案。總之,作業過程其實沒有上面寫的那麼順暢,但比起上週 Webpack 作業,至少知道整個流程的感覺,而不是像上次一樣進入一個完全未知領域。

這次作業也是學到很多東西,而且知道 Gulp 可以自動化處理好多事情,以後就可以好好利用~


因為這次作業無法放在 CodePen 上,所以我放上這次作業的 GitHub 跟 GitHub Page。


No comments:

Post a Comment