Webpack問(wèn)答

到目前胧后,Webpack已發(fā)布到v3.8.1尊沸,網(wǎng)上有很多Webpack入門到精通的教程及文檔,此文就從問(wèn)答的角度梳理下Webpack的知識(shí)脈絡(luò)妇多。

基礎(chǔ)篇

1. 為什么要使用Webpack伤哺?

在面對(duì)復(fù)雜的業(yè)務(wù)需求和前端性能優(yōu)化的場(chǎng)景時(shí),會(huì)存在以下需要解決的問(wèn)題:

  • 前端模塊化工具
  • 異步模塊加載及管理
  • 樣式(Style)預(yù)處理:Scss者祖、Less立莉、Postcss等
  • 樣式(Style)后處理:autoprefixer、img資源內(nèi)聯(lián)七问、原子css蜓耻、css module
  • ES6語(yǔ)法、TypeScript械巡、CoffeeScript支持
  • 專有代碼處理:JSX媒熊、*.vue
  • 外部依賴模塊:Handlerbars等模板、gzip坟比、text芦鳍、json等
  • 公共模塊提取
  • 開(kāi)模模式自動(dòng)刷新瀏覽器
  • 長(zhǎng)效緩存處理
  • 代碼壓縮混淆
  • ...

等等以上如果人肉處理則會(huì)相當(dāng)麻煩,按照Webpack的模塊打包思路葛账,以上問(wèn)題都能很好的解決柠衅。

2. 什么是Webpack?

Webpack是模塊打包器:它做的事情是籍琳,分析你的項(xiàng)目結(jié)構(gòu)菲宴,找到JavaScript模塊以及其它的一些瀏覽器不能直接運(yùn)行的拓展語(yǔ)言(Scss,TypeScript等)趋急,并將其轉(zhuǎn)換和打包為合適的格式供瀏覽器使用喝峦。

3. Webpack和Browserify、Rollup呜达、Gulp谣蠢、Grunt、Seajs、Requirejs的區(qū)別眉踱?

以上工具的功能可分為兩類:

1. 任務(wù)管理器(Task Runner)

Gulp / Grunt是一種任務(wù)管理工具挤忙,能夠優(yōu)化前端工作流程。比如自動(dòng)刷新頁(yè)面谈喳、combo册烈、壓縮css、js婿禽、編譯less等等赏僧。簡(jiǎn)單來(lái)說(shuō),就是使用Gulp/Grunt扭倾,然后配置你需要的插件次哈,就可以把以前需要手工做的事情讓它幫你做了。

2. 模塊化解決方案

2.1 在線模塊方案

Seajs/Requirejs是一種在線"編譯" 模塊的方案吆录,相當(dāng)于在頁(yè)面上加載一個(gè) CMD/AMD 解釋器。這樣瀏覽器就認(rèn)識(shí)了 define琼牧、exports恢筝、module 這些東西。也就實(shí)現(xiàn)了模塊化巨坊。

2.2 模塊打包器

Browserify / Webpack / Rollup是一個(gè)預(yù)編譯模塊的方案撬槽,相比于上面 ,這個(gè)方案更加智能趾撵。沒(méi)用過(guò)browserify侄柔,這里以webpack為例。首先占调,它是預(yù)編譯的暂题,不需要在瀏覽器中加載解釋器。另外究珊,你在本地直接寫JS沼琉,不管是 AMD / CMD / ES6 風(fēng)格的模塊化腻扇,它都能認(rèn)識(shí),并且編譯成瀏覽器認(rèn)識(shí)的JS。以上三個(gè)工具都能完成對(duì)lib庫(kù)的打包虫碉,React,Vue罩驻,Ember悔常,Preact,D3瞬浓,Three.js初婆,Moment 以及其他許多知名的庫(kù)都使用 Rollup 。Rollup使用Tree Shaking技術(shù)精簡(jiǎn)Lib庫(kù),這個(gè)最新的Webpack也能做到烟逊,但是從最終的打包代碼中分析渣窜,Rollup做好的包會(huì)更純凈一些,性能更好一些宪躯。

4. Webpack的構(gòu)建思路乔宿?

一切都是模塊

