一、 早期的前端打包工具有很多,主要是Grunt和Gulp等,他們早期的打包思路是通過一個配置文件告訴工具,哪些文件應該被綁定在一起輸出到一個文件中,這樣可以減少了http請求,還可以做一些壓縮。
而webpack的核心優勢在於它從入口文件出發,遞歸構建依賴關系圖。通過這樣的依賴梳理,webpack打包出的bundle不會包含重復或未使用的模塊,實現了按需打包,極大的減少了冗余。
二、webpack做的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(less、Sass/Scss,TypeScript,ES6+等),並將其轉換和打包為合適的格式或版本供瀏覽器使用。
三、 webpack最核心的理念是
- 一切皆模塊:JS,CSS,Image,Html,Font
- 按需加載:默認會打包出一個bundle.js,可能很大,所以首屏速度很慢,所以webpack還可以使用一些特性對代碼分割出多個“bundle”小文件,還可以實現按需異步加載等。
四、本文主要記錄webpack優化,主要有兩個大的維度,一是打包速度,二是打包出來的體積。
打包速度
首先可以通過一些webpack自帶的stats統計分析,還可以通過speed-measure-webpack-plugin專業插件進行分析,他可以分析各個plugin和loader具體耗時。
也可以通過wbpack-bundle-analyzer工具啟動一個圖形化工具來進行標注,面積越大的區域,說明體積占用越多,比如很多項目引入全量的babel-ployfill,往往會占很大的面積,可通過user-agent去請求在線服務里的包,不同版本的瀏覽器可能需要的包大小不同。
1. 首先應該使用更高版本的webpack,如現在是V4,它比V3版本提高了50%-98%的速度。
- nodejs版本更高,使用了更高版本的V8引擎
- ES6新的更優秀的語法,for of代替foreach, includes代替index of,map set代替 object,md4 代替md5, 字符串方式代替正則,多進程打包V4的thread-loader代替happy-pack
- mode:production方式,默認自動內置了10來中優化插件。
2. 充分利用緩存Cache
- babel-loader,加入參數Cache-Directory=true
- terser-webpack-plugin,配置cache:true,V3中使用uglifyJS-webpack-plugin(它也可以開啟並行壓縮,也有並行壓縮的版本)壓縮,但V4中不推薦使用了。
- hard-source-webpack-plugin,對模塊轉換緩存
他們都會在node_modules目錄下.cache文件夾來緩存結果,這樣就加快第二次打包速度。
3. 縮小構建目標,減少對JS/JSX的分析,減少文件的搜索范圍。
在loader中使用include、exclude來指定只分析某些目錄,或者不分析某些目錄
webpack4有一個resolve節點,比如alias,可以直接告訴webpack某些第三方包(react,react-dom...)從哪里去獲取,不用一層一層的遍歷查找, extendsion,指定為JS,mainField指定為 main。
4. 並行打包/壓縮
v3中使用的HappyPackPlugin(作者已經不維護升級了),ParallelUglifyJS並行壓縮(對ES6支持不好),因此V4中推薦使用thread-loader(多進程)和terser-webpack-plugin(並行壓縮)
5. DllPlugin和DLLReferencePlugin
官方維護的兩個插件,他們支持分離基礎包,提前把一些第三方基礎架構包分離出來,比如生成一個vendor.js, 放到html模塊中添加相對路徑引用,同時也可以引用放到CDN緩存中的包。
打包體積優化
1. mode:production
生產模式會默認使用10來個優化插件,比如TreeShaking(對代碼靜態分析,把沒用的代碼除去,減少體積),如沒用使用的JS方法,變量,返回值,if(false){ xxx }語句等篩除,他還可以通過scop-hoisting減少閉包代碼的生成。
css需要通過uncss和purifycssplugin做TreeShaking。
2. 圖片壓縮,會對圖片再次壓縮,這里和(url-loader)小圖片base64編碼內嵌到源文件中方式不同。
image-webpack-loader。
3. ployfill在線方法, 它很大,可以使用在線的CDN服務,它是一個在線服務器,根據user-agent只下載當前瀏覽器不支持的特性,相對包很小。
4. 壓縮,出掉空格,注釋,換行符,長方法名混淆壓縮為短方法名
mini-css-extract-plugin,parallelUglifyJSPlugin, terser-webpack-plugin
5. 代碼分割
- 首屏只加載首屏需要的JS
- 按需動態import,這個需要babel插件(babel-plugin-syntax-dynamic-import),可以把一些第三方庫,比如pdf,使用React.lazy和Suspense進行組件延遲加載,這里使用的是promise技術; 還可以使用react-loader等技術。
- 使用splitChunksPlugin,對調用n次的代碼,分成一個公共文件,webpack4中,它被內置放到optimization節點中進行配置。
- entry多入口,多頁面,SEO更好,頁面解耦。
- require.ensure(), commonJS中使用等。
webpack其他碎片知識
loader/plugin
babel-loader,ts-loader
less/scss-loader(less/scss轉換為css) -> css-loader(css轉換為CommonJSObject) -> style-loader(內嵌到Html頭部的style區域中),注意他們的順序需要正確
file-loader, 處理圖片(jpg,jpeg,png,gif),字體等
url-loader,可以把小文件內置到元素中,不用多一個請求。
postcss-loader + autoprefixer自動給css加前綴(-moz-,-webkit-,-ms-,-o-)
cleanwebpackplugin, 先清理生成目錄
raw-loader,減少http請求,比如一些初始化的css使用<script>require('raw-loader')內聯進來,避免閃動。
WDS (webpack-dev-server)
主要模塊是HMR-Server和HMR Runtime(瀏覽器端),HMR Server增量編譯,得出差異,通知HMR-Runtime,HMR-Runtime通過jsonp方式傳輸變化的數據,再對瀏覽器局部刷新。
文件指紋,版本管理
hash,一個項目一個
chunkHash,一個入口一個,一個頁面入口一個,頁面的css,js共有這個指紋
contentHash,一個文件一個,文件內容不變,它的值不變。
JS用chunkHash,如在output節點中的文件名使用:[name][chunkhash:8].js
css用contentHash,在miniCssExtractPlugin配置,fileName:[name][contenthash:8].css
注意:圖片文件的指紋,他與前面幾個不同,它的hash是它自身的md5值,所以options:{name:image/[name][hash:8].[ext]}
sourceMap
eval: 使用eval包裹模塊代碼
souce map:分離的一個個map文件
cheap: 不包括列信息
inline: JS和source map文件在一起
module,包含loader的source map
他們還可以排列組合出10多種類別,production模式下,設置為none
服務端渲染 SSR,同構應用
第一個請求就返回了首屏必要的data,由后端(Node)計算出Data后,renderToString插入(替換)到html模塊的placeholder占位符中,再返回給客戶端直接展示,常把數據掛在window.__initData__變量中。
good:減少請求數量,加快顯示,減少首屏白屏時間;SEO優化,有利於爬蟲分析;
bad:window,document等很多對象不能識別,需要hack處理,css不生效,需要使用css-in-jss等方式;fetch,ajax需要改成服務端兼容的axios,isomorphic-fetch;mode不能直接使用production,因為它會吃掉占位符;