隨著網頁技術不斷翻新,前端需要處理的事也就越來越繁雜;在 2016 年有一篇當時很紅的文章,詼諧的故事除了是技術名詞科普之外,同時也諷刺了前端技術的瘋狂增長,蠻推薦沒看過的讀者看一下的。在這樣龐雜豐富且迅速更迭的技術環境之下,將開發流程自動化、工程化就成了必需,開發者也自然會需要一些工具來協助優化這些流程。今天就讓我們一起聊聊前端的打包工具。
筆者撰文的時候,將該文章重看一次,發現比三年前第一次看的時候多理解不少,雖然還不到 100% 清楚,但總歸算得上是有成長吧XD
本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您 前往購買 支持!
工程化的起點,自然是從 Node.js 問世,套件管理工具 npm
誕生開始。透過 npm
,開發者便能快速且輕易的從 NPM Registry 下載到其他開發者提交的套件,並加在自己的專案中;除此之外,npm
也提供了 script
的功能,將 shell script 封裝成別名快速使用:
{
"scripts":{
"dev": "node app.js"
}
}
例如上例,當開發者輸入 npm run dev
,就等同於執行了 node app.js
這行指令;透過這樣簡短的 script 設定,也確實可以解決一些基本的需求。
但其實大部分需求在開發過程中,重複性是非常高的。例如將 SASS、SCSS 編譯成 CSS,透過 npm script,就需要修改完、輸入指令進行編譯、Reload,開發者的開發體驗便會大打折扣;要解決這樣的情況,Grunt 是一個不錯的解決方案。
Grunt 提供了更大的程式化空間,讓開發者能更自由的控制自動化流程;藉由大量的插件(plugin),以及高度客製化的設定,開發者可以輕易的完成一些常見的任務。
例如 昨天 提到的 Uglify,就可以透過 grunt-contrib-uglify 這個套件,幫我們自動完成這樣繁瑣的步驟。
但 Grunt 的設定是大多是每個插件個別設定,一旦專案中的插件數量增加,設定跳來跳去,同一支原始檔案的去向很容易就變得難以追蹤;Gulp 透過管道(Pipes)及串流(Streams)的概念,便能有效避免前述的情況發生。
例如我們想要將 LESS 編譯成 CSS,再將檔案進行最小化(minify)處理,Gulp 中的設定可能會這樣寫:
function css() {
return src('assist/style/*.less')
.pipe(less())
.pipe(minifyCSS())
.pipe(dest('dist/css'))
}
藉由 pipe
函式,將任務結果依序往後傳遞,同檔案類型會經過什麼處理,也就變得一目了然;除此之外,Gulp 相較於 Grunt 還多了檔案監看、檔案讀寫等功能,將許多繁雜的基礎功能都得以自動化,開發者的開發流程也能因此省上不少力!
更多 Gulp 的說明及範例,可以參考 廖洧杰 的 Gulp.js 自動化前端任務流程
但隨著前端的演進,開發流程又更加複雜,針對檔案類型做處理已經不能滿足開發者的需求;加上資源量越來越多,在 HTTP/1.1 有單一網域請求數上限 的情況下,勢必需要將前端的程式分開包裝,才能夠將資源傳遞給使用者。
熱門套件 Webpack 則給出了一個全新的思考模式;藉由定義專案的 entry
及 output
,Webpack 便會從 entry
開始解析專案,最後將相依的檔案打包成指定的 output
;除此之外,也可以在過程中間加上 loader
、plugins
,對需要的檔案做進一步的處理。
同樣拿 CSS 來看看好了,這次將 SCSS 依序處理成 CSS:
rules: [
{
test: /\.(scss)$/,
include: [path.resolve(__dirname, 'app')],
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: false,
minimize: true,
sourceMap: true,
importLoaders: 3,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
config: {
path: path.resolve(__dirname, 'postcss.config.js'),
},
},
},
{
loader: 'resolve-url-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
}),
},
//...
]
透過上述設定,將會依序透過 sass-loader
、resolve-url-loader
、postcss-loader
、css-loader
、style-loader
對副檔名為 .scss
的檔案做處理,其餘檔案類型也能夠設定、搭配相對應的 loader,將前端的內容封裝、打包。
看到一堆設定是不是快瘋掉了?那還只是冰山一角呢。這也是 Webpack 最讓人詬病的問題;Webpack 提供開發者更高的自由度,同時卻也帶來更多的複雜度;但在一般的中小型專案,可能根本不需要那些繁雜的設定,這時候用 Webpack 就有點殺雞用牛刀了。
開源社群中有一些新的打包工具,都能讓開發者可以減少撰寫龐大設定的痛苦,並享用自動化流程的便利;例如專注在 Node.js 後端專案的Backpack,號稱是新一代打包工具的 Rollup,及完全無設定便能直接使用的 Parcel,都是筆者我認為很值得關注的打包工具。
說了那麼多打包工具,用途到底是什麼呢?不外乎是以下幾點:
我們在 前幾天 有稍微提到了前端三大框架,相信這也是不少開發者接觸打包工具的起點。由於前端框架提供了許多額外的語法,但瀏覽器只能解讀 JavaScript,中間必須要經過一個轉換的步驟,將開發者撰寫的程式,轉譯成瀏覽器能解讀的內容。
例如前面舉例的 LESS
、SASS
會透過 postcss
做相對應的處理;React 用的 JSX
語法,Vue 的 .vue
檔、ES6+ 的語法等等,則會透過在 本系列文的這一篇 提到過的 Babel
,做程式碼的轉譯;有了打包工具轉譯程式碼,開發者也就可以自由使用更新的框架、更精簡的語言特性,不用太擔心支援度的問題。
針對程式進行效能優化是非常重要的一環,也因為其繁瑣的流程設定,打包工具在優化程式這塊能充分的給予協助。
例如 昨天 我們聊到的優化手段,幾乎所有的內容,Webpack 都能找到相對應的插件協助開發者處理;例如 minify、uglify、切分 chunk 等等。關於這部份,筆者先前有撰寫過相關的內容,有興趣的讀者可以移步到 我的 Blog 參考。
例如為了減少前端請求數目的 CSS Sprite,比起用 PS 慢慢對齊、計算 CSS 屬性,還不如透過打包工具的套件例如 Webpack Spritesmith,來完成圖檔合併的工作;其他像是 Webpack 的 Dev Server 內建的 Hot Reload 功能,網頁開發者就不用一直手動重新整理頁面了。
加上前面的轉譯及優化程式,將原本需要手動處理的事情,全部交給打包工具處理,再透過設定妥當的自動化流程,讓開發者寶貴的時間不用再去做週期性的繁瑣雜事。
今天很快速的將打包工具的演進瀏覽了一圈,過程中提供幾個簡易小範例,並大致說明打包工具的用途,最後與昨天提到的程式優化做呼應。在網站內容不斷增加,開發流程逐漸趨近工程化的現在,嘗試去使用、理解這些能協助建置自動化開發流程的套件,可以讓開發者有效地省下大量的時間。
那麼今天的內容就到這邊啦,我們大家明天見~
筆者
Gary
半路出家網站工程師;半生熟的前端加上一點點的後端。
喜歡音樂,喜歡學習、分享,也喜歡當個遊戲宅。相信一切安排都是最好的路。