就像JS文件可以視作“模塊”一樣,其他所有的一切(CSS访雪,圖片详瑞,HTML)都可以被視作模塊。也就是說(shuō)臣缀,你可以require("myJSfile.js")或者require("myCSSfile.css")坝橡。這意味著我們可以把任何靜態(tài)資源分割成可控的模塊,以供重復(fù)使用等不同的操作精置。

只加載“你需要的”和“你何時(shí)需要”的

典型的模塊加載器會(huì)把所有的模塊最終打包生成一個(gè)巨大的“bundle.js”文件计寇。但在很多實(shí)際的項(xiàng)目當(dāng)中,這個(gè)“bundle.js”文件體積可能會(huì)達(dá)到10MB~15MB脂倦,并且會(huì)一直不停進(jìn)行加載番宁!所以Webpack通過(guò)大量的特性去分割你的代碼,生成多個(gè)“bundle”片段赖阻,并且異步地加載項(xiàng)目的不同部分蝶押,因此只會(huì)為你加載“你需要的”和“你何時(shí)需要”的部分。

入門篇

入門主要是對(duì)Webpack配置的解讀及常見(jiàn)問(wèn)題點(diǎn)解答火欧。

1. 如何區(qū)分Webpack構(gòu)建形式(開(kāi)發(fā)環(huán)境 VS 生產(chǎn)環(huán)境)棋电?

按照Webpack文檔中說(shuō)明的,使用配置對(duì)象編寫配置文檔苇侵,一般Webpack構(gòu)建分為兩個(gè)環(huán)境:開(kāi)發(fā)赶盔、發(fā)布。因此需要三個(gè)配置:

  • webpack.base.config.js:基礎(chǔ)公共
  • webpack.dev.config.js:開(kāi)發(fā)模式
  • webpack.prod.config.js:發(fā)布模式

通過(guò)webpack-merge將配置合并榆浓,使用pkg.script管理構(gòu)建任務(wù)招刨。

為了在window和max上都能無(wú)縫兼容,這里使用了 cross-env 配置環(huán)境變量哀军,例如:

"scripts": {
    "clear": "rm -rf build&& mkdir build",
    "start": "npm run clear&& cross-env NODE_ENV=development webpack-dev-server --host 0.0.0.0 --devtool eval --progress --color --profile",
    "deploy": "npm run pre&& npm run clear&& cross-env NODE_ENV=production webpack -p --progress"
}

代碼中可使用如下方式判斷:

if (process.env.NODE_ENV === 'development') {
    // ...
}

2. Entry在MPA下如何寫沉眶?

MPA是Muti Page Application的縮寫,指多頁(yè)應(yīng)用程序杉适。這種需求常見(jiàn)于:

  1. 多頁(yè)靜態(tài)項(xiàng)目編寫
  2. 多頁(yè)后端渲染模板編寫

如果頁(yè)面不多谎倔,且模板路徑無(wú)規(guī)律的話,使用對(duì)象語(yǔ)法定義Entry配置猿推,具體Entry配置參考文檔片习。

如果頁(yè)面結(jié)構(gòu)已約定捌肴,且頁(yè)面眾多,這里建議使用JS代碼獲取入口路徑藕咏,比如像這個(gè)項(xiàng)目express-hbs-webpack-demo状知,約定了chunkName和入口JSindex: .../views/index/main.js

|   ├── views        // 前端各個(gè)頁(yè)面
|   |   |── common   // 公共Partials
|   |   |── layout.hbs
|   |   |── index    // 主頁(yè)
|   |   |   |── partials
|   |   |   |   |── content.hbs    // 頁(yè)面Partials
|   |   |   |   |── xxxxxxxx.hbs   // 其余頁(yè)面Partials
|   |   |   |   |── resource.ejs   // webpack的 HTMLHtmlWebpackPlugin 插件需要這個(gè)模板
|   |   |   |   └── resource.hbs   // 最終生成的hbs資源片段
|   |   |   |── index.hbs    // 頁(yè)面主結(jié)構(gòu)
|   |   |   |── main.js      // 頁(yè)面入口js,約定只能是index.js或者main.js
|   |   |   └── style.less   // 當(dāng)前頁(yè)面的樣式孽查,支持scss/less/css等
|   |   └── xxxxx    // 其余頁(yè)面
|   └── app.js       // 頁(yè)面公共部分饥悴,比如全局樣式及腳本,或者頁(yè)面初始化時(shí)的動(dòng)作

代碼示例如下(源碼)

