作為奇舞團的一個小小程式媛,每天都在不斷地接受新知識,PostCSS剛學完,PostHTML又出來了。剛研究明白Rollup的配置,又有一個橫空出世的打包工具——Parceljs。
我本來是拒絕的,畢竟在這個充滿誘惑的年代,我要維持自己內心的純潔,但當我開啟Parcel的官網,看到下面的benchmark資料,我就不淡定了。
居然帶快取的時候比webpack快10倍!!!
雖然是官方給出的單例測試,但是在4個CPU的2016年MPB,擁有1726個模組, 6.5M的未壓縮檔案的app上跑出這個成績,簡直是令人心動不已!
雖然“又小又快又容易”在自然界中某些場合下並不見得是什麼好事,但是在如今的前端領域,這個詞簡直是對這種解放人類天性的打包產品最好的褒獎!
parcel可以說是將懶貫徹到極致。一個月前,我由於無法忍受(實際上是因為看不懂記不住)webpack繁複的配置,而擁抱了Rollupjs,然後看到了Parcel之後,估計我又該”移情別戀“了!
據官方介紹,Parceljs擁有這樣超快打包速度的原因得益於它開啟了多程式打包,並使用檔案系統快取機制,從而提升了重啟後重打包的速度。
開始入手
對於parcel,入手過程堪稱傻瓜式~
可以使用 Yarn 或者 npm進行安裝,對於我們這些年輕的FEer,當然是選擇npm了
1 2 3 |
// 全域性安裝 parcel $ npm install -g parcel-bundler |
下面就可以開始嘗試檔案打包了.
Parcel與眾不同的一點是,它可以使用任何型別的檔案作為入口檔案,但是官方推薦是用HTML檔案或者是JavaScript檔案,如果你在HTML檔案中引入了一個相對地址的JavaScript檔案,Parcel也會自動給將相對於HTML的地址替換為相對於輸出檔案的地址,真是超級貼心!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// index.html檔案 <html> <body> <script src="./index.js"></script> </body> </html> // index.js 檔案 console.log("hello world"); |
如果你的專案沒有自己的服務端,感謝Parcel有一個內建的Dev Server?,你可以使用Parcel提供的這個Dev Server,它會在你修改檔案之後自動兼聽你的檔案改變進行重打包,同樣可以配置HMR來加快開發速度。
但是具體怎麼新增HMR,這就是後話了,大家收!
當然,如果你的專案有自己的服務端,你可以不使用這個Dev Server,而檔案自動監聽重打包和HMR也不會受影響。執行下面的程式碼,你就會在自己目錄裡面看到一個裝滿打包好檔案的/dist資料夾。
如果你已經為上線做最後一次打包的準備,你可以直接用build模式,Parcel將不會開啟監聽,只會編譯一次!而且Parcel會在生產模式的時候使用 uglify-js ( JavaScript), cssnano ( CSS), 和 htmlnano ( HTML)進行壓縮混淆處理。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 沒有服務端的時候,啟動一個dev server,執行在10086埠 parcel index.html -p 10086 // 開啟服務端的時候,開啟watch模式 parcel watch index.html // production 模式,沒有比上線更令人開心而恐懼的事情了 parcel build index.html |
Parcel的食物——Assets
如果說打包工具是一個人的話,那麼他吃進去了一堆檔案,又輸出了一堆檔案(這裡我用的是輸出。。。)。
對於Parcel來說,他的食物其實可以是任何型別的檔案,但是Parcel對於JavaScript、CSS和HTML檔案有著天生的優秀的支援度。Parcel可以自動分析檔案中的依賴,然後將這些依賴打包到最終的輸出檔案中。
JavaScript
對於最好吃的JavaScript來說,Parcel支援cjs和es6兩種語法,他同樣可以支援動態import()來非同步載入檔案,這一點對於之後會談到的程式碼分割來說很重要。
1 2 3 4 5 6 7 8 |
// 使用Cjs引入module const dep = require('./path/to/dep'); // 使用es6引入module import dep from './path/to/dep'; |
對於在JavaScript檔案中引入的非JavaScript資源來說,比如CSS,你可以使用import將其引入,他會將所有的同型別檔案放在一個另外的單獨的打包檔案內。對於最終的輸出檔案來說,這些資源就是一個一個的URL,直接引用就好了,其他的檔案型別也是一樣的。
而如果你非要堅持inline一個檔案的話,就需要你去使用Node.js的fs.readFileSyncAPI了。注意,所用的URL是靜態分析的,意味著你不能在URL中拼接變數了,當然__dirname和 __filename除外,對於這些“內部員工”你也沒啥辦法了!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// index.js 中可以這樣引入css和其他資源 // 引入CSS檔案 import './test.css'; // 從CSS檔案中引入一個CSS模組 import classNames from './test.css'; // 引入一個圖片檔案 import imageURL from './test.png'; // 如果你非要堅持用inline,你只能這麼做 import fs from 'fs'; // 以字串形式讀取內容 const string = fs.readFileSync(__dirname + '/test.txt', 'utf8'); // 以buffer形式讀取內容 const buffer = fs.readFileSync(__dirname + '/test.png'); |
CSS
對於CSS來說,她不光可以被JavaScript和HTML引用,自己也可以使用@import引入其他的CSS資源,這樣引入CSS資源的時候,Parcel會將其inline進來。
而在CSS中引入的其他資源,要使用url(),比如圖片和字型神馬的,Parcel就會將其改為相對於輸出檔案的路徑,但是你寫的時候還是要相對於當前的CSS路徑的。
1 2 3 4 5 6 7 8 9 10 11 12 |
/* 引入其他css */ @import './other.css'; .test { /* 引入一個圖片 */ background: url('./images/background.png'); } |
對於Parcel來說無論你是炒CSS還是煮CSS。。。我是說無論是LESS, SASS 還是Stylus,他都一視同仁,採取同樣的處理方法,從不挑食。
HTML
你可以將HTML視為Parcel的入口,但是他也可以被引入一個JavaScript檔案中。對於其中的各種scripts, style, media 的URL,與上面的處理方式是一樣的,但是一定要注意,路徑是相對於當前檔案的!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<html> <body> <!-- 引入一個圖片檔案 --> <img src="./images/header.png"> <a href="./other.html">Link to another page</a> <!-- 引入一個JavaScript檔案 --> <script src="./index.js"></script> </body> </html> |
各種轉換工具
與很多(你知道的,例如webpack,rollup這樣的)打包工具不同,Parceljs天生整合了Babel,PostCSS,PostHTML,只要Parcel發現了這些的配置檔案,他就會自動run這些轉換工具。
另外一個神奇之處在於,對於一些第三方的node_modles裡面釋出時帶的配置檔案,Parcel都可以為他自動開啟編譯程式,而且只會載入有用東西,所以當你引入一些特定的檔案的時候,無需手動而配置和了解他是怎麼樣子build的,也可愉快地使用了。
程式碼分割技術
上文中我們提到的動態載入,也就是使用import()來載入依賴,這種新奇的寫法在Parcel上有了用武之地。Parcel不需要額外配置,只要他發現程式碼中有使用import()引入的模組,就會自動的進行程式碼分割。最終生成的打包資料夾中,就會將這些模組打包成獨立的檔案,而在主檔案中使用URL的形式引入,最後的主檔案打包體積更小、載入速度更快~
如果你已經習慣了之前在檔案頂部去集中import的話,也不用擔心。在Parcel中動態載入是可以進行懶載入的,你可以使用下面的寫法。事實上,這些子打包檔案,只會在你使用的時候才載入進來,四不四很開心啊!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 建立一個要動態載入的頁面名字的索引地圖 // 這些頁面實際上只會在真正使用的時候去載入 const pages = { about: import('./pages/about'), blog: import('./pages/blog') }; async function renderPage(page) { // 懶載入請求頁面 const render = await pages[page]; return render(); } |
想要對動態載入有更深入的瞭解,可以回翻我們之前的文章,《 chrome63支援動態 import() 》。
?注意哦!因為動態載入返回的是一個Promise,所以你是可以用async/await的,但是如果你的瀏覽器是不支援這個寫法的話,一定要使用bable-polyfill(app)或 babel-runtime& babel-plugin-transform-runtime(library)
HMR 模組熱替換
HMR(Hot Module Replacement)模組熱替換可以說是改變FE生活,拯救懶癌末期患者的福利技術。他會自動更新瀏覽器端的改變的模組,而不需要重新去reload整個頁面,大大提升了開發速度。 據官方稱, Parcel的HMR實現支援JavaScript和CSS,而且會在生產模式的時候自動禁止掉。當改動儲存後,Parcel會將改變的內容重新打包,併傳送一個update的訊號通知所有運作的端,比如說你的瀏覽器。 但是實際我測試結果發現,js的改變應該是可以進行熱替換的,但是CSS改變之後,頁面並沒有反應,如果我想讓他有反應的話,就需要配置PostCSS。蜜汁尷尬啊,人家只是想單純地寫一個css而已。。。。
更友好的日誌資訊
當執行Parcle的時候,終端裡面列印的日誌中會顯示目前打包的進度,以及打包完成所耗費的時間。Mac使用者還有一些驚喜的福利,就是會出現一些很可愛的emoji,讓等待也不會無聊,比如這樣:
當打包遇到問題的時候,Parcel會列印出來帶有語法高亮的程式碼,讓你更容易的去進行排錯。
總結
總之,Parcel對於一些希望快速搭建專案的人來說,是一個節省時間的方式,這種0配置的打包工具可以極大減輕專案搭建初期的工作,私下稱它是“打包保姆”也不為過。
但是如果你想要有些個性化的處理,比如說在rollup中就提供了一個可以將打包檔案中的程式碼排成想要的形狀的外掛,Parcel可能就無法幫你實現了。想知道我說的這個外掛什麼,並且對Rollup有更深入的瞭解的話,你可以去我的PPT看一看,rollup 小巧又有趣打包工具-聲享: https://ppt.baomitu.com/d/e0af330f
下面我精選了一些知乎上對於Parcel的評價,如果侵犯了答主的著作權,可以在奇舞週刊的後臺聯絡我~
陳成
連結:https://www.zhihu.com/question/263676981/answer/272172727
看到 ParcelJS 還是眼前一亮的。新建 index.html、index.js 和 index.css,然後 parcel index.html,就能拿到可執行的 html、js 和 css 組合。html 可以作為入口正是我期望的,這讓前端開發迴歸到本來的狀態,很舒服。
ParcelJS 是以 assets 方式組織的,assets 可以是任意檔案,所以你可以構建任意檔案。而在 webpack 中,只有 JS 是一等公民(webpack@4 會增加 CSS 為一等公民),所以必須是以 JS 為入口去組織其他檔案,這很彆扭。
速度是 ParcelJS 主要賣點。體驗下來確實快,原因是多核(通過 worker 平行構建)和檔案系統快取(二次構建會快,和 webpack 的 dll 異曲同工)。不過 webpack 也有多核處理 loader 和壓縮的外掛,沒對比過,不知道差異如何。另外 webpack 的構建速度慢在 dev 模式下還是可以的,主要還是壓縮慢,在壓縮速度上沒有突破,僅提升編譯速度,只能解決一部分問題。
關於 0 配置。ParcelJS 本身是 0 配置的,但 HTML、JS 和 CSS 分別是通過 posthtml、babel 和 postcss 處理的,所以我們得分別配 .posthtmlrc、.babelrc 和 .postcssrc。功能上,Code Splitting 和 Hot Module Replacement 沒啥新的,和 webpack 等工具相同。
對於我來說,功能目前還缺 SourceMap、公共檔案提取、publicPath 配置(Code Splitting 需要)、Tree Shake 和 Scope Hoist 等。很好的開始,持續維護的話應該不缺使用者。
周左左
連結:https://www.zhihu.com/question/263676981/answer/271919415
但是對於打包工具來說速度不是最首要的,生態才是。
下面從實踐出發談談我個人對打包工具的理解
曾經
打包工具的出現,很大程度上緩解了傳統的手動引入資原始檔(css, js等)到html帶來的不便。從。從最初的require.js模組載入庫以及相關規範出現開始,社群就開始進行各種嘗試。到gulp利用pipe以及watch概念來構建前端自動化流程,之後引入了browserify打包作為構建工具中的一環,最終到了現在webpack集大成者。
webpack當年首先是作為一款與browserify功能相當的打包工具/庫,出現在社群中的。所以你會常常看見社群裡的教程/問答貼: gulp + webpack 或是 gulp + browserify。當然還有但是風韻猶存的grunt,以上組合可以自行想象。
之後webpack重點開發了自身作為一個構建工具的作用,而不是僅僅作為一個打包庫寄人籬下。在這之前,我們都沒見過打包工具需要去監聽檔案變動而觸發打包動作(例子一個),他們都只是從gulp/grunt這種構建工具的pipe來獲取到具體變化,再去執行打包動作。所以一句話,像監聽變化、assets資源優化等等這種事情,在以往打包工具/庫是觸及不到的。打包工具只針對js作為入口檔案,遞迴獲取依賴,建立依賴樹,逐個利用自帶的或第三方的中介軟體來最終輸出bundle js。
當下
剛剛提到webpack不滿足於自己僅僅只是一介打包工具而存活於世,或是說社群本意就是將它打造成為一款前端專案構建工具。webpack推出了一系列的功能,漸漸發覺,gulp能做的事情,現在webpack都能做到了,甚至還多了一系列的gulp配合打包庫才能做到的功能。Parcel無配置,從assets出發的構建工具,嶄露頭角。
我超喜歡在webpack的 個個loader、plugin都是人才說話又好聽
未來
未來肯定是屬於操作簡單便捷的構建工具的,它要能支援js、html、css三劍客的打包,速度足夠快,生態夠好。但是依然要有定製性:
1.一半是傳統多頁面應用,一半是SPA的業務場景。
2.新增各種稀奇古怪的js語言。
3.針對css進行優化,像js那樣能夠提取出common chunk。webpack 4新增了許多特性對css打包進行了支援。我們拭目以待。
4.針對業務進行分組打包,而不是所有的entry都要強制打包(目前可以利用多config特性來解決,但是要小心webpack-dev-server的坑)。
思考:針對場景的配置成本
如果你要使用react,就用create-react-app,如果你要使用angular,就用angular-cli。
但是如果你不用前端框架,或者嫌這些cli太重了,或不透明(其實不然),當然可以用parcel。
但是如果你又要引入typescript或者一些其他神奇的plugin到parcel中,那麼又成了鐵頭娃、填坑俠。