百日轉職前端工程師:第十三週 Webpack, Gulp 《DAY 19》
大家好,這是百日轉職前端工程師的 Day19,也是 11/14(六),這週的主題會講的是一些現代前端工具 Webpack 和 Gulp。Webpack 功能非常強大,能夠將各種功能作為模組打包成一個可引入運行在瀏覽器上的檔案,由於篇幅眾多獨立為一篇,是現代開發上一定會用到的工具之一。而 Gulp 則是可以將工作的流程自動化,本篇也會帶你搞懂這兩者的差別。
大家好,這是百日轉職前端工程師的 Day19,也是 11/14(六),這週的主題會講的是一些現代前端工具 Webpack 和 Gulp。
Webpack 功能非常強大,能夠將各種功能作為模組打包成一個可引入運行在瀏覽器上的檔案,由於篇幅眾多獨立為一篇,是現代開發上一定會用到的工具之一。而 Gulp 則是可以將工作的流程自動化,本篇也會帶你搞懂這兩者的差別。
復盤系列將會回答我正在上的課程 Huli 的程式導師實驗計畫每一週學習上的自我檢測目標。
一、webpack 的目的以及原理
- Webpack 是一套模組整合工具(Webpack is a module bundler.)
- 可將零散的 JavaScript 模組打包,然後在瀏覽器上運行,解決舊瀏覽器不支援部分新語法的問題,也利於後續管理與維護
Webpack 打包流程
- index.js, utils.js, templates.js → 經過 webpack 打包 → main.js
- index.html 用 src 引入 main.js 模組
在專案資料夾內安裝 webpack
- mkdir webpack-demo
- cd webpack-demo
- npm init -y
- npm install webpack webpack-cli --save-dev
通常開發者會把原始的檔案放在 src 資料夾,編譯過後的檔案放在 dist 資料夾,以方便管理。執行 webpack 就可以進行編譯,預設會把 src 裡的檔案打包到 dist 內成名為 main.js 的檔案。
也可以在根目錄建立設定檔 webpack.config.js 來自定義路徑和要打包的模式,模式有兩種
- production 的壓縮程度較高,檔案會變比較小,類似 uglify 過後的一串 JavaScript
- mode: 'production',
- development 的可讀性則相較高,但檔案較大
- mode: 'development'
webpack.config.js 檔案的格式
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
};
*在 package.json 建立 script,之後 npm run build 就可以執行 webpack
"scripts": {
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1" },
二、使用 webpack 進行模組化開發
- 除了打包 JavaScript 之外,webpack 把 modules 的概念延伸至更廣的範圍,各種資源都可以視為一個 module
- 所以我們也可以利用 webpack 幫我們打包圖片或 CSS
- 透過各種 loader 把資源載入 webpack,也可以順便做到 uglify JavaScript 或是 minify CSS 之類的編譯
以下會介紹一些常見的 loader 的使用,打包 npm 上別人第三方的 modules
1. 打包 npm 上別人第三方的 modules
透過 CSS loader 打包 CSS 檔案
- css-loader:打包 CSS,在 webpack.config.js 加入 loader
module.exports = {
module: {
rules: [
{ test: /\.css$/i,
use: ['style-loader', 'css-loader'],
}, ], }, };
- CLI 安裝 style.loader 和 css.loader
- npm install --save-dev css-loader style-loader
- 在 index.js 引入 style.css 一併打包為 main.js
- import css from './style.css';
透過 babel-loader 執行 babel
- CLI 安裝 babel-loader
- npm install -D babel-loader @babel/core @babel/preset-env
- 在 webpack.config.js 設定檔加入 babel-loader 如下
rules: [ {
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
} } }
透過 sass-loader 執行 sass
- CLI 安裝 sass-loader
- npm install sass-loader sass webpack --save-dev
- 在 webpack.config.js 設定檔加入 sass-loader 如下
rules: [ {
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader',
], }, ],
2. 利用 Dev Server 自動化打包
dev server 是一套可以在檔案改變的時候「自動」重新打包的 plugin,不用更動程式碼每次都要手動在 CLI 輸入 wbpack / npm run build 太麻煩。
- CLI 安裝 dev server
- npm install sass-loader sass webpack --save-dev
- 在 webpack.config.js 設定檔加入以下,定義編譯完的檔案放在哪裡
- devServer: { contentBase: './dist', },
- 在 package.json 加入 start 指令,之後 CLI 輸入 npm run start,就會進入到 dev server start 的狀態
- "scripts": { "start": "webpack-dev-server --open", }
3. 使用 source map 看到原來程式碼的樣子
在 webpack.config.js 加入 source-map 程式碼,在 debug 的時候,就可以在 chrome dev tool 溯源到真正的程式碼。
module.exports = {
mode: 'development',
entry: {
app: './src/index.js',
},
devtool: 'inline-source-map',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
}, };
4. 使用 HtmlWebpackPlugin 自動產生打包後的 html
在 webpack.config.js 加入 HtmlWebpackPlugin,自動產生打包後的 html 在 index.html 檔案內
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin()]
};
參考資料:
三、 gulp 的目的以及原理?
- gulp 是一個前端自動化構建工具(Task Mananger)
- 開發者可使用它來建構自動化工作流程
- 但通常在比較小的網站才會看到 gulp
- 如果在大型專案網站用到前端框架,任務又更多而且方向不太一樣,比較會用 webpack
1. 為什麼需要 gulp?
- 為了將工作流程自動化,也就是整合前端開發環境
- 藉由簡化工作量,可讓開發者將重點放在功能的開發上
例如下列功能
- 讓網頁自動重新整理
- 編譯 SASS 、程式碼檢測
- 壓縮 CSS, JS, 圖檔
- 自訂任務流程…
2. 如何安裝 gulp?
- 先用 npm 安裝 gulp-cli:npm install --global gulp-cli
- 開啟使用 gulp 的專案資料夾
- 進行初始化:npm init
- 安裝 gulp package:npm install --save-dev gulp
- 使用指令檢查是否安裝成功:gulp --version
3. gulp 提供的 API
- gulp.task
- 執行工作
- gulp.src
- 執行資料來源
- gulp.dest
- 執行結果位置
- gulp.watch
- 監視執行過程中,資料是否變更
使用 gulp 內建的 API watch(),在檔案每次被改動的時候自動執行 gulp 工作流程,例如我們可以在 SCSS 檔案被更改的時候,自動執行 complieCSS 這個 task,就不需要每次都要手動執行流程。
但要注意的是,使用 watch() 的 task 不能是同步的,如果是同步的任務,將無法確認 task 的完成情況,task 將不會再次運行。
gulp API 使用實例
const { src, dest, series, parallel, watch } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const sass = require('gulp-sass');
sass.compiler = require('node-sass');
const cleanCSS = require('gulp-clean-css');
const rename = require('gulp-rename');
function compileJS() {
return src('src/*.js')
.pipe(babel())
.pipe(uglify())
.pipe(rename({ extname: '.min.js' }))
.pipe(dest('dist'));
}
function compileCSS() {
return src('src/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(dest('./css'))
.pipe(cleanCSS({ compatibility: 'ie8' }))
.pipe(rename({ extname: '.min.css' }))
.pipe(dest('./css'))
}
exports.default = function() {
watch('src/*', series(compileCSS, compileJS));
}
四、利用 gulp 提供的常用 plugin 建構自動化工作流程
各種常用的 gulp plugin,在 src 和 dest 之間加入這些 plugin,可以幫助我們建構自動化工作流程。
1. gulp-babel
就是在 gulp 任務管理器內執行 babel 轉換語法的 plugin
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
function defaultTask() {
return src('src/test.js')
.pipe(babel({ presets: ['@babel/env']}) // .babelrc 第一次用要放參數
).pipe(dest('dist'));
}
exports.default = defaultTask;
2. gulp-uglify
gulp-uglify 針對 JS 檔案進行壓縮與最小化,檔案會被壓縮成一行變數會被簡化,可以減少檔案大小
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
function defaultTask() {
return src('src/*.js') .pipe(babel())
.pipe(uglify())
.pipe(dest('dist'));
}
3. gulp-rename
經過 uglify 的 JavaScript 檔案,通常會命名成 .min.js 以做為區別,所以可以再引入 rename 套件
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
function defaultTask() {
return src('src/*.js') .pipe(babel())
.pipe(uglify())
.pipe(rename({extname: '.min.js'}))
.pipe(dest('dist'));
}
4. gulp-sass
gulp-sass 套件可將 sass 轉換成 css
const { src, dest } = require('gulp');
const sass = require('gulp-sass');
sass.compiler = require('node-sass');
function defaultTask() {
return src('src/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(dest('./css'));
}
exports.default = defaultTask;
5. gulp-clean-css
gulp-clean-css 類似 minify js 對 JS 在做的事,可以把 css 檔案壓縮
將 clean-css 加入流程,設定 sass 的編譯器,同時也加入 rename 這個 plugin,讓編譯完的 css 重新命名為 min.css。做兩次 dest(),編譯完 sass 先複製一次,變成 min.css 後再複製一次,這樣資料夾內就同時有被壓縮前和被壓縮後的檔案。
const { src, dest } = require('gulp');
const sass = require('gulp-sass');
sass.compiler = require('node-sass');
const cleanCSS = require('gulp-clean-css');
const rename = require('gulp-rename');
function compileCSS() {
return src('src/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(dest('./css'))
.pipe(cleanCSS({ compatibility: 'ie8' }))
.pipe(rename({ extname: '.min.css' }))
.pipe(dest('./css'))
}
6. gulp-imagemin
gulp-imagemin 可將圖片檔案壓縮變小
const { src, dest, series, parallel, watch } = require('gulp');
const imgMin = require('gulp-imagemin');
function minifyImg() {
return src('src/img/*')
.pipe(imgMin())
.pipe(dest('lib/img'))
}
exports.default= minifyImg;
五、gulp 引入 series / parallel 執行任務
- 引入 series:按順序執行 compileCSS 和 compileJS 兩個任務
- exports.default = series(compileCSS, compileJS);
- 引入 parallel:同時執行 compileCSS 和 compileJS 兩個任務
- exports.default = parallel(compileCSS, compileJS);
- 執行單個任務
- exports.compileCSS = compileCSS
六、Webpack 和 Gulp 的差別?
Webpack
- 是一套模組整合工具(module bundler)
- 目的:利用模組化的概念,將各種資源打包成能在瀏覽器上執行的程式碼
- 功能:將各種資源視為一種模組引入打包成 main.js
gulp
- 是一套任務管理工具(task manager)
- 目的:提供自動化與流程管理,整合前端開發環境,藉由簡化工作量,可讓開發者將重點放在功能的開發上。
- 功能:提供自訂任務流程,例如 babel、scss、壓縮、重新整理、校正時間等。
兩者目標並不相同,為截然不同的工具,但是恰巧均能夠達到部分功能如以下,因此容易被混淆
- 使用 Babel 將 ES6 編譯成 ES5 語法
- 將 SASS 檔編譯成 CSS 檔
- 壓縮 CSS, JS, 圖檔等
七、總結
『想說的話一杯酒說不盡,那不如就打包外帶吧。』