/**
 * read path of js entry files(for webpack entry)
 * notice: the entry file name must be main.js or index.js, and only one exist
 */
exports.webpackEntry = function () {
  var webpackEntry = {} // for webpack entry
  glob.sync(`${config.viewsPath}/**/{main,index}.js`).forEach(function (entry) {
    var tmp = entry.split('/').splice(-3)
    var moduleName = tmp.slice(1, 2)[0]
    // eg: entry -> {about: '/Users/xxx/xxx/express-here/client/views/about/main.js'}
    webpackEntry[moduleName] = entry
  })
  return webpackEntry

4. 如何在移動(dòng)配置文件的同時(shí)不影響構(gòu)建結(jié)果盲再?

因?yàn)樵谂渲弥行枰褂孟鄬?duì)路徑獲取某些文件/模塊的位置西设,因此當(dāng)移動(dòng)Webpack配置時(shí)會(huì)影響資源引用,這個(gè)很好處理答朋,使用context即可贷揽,這使得你的配置獨(dú)立于 CWD(current working directory - 當(dāng)前執(zhí)行路徑)。

context: path.resolve(__dirname, "app")

4. 靜態(tài)資源托管到CDN時(shí)梦碗,構(gòu)建時(shí)資源如何改寫禽绪?

需要看下文檔中對(duì)output.publicPath的說(shuō)明。這里需要對(duì)三個(gè)常用屬性進(jìn)行解讀:

  • filename:決定了每個(gè)輸出 bundle 的名稱
  • path:這些 bundle 將寫入到 output.path 選項(xiàng)指定的目錄下洪规。
  • publicPath:將上面兩個(gè)準(zhǔn)備的路徑字符串前面加上外部路徑印屁,比如某個(gè)CDN:http://xx.xx.com/public/,此時(shí)原資源位置:js/xx.xxxxx.bundle.js --> http://xx.xx.com/public/js/xx.xxxxx.bundle.js

因此淹冰,增加publicPath屬性就可改寫資源名稱外部路徑。

5. 構(gòu)建Lib庫(kù)使用Rollup還是Webpack巨柒?

建議使用Rollup打包樱拴,編譯最終生成的模塊干凈清晰。

6. 引入模塊時(shí)洋满,rules的匹配順序是晶乔?

module是對(duì)import/require引入的資源進(jìn)行匹配處理的配置項(xiàng)。

module.noParse

首先牺勾,如果定義了module.noParse且匹配到時(shí)正罢,則不解析匹配的模塊,因此模塊內(nèi)部不應(yīng)該有import, require, define 的調(diào)用驻民,或任何其他導(dǎo)入機(jī)制翻具,這樣做可以忽略大型的 library 可以提高構(gòu)建性能。比如:

noParse: /jquery|lodash/

// 從 webpack 3.0.0 開(kāi)始
noParse: function(content) {
  return /jquery|lodash/.test(content);
}

module.rules

其次回还,如果上述未匹配到裆泳,則進(jìn)行module.rules匹配。數(shù)組中對(duì)象可通過(guò)屬性:test, include, excluderesource對(duì)資源匹配柠硕。

這里需要注意工禾,Webpack執(zhí)行最后一次匹配到的rule配置运提。因此,rule配置順序影響構(gòu)建闻葵,無(wú)用的rule全部去掉民泵。

7. 代碼中import/require找模塊的過(guò)程?

這個(gè)問(wèn)題也是對(duì)“模塊解析(Module Resolution)”過(guò)程的提問(wèn)槽畔。

Webpack使用enhanced-resolve來(lái)解析文件路徑栈妆。

1. 絕對(duì)路徑

不需要解析

2. 相對(duì)路徑

由當(dāng)前文件的上下文生成絕對(duì)路徑,之后轉(zhuǎn)到第一種情況竟痰。

3. 模塊路徑

import "module"; // 文件
import "module/lib/file"; // 文件夾

模塊將在 resolve.modules 中指定的所有目錄內(nèi)搜索签钩。

  1. resolve.alias查找有沒(méi)匹配到的別名,如果匹配到則替換路徑坏快,繼續(xù)向下
  2. 如果路徑指向文件
    • 如果文件自帶拓展名铅檩,則直接引用
    • 否則,使用resolve.extensions作為拓展名解析
  3. 如果路徑指向文件夾
    • 如果文件夾中包含 package.json 文件莽鸿,則按照順序查找 resolve.mainFields 配置選項(xiàng)中指定的字段(默認(rèn)為index)昧旨。并且 package.json 中的第一個(gè)這樣的字段確定文件路徑。
    • 如果 package.json 文件不存在或者 package.json 文件中的 main 字段沒(méi)有返回一個(gè)有效路徑祥得,則按照順序查找 resolve.mainFields 配置選項(xiàng)中指定的文件名兔沃,看是否能在 import/require 目錄下匹配到一個(gè)存在的文件名。
    • 文件擴(kuò)展名通過(guò) resolve.mainFields 選項(xiàng)采用類似的方法進(jìn)行解析级及。

8. 外部依賴如何處理乒疏?

比如公共模塊不希望打包到vendor中,而是通過(guò)CDN的方式引入的時(shí)候饮焦。這里參考下externals的配置怕吴。

配置:

externals: {
  jquery: 'jQuery'
}

代碼中:

import $ from 'jquery';

說(shuō)明:

jQuery<script>中的引入全局變量window.jQueryjquery為代碼中的引入名稱县踢。

9. Webpack管理的模塊如何掛載到window上转绷?

參考這個(gè)loader:expose-loader

8. 如何選擇SourceMap生成類型硼啤?

SourceMap 在devtool 屬性下設(shè)置:

  • 開(kāi)發(fā)模式時(shí)议经,使用source-map,他提供全套支持
  • 發(fā)布模式時(shí)谴返,使用cheap-module-eval-source-map煞肾,不影響構(gòu)建速度

進(jìn)階篇

1. 長(zhǎng)效緩存怎么處理?

原則

一般來(lái)說(shuō)嗓袱,我們發(fā)布到線上的資源都會(huì)再次進(jìn)行迭代開(kāi)發(fā)扯旷,或者增加新需求。為了保證用戶瀏覽的流暢性索抓,服務(wù)端會(huì)對(duì)靜態(tài)資源進(jìn)行長(zhǎng)效緩存钧忽。這里我們希望:

  • 新需求上線對(duì)緩存變更影響越小越好毯炮,最小化用戶本地更新,減少服務(wù)器請(qǐng)求帶寬
  • 用戶本地緩存不能和新上線的資源產(chǎn)生沖突
  • 資源和頁(yè)面在進(jìn)行升級(jí)時(shí)耸黑,不能出現(xiàn)資源引用的問(wèn)題(404)

傳統(tǒng)的方式使用資源名后面加query的方式處理緩存桃煎,比如:

var sourcePath = `http://xx.xx.com/public/js/index.js?time=${new Date().getTime()}`
var sourcePath = `http://xx.xx.com/public/js/index.js?v=${version()}`

類似的方式進(jìn)行防緩存處理,但是這樣的做法不便于管理大刊,且頁(yè)面和資源部署先后會(huì)出現(xiàn)沖突为迈。

因此,最暴力直接的方式是根據(jù)資源內(nèi)容改寫文件名的方式處理長(zhǎng)效緩存的問(wèn)題缺菌。當(dāng)資源發(fā)生變化時(shí)修改資源名稱葫辐,不變則不處理。使用這種方式伴郁,可以先替換靜態(tài)資源耿战,之后替換頁(yè)面,不會(huì)造成沖突焊傅。

以上的介紹建議閱讀這篇文章:用 webpack 實(shí)現(xiàn)持久化緩存剂陡。我這里總結(jié)最終執(zhí)行的部分。

Webpack解讀

Webpack使用內(nèi)建模塊系統(tǒng)管理模塊引用狐胎,為了保證模塊內(nèi)容穩(wěn)定鸭栖,這里需要將webpack runtime/chunk清單(經(jīng)常改變)模塊(相對(duì)固定)分離。

因此握巢,長(zhǎng)效緩存的核心就是:生成穩(wěn)定的模塊及模塊ID晕鹊,只有模塊內(nèi)容變動(dòng)才會(huì)改變模塊及模塊ID,只進(jìn)行必要的模塊及模塊ID變更暴浦。

措施

  • 合理劃分公共模塊
    • 提取vendor模塊
    • 使用CommonsChunkPlugin提取公共模塊
    • 提取manifest溅话,將模塊與webpack runtime/Manifest分離
  • 使用chunkhash改寫js模塊的文件名, 使模塊唯一化
  • 使用HashedModuleIdsPlugin穩(wěn)定模塊ID
  • 使用import()加載異步模塊
  • 使用inline-manifest-webpack-plugin將 manifest文件 inline到html中處理

2. 模塊熱替換的思路是?

參考文檔

3. 如何優(yōu)化構(gòu)建性能肉渴?

參考文檔

4. 如何在編譯后的代碼中添加作者信息公荧, 在文件后面添加版權(quán)信息带射?

使用new webpack.BannerPlugin('版權(quán)所有同规,翻版必究')處理

(完)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市窟社,隨后出現(xiàn)的幾起案子券勺,更是在濱河造成了極大的恐慌,老刑警劉巖灿里,帶你破解...
    沈念sama閱讀 212,332評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件关炼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡匣吊,警方通過(guò)查閱死者的電腦和手機(jī)儒拂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,508評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門寸潦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人社痛,你說(shuō)我怎么就攤上這事见转。” “怎么了蒜哀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,812評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵斩箫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我撵儿,道長(zhǎng)乘客,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,607評(píng)論 1 284
  • 正文 為了忘掉前任淀歇,我火速辦了婚禮易核,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘房匆。我一直安慰自己耸成,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,728評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布浴鸿。 她就那樣靜靜地躺著井氢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岳链。 梳的紋絲不亂的頭發(fā)上花竞,一...
    開(kāi)封第一講書(shū)人閱讀 49,919評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音掸哑,去河邊找鬼约急。 笑死,一個(gè)胖子當(dāng)著我的面吹牛苗分,可吹牛的內(nèi)容都是我干的厌蔽。 我是一名探鬼主播,決...
    沈念sama閱讀 39,071評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼摔癣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奴饮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起择浊,我...
    開(kāi)封第一講書(shū)人閱讀 37,802評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤戴卜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后琢岩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體投剥,經(jīng)...
    沈念sama閱讀 44,256評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,576評(píng)論 2 327
  • 正文 我和宋清朗相戀三年担孔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了江锨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吃警。...
    茶點(diǎn)故事閱讀 38,712評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖啄育,靈堂內(nèi)的尸體忽然破棺而出汤徽,到底是詐尸還是另有隱情,我是刑警寧澤灸撰,帶...
    沈念sama閱讀 34,389評(píng)論 4 332
  • 正文 年R本政府宣布谒府,位于F島的核電站,受9級(jí)特大地震影響浮毯,放射性物質(zhì)發(fā)生泄漏完疫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,032評(píng)論 3 316
  • 文/蒙蒙 一债蓝、第九天 我趴在偏房一處隱蔽的房頂上張望壳鹤。 院中可真熱鬧,春花似錦饰迹、人聲如沸芳誓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锹淌。三九已至,卻和暖如春赠制,著一層夾襖步出監(jiān)牢的瞬間赂摆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工钟些, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烟号,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,473評(píng)論 2 360
  • 正文 我出身青樓政恍,卻偏偏與公主長(zhǎng)得像汪拥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子篙耗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,606評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • GitChat技術(shù)雜談 前言 本文較長(zhǎng)迫筑,為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack鹤树,它要...
    蕭玄辭閱讀 12,681評(píng)論 7 110
  • 無(wú)意中看到zhangwnag大佬分享的webpack教程感覺(jué)受益匪淺铣焊,特此分享以備自己日后查看逊朽,也希望更多的人看到...
    小小字符閱讀 8,147評(píng)論 7 35
  • 最近在學(xué)習(xí) Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學(xué)習(xí) Webpack 的...
    My_Oh_My閱讀 8,173評(píng)論 40 247
  • publicPath指定了一個(gè)在瀏覽器中被引用的URL地址罕伯。 對(duì)于使用 和 加載器,當(dāng)文件路徑不同于他們的本地磁盤...
    飛呀飛哥閱讀 1,695評(píng)論 0 0
  • 開(kāi)場(chǎng)馬東說(shuō):講話是我們生活中最常用到的交流方式叽讳,但是會(huì)不會(huì)講話追他,工作和生活中的重要關(guān)鍵時(shí)刻坟募,你能不能講好話,對(duì)你的...
    求上進(jìn)的少女婧閱讀 1,398評(píng)論 0 0