webpack入門(mén)筆記

2018年8月25日更新蛾默,目前 webpack 已經(jīng)更新值 4.17.1 练链,本文所用到的各種庫(kù)或多或少有些過(guò)時(shí)帮非,跟著代碼操作下來(lái)可能會(huì)遇到各種問(wèn)題刮便,不過(guò) webpack 的主體思想沒(méi)變,所以還是希望本文對(duì)新學(xué) webpack 的你蓄诽,有所幫助薛训。此外用基于 webpack 4.17.1 寫(xiě)了一個(gè)簡(jiǎn)單的demo,如果遇到啥問(wèn)題仑氛,可以參考乙埃,之后應(yīng)該會(huì)逐步來(lái)完善這個(gè)demo闸英,如果有啥通用的想實(shí)現(xiàn)的功能,也可以在里面提 issue介袜。

2017年12月7日更新甫何,添加了clean-webpack-plugin,babel-env-preset,添加本文涉及到的所有代碼的示例,如果你在學(xué)習(xí)過(guò)程中出錯(cuò)了遇伞,可點(diǎn)擊此處參考(有些過(guò)時(shí)了辙喂,不要再 fork 了)

寫(xiě)在前面的話

閱讀本文之前,先看下面這個(gè)webpack的配置文件鸠珠,如果每一項(xiàng)你都懂巍耗,那本文能帶給你的收獲也許就比較有限,你可以快速瀏覽或直接跳過(guò)渐排;如果你和十天前的我一樣炬太,對(duì)很多選項(xiàng)存在著疑惑,那花一段時(shí)間慢慢閱讀本文驯耻,你的疑惑一定一個(gè)一個(gè)都會(huì)消失亲族;如果你以前沒(méi)怎么接觸過(guò)Webpack,而你又你對(duì)webpack感興趣可缚,那么動(dòng)手跟著本文中那個(gè)貫穿始終的例子寫(xiě)一次霎迫,寫(xiě)完以后你會(huì)發(fā)現(xiàn)你已明明白白的走進(jìn)了Webpack的大門(mén)。

// 一個(gè)常見(jiàn)的`webpack`配置文件const webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = {? ? ? ? entry: __dirname + "/app/main.js", //已多次提及的唯一入口文件? ? ? ? output: {? ? ? ? ? ? path: __dirname + "/build",? ? ? ? ? ? filename: "bundle-[hash].js"? ? ? ? },? ? ? ? devtool: 'none',? ? ? ? devServer: {? ? ? ? ? ? contentBase: "./public", //本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? ? ? historyApiFallback: true, //不跳轉(zhuǎn)? ? ? ? ? ? inline: true,? ? ? ? ? ? hot: true? ? ? ? },? ? ? ? module: {? ? ? ? ? ? rules: [{? ? ? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? ? ? use: ExtractTextPlugin.extract({? ? ? ? ? ? ? ? ? ? ? ? fallback: "style-loader",? ? ? ? ? ? ? ? ? ? ? ? use: [{? ? ? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? localIdentName: '[name]__[local]--[hash:base64:5]'? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? ? ? ? ? }],? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? ]? ? },? ? plugins: [? ? ? ? new webpack.BannerPlugin('版權(quán)所有帘靡,翻版必究'),? ? ? ? new HtmlWebpackPlugin({? ? ? ? ? ? template: __dirname + "/app/index.tmpl.html"http://new 一個(gè)這個(gè)插件的實(shí)例女气,并傳入相關(guān)的參數(shù)? ? ? ? }),? ? ? ? new webpack.optimize.OccurrenceOrderPlugin(),? ? ? ? new webpack.optimize.UglifyJsPlugin(),? ? ? ? new ExtractTextPlugin("style.css")? ? ]};

什么是WebPack,為什么要使用它测柠?

為什要使用WebPack

現(xiàn)今的很多網(wǎng)頁(yè)其實(shí)可以看做是功能豐富的應(yīng)用炼鞠,它們擁有著復(fù)雜的JavaScript代碼和一大堆依賴包。為了簡(jiǎn)化開(kāi)發(fā)的復(fù)雜度轰胁,前端社區(qū)涌現(xiàn)出了很多好的實(shí)踐方法

模塊化谒主,讓我們可以把復(fù)雜的程序細(xì)化為小的文件;

類(lèi)似于TypeScript這種在JavaScript基礎(chǔ)上拓展的開(kāi)發(fā)語(yǔ)言:使我們能夠?qū)崿F(xiàn)目前版本的JavaScript不能直接使用的特性,并且之后還能轉(zhuǎn)換為JavaScript文件使瀏覽器可以識(shí)別赃阀;

Scss霎肯,less等CSS預(yù)處理器

...

這些改進(jìn)確實(shí)大大的提高了我們的開(kāi)發(fā)效率,但是利用它們開(kāi)發(fā)的文件往往需要進(jìn)行額外的處理才能讓瀏覽器識(shí)別,而手動(dòng)處理又是非常繁瑣的榛斯,這就為WebPack類(lèi)的工具的出現(xiàn)提供了需求观游。

什么是Webpack

WebPack可以看做是模塊打包機(jī):它做的事情是,分析你的項(xiàng)目結(jié)構(gòu)驮俗,找到JavaScript模塊以及其它的一些瀏覽器不能直接運(yùn)行的拓展語(yǔ)言(Scss懂缕,TypeScript等),并將其轉(zhuǎn)換和打包為合適的格式供瀏覽器使用王凑。

WebPack和Grunt以及Gulp相比有什么特性

其實(shí)Webpack和另外兩個(gè)并沒(méi)有太多的可比性搪柑,Gulp/Grunt是一種能夠優(yōu)化前端的開(kāi)發(fā)流程的工具聋丝,而WebPack是一種模塊化的解決方案,不過(guò)Webpack的優(yōu)點(diǎn)使得Webpack在很多場(chǎng)景下可以替代Gulp/Grunt類(lèi)的工具工碾。

Grunt和Gulp的工作方式是:在一個(gè)配置文件中弱睦,指明對(duì)某些文件進(jìn)行類(lèi)似編譯,組合渊额,壓縮等任務(wù)的具體步驟况木,工具之后可以自動(dòng)替你完成這些任務(wù)。

Webpack的工作方式是:把你的項(xiàng)目當(dāng)做一個(gè)整體旬迹,通過(guò)一個(gè)給定的主文件(如:index.js)焦读,Webpack將從這個(gè)文件開(kāi)始找到你的項(xiàng)目的所有依賴文件,使用loaders處理它們舱权,最后打包為一個(gè)(或多個(gè))瀏覽器可識(shí)別的JavaScript文件。

如果實(shí)在要把二者進(jìn)行比較仑嗅,Webpack的處理速度更快更直接宴倍,能打包更多不同類(lèi)型的文件。

開(kāi)始使用Webpack

初步了解了Webpack工作方式后仓技,我們一步步的開(kāi)始學(xué)習(xí)使用Webpack鸵贬。

安裝

Webpack可以使用npm安裝,新建一個(gè)空的練習(xí)文件夾(此處命名為webpack sample project)脖捻,在終端中轉(zhuǎn)到該文件夾后執(zhí)行下述指令就可以完成安裝阔逼。

//全局安裝npm install -g webpack//安裝到你的項(xiàng)目目錄npm install --save-dev webpack

正式使用Webpack前的準(zhǔn)備

在上述練習(xí)文件夾中創(chuàng)建一個(gè)package.json文件,這是一個(gè)標(biāo)準(zhǔn)的npm說(shuō)明文件地沮,里面蘊(yùn)含了豐富的信息嗜浮,包括當(dāng)前項(xiàng)目的依賴模塊,自定義的腳本任務(wù)等等摩疑。在終端中使用npm init命令可以自動(dòng)創(chuàng)建這個(gè)package.json文件

npm init

輸入這個(gè)命令后危融,終端會(huì)問(wèn)你一系列諸如項(xiàng)目名稱,項(xiàng)目描述雷袋,作者等信息吉殃,不過(guò)不用擔(dān)心,如果你不準(zhǔn)備在npm中發(fā)布你的模塊楷怒,這些問(wèn)題的答案都不重要蛋勺,回車(chē)默認(rèn)即可。

package.json文件已經(jīng)就緒鸠删,我們?cè)诒卷?xiàng)目中安裝Webpack作為依賴包

// 安裝Webpacknpm install --save-dev webpack

回到之前的空文件夾抱完,并在里面創(chuàng)建兩個(gè)文件夾,app文件夾和public文件夾,app文件夾用來(lái)存放原始數(shù)據(jù)和我們將寫(xiě)的JavaScript模塊刃泡,public文件夾用來(lái)存放之后供瀏覽器讀取的文件(包括使用webpack打包生成的js文件以及一個(gè)index.html文件)乾蛤。接下來(lái)我們?cè)賱?chuàng)建三個(gè)文件:

index.html --放在public文件夾中;

Greeter.js-- 放在app文件夾中;

main.js-- 放在app文件夾中;

此時(shí)項(xiàng)目結(jié)構(gòu)如下圖所示

我們?cè)趇ndex.html文件中寫(xiě)入最基礎(chǔ)的html代碼每界,它在這里目的在于引入打包后的js文件(這里我們先把之后打包后的js文件命名為bundle.js,之后我們還會(huì)詳細(xì)講述)家卖。

<htmllang="en"><head><metacharset="utf-8"><title>Webpack Sample Projecttitle>head><body><divid='root'>div><scriptsrc="bundle.js">script>body>html>

我們?cè)贕reeter.js中定義一個(gè)返回包含問(wèn)候信息的html元素的函數(shù),并依據(jù)CommonJS規(guī)范導(dǎo)出這個(gè)函數(shù)為一個(gè)模塊:

// Greeter.jsmodule.exports = function() {? var greet = document.createElement('div');? greet.textContent = "Hi there and greetings!";? return greet;};

main.js文件中我們寫(xiě)入下述代碼眨层,用以把Greeter模塊返回的節(jié)點(diǎn)插入頁(yè)面。

//main.js const greeter = require('./Greeter.js');document.querySelector("#root").appendChild(greeter());

正式使用Webpack

webpack可以在終端中使用上荡,在基本的使用方法如下:

# {extry file}出填寫(xiě)入口文件的路徑趴樱,本文中就是上述main.js的路徑,# {destination for bundled file}處填寫(xiě)打包文件的存放路徑# 填寫(xiě)路徑的時(shí)候不用添加{}webpack {entry file} {destination for bundled file}

指定入口文件后酪捡,webpack將自動(dòng)識(shí)別項(xiàng)目所依賴的其它文件叁征,不過(guò)需要注意的是如果你的webpack不是全局安裝的,那么當(dāng)你在終端中使用此命令時(shí)逛薇,需要額外指定其在node_modules中的地址捺疼,繼續(xù)上面的例子,在終端中輸入如下命令

# webpack非全局安裝的情況node_modules/.bin/webpack app/main.js public/bundle.js

結(jié)果如下

可以看出webpack同時(shí)編譯了main.js 和Greeter,js,現(xiàn)在打開(kāi)index.html,可以看到如下結(jié)果

有沒(méi)有很激動(dòng)永罚,已經(jīng)成功的使用Webpack打包了一個(gè)文件了啤呼。不過(guò)在終端中進(jìn)行復(fù)雜的操作,其實(shí)是不太方便且容易出錯(cuò)的呢袱,接下來(lái)看看Webpack的另一種更常見(jiàn)的使用方法官扣。

通過(guò)配置文件來(lái)使用Webpack

Webpack擁有很多其它的比較高級(jí)的功能(比如說(shuō)本文后面會(huì)介紹的loaders和plugins),這些功能其實(shí)都可以通過(guò)命令行模式實(shí)現(xiàn)羞福,但是正如前面提到的惕蹄,這樣不太方便且容易出錯(cuò)的,更好的辦法是定義一個(gè)配置文件治专,這個(gè)配置文件其實(shí)也是一個(gè)簡(jiǎn)單的JavaScript模塊卖陵,我們可以把所有的與打包相關(guān)的信息放在里面。

繼續(xù)上面的例子來(lái)說(shuō)明如何寫(xiě)這個(gè)配置文件张峰,在當(dāng)前練習(xí)文件夾的根目錄下新建一個(gè)名為webpack.config.js的文件赶促,我們?cè)谄渲袑?xiě)入如下所示的簡(jiǎn)單配置代碼,目前的配置主要涉及到的內(nèi)容是入口文件路徑和打包后文件的存放路徑挟炬。

module.exports = {? entry:? __dirname + "/app/main.js",//已多次提及的唯一入口文件? output: {? ? path: __dirname + "/public",//打包后的文件存放的地方? ? filename: "bundle.js"http://打包后輸出文件的文件名? }}

注:“__dirname”是node.js中的一個(gè)全局變量鸥滨,它指向當(dāng)前執(zhí)行腳本所在的目錄。

有了這個(gè)配置之后谤祖,再打包文件婿滓,只需在終端里運(yùn)行webpack(非全局安裝需使用node_modules/.bin/webpack)命令就可以了,這條命令會(huì)自動(dòng)引用webpack.config.js文件中的配置選項(xiàng)粥喜,示例如下:

又學(xué)會(huì)了一種使用Webpack的方法凸主,這種方法不用管那煩人的命令行參數(shù),有沒(méi)有感覺(jué)很爽额湘。如果我們可以連webpack(非全局安裝需使用node_modules/.bin/webpack)這條命令都可以不用卿吐,那種感覺(jué)會(huì)不會(huì)更爽~旁舰,繼續(xù)看下文。

更快捷的執(zhí)行打包任務(wù)

在命令行中輸入命令需要代碼類(lèi)似于node_modules/.bin/webpack這樣的路徑其實(shí)是比較煩人的嗡官,不過(guò)值得慶幸的是npm可以引導(dǎo)任務(wù)執(zhí)行箭窜,對(duì)npm進(jìn)行配置后可以在命令行中使用簡(jiǎn)單的npm start命令來(lái)替代上面略微繁瑣的命令。在package.json中對(duì)scripts對(duì)象進(jìn)行相關(guān)設(shè)置即可衍腥,設(shè)置方法如下磺樱。

{? "name": "webpack-sample-project",? "version": "1.0.0",? "description": "Sample webpack project",? "scripts": {? ? "start": "webpack" // 修改的是這里,JSON文件不支持注釋婆咸,引用時(shí)請(qǐng)清除? },? "author": "zhang",? "license": "ISC",? "devDependencies": {? ? "webpack": "3.10.0"? }}

注:package.json中的script會(huì)安裝一定順序?qū)ふ颐顚?duì)應(yīng)位置竹捉,本地的node_modules/.bin路徑就在這個(gè)尋找清單中,所以無(wú)論是全局還是局部安裝的Webpack尚骄,你都不需要寫(xiě)前面那指明詳細(xì)的路徑了块差。

npm的start命令是一個(gè)特殊的腳本名稱,其特殊性表現(xiàn)在倔丈,在命令行中使用npm start就可以執(zhí)行其對(duì)于的命令憨闰,如果對(duì)應(yīng)的此腳本名稱不是start,想要在命令行中運(yùn)行時(shí)乃沙,需要這樣用npm run {script name}如npm run build蜀铲,我們?cè)诿钚兄休斎雗pm start試試渔呵,輸出結(jié)果如下:

現(xiàn)在只需要使用npm start就可以打包文件了录豺,有沒(méi)有覺(jué)得webpack也不過(guò)如此嘛夫嗓,不過(guò)不要太小瞧webpack民轴,要充分發(fā)揮其強(qiáng)大的功能我們需要修改配置文件的其它選項(xiàng)浪谴,一項(xiàng)項(xiàng)來(lái)看。

Webpack的強(qiáng)大功能

生成Source Maps(使調(diào)試更容易)

開(kāi)發(fā)總是離不開(kāi)調(diào)試寻咒,方便的調(diào)試能極大的提高開(kāi)發(fā)效率毛秘,不過(guò)有時(shí)候通過(guò)打包后的文件,你是不容易找到出錯(cuò)了的地方,對(duì)應(yīng)的你寫(xiě)的代碼的位置的叫挟,Source Maps就是來(lái)幫我們解決這個(gè)問(wèn)題的艰匙。

通過(guò)簡(jiǎn)單的配置,webpack就可以在打包時(shí)為我們生成的source maps抹恳,這為我們提供了一種對(duì)應(yīng)編譯文件和源文件的方法员凝,使得編譯后的代碼可讀性更高,也更容易調(diào)試奋献。

在webpack的配置文件中配置source maps健霹,需要配置devtool,它有以下四種不同的配置選項(xiàng)瓶蚂,各具優(yōu)缺點(diǎn)糖埋,描述如下:

devtool選項(xiàng)

配置結(jié)果

source-map

在一個(gè)單獨(dú)的文件中產(chǎn)生一個(gè)完整且功能完全的文件。這個(gè)文件具有最好的source map窃这,但是它會(huì)減慢打包速度瞳别;

cheap-module-source-map

在一個(gè)單獨(dú)的文件中生成一個(gè)不帶列映射的map,不帶列映射提高了打包速度杭攻,但是也使得瀏覽器開(kāi)發(fā)者工具只能對(duì)應(yīng)到具體的行祟敛,不能對(duì)應(yīng)到具體的列(符號(hào)),會(huì)對(duì)調(diào)試造成不便兆解;

eval-source-map

使用eval打包源文件模塊馆铁,在同一個(gè)文件中生成干凈的完整的source map。這個(gè)選項(xiàng)可以在不影響構(gòu)建速度的前提下生成完整的sourcemap锅睛,但是對(duì)打包后輸出的JS文件的執(zhí)行具有性能和安全的隱患埠巨。在開(kāi)發(fā)階段這是一個(gè)非常好的選項(xiàng),在生產(chǎn)階段則一定不要啟用這個(gè)選項(xiàng)衣撬;

cheap-module-eval-source-map

這是在打包文件時(shí)最快的生成source map的方法乖订,生成的Source Map 會(huì)和打包后的JavaScript文件同行顯示扮饶,沒(méi)有列映射具练,和eval-source-map選項(xiàng)具有相似的缺點(diǎn);

正如上表所述甜无,上述選項(xiàng)由上到下打包速度越來(lái)越快扛点,不過(guò)同時(shí)也具有越來(lái)越多的負(fù)面作用,較快的打包速度的后果就是對(duì)打包后的文件的的執(zhí)行有一定影響岂丘。

對(duì)小到中型的項(xiàng)目中陵究,eval-source-map是一個(gè)很好的選項(xiàng),再次強(qiáng)調(diào)你只應(yīng)該開(kāi)發(fā)階段使用它奥帘,我們繼續(xù)對(duì)上文新建的webpack.config.js铜邮,進(jìn)行如下配置:

module.exports = {? devtool: 'eval-source-map',? entry:? __dirname + "/app/main.js",? output: {? ? path: __dirname + "/public",? ? filename: "bundle.js"? }}

cheap-module-eval-source-map方法構(gòu)建速度更快,但是不利于調(diào)試,推薦在大型項(xiàng)目考慮時(shí)間成本時(shí)使用松蒜。

使用webpack構(gòu)建本地服務(wù)器

想不想讓你的瀏覽器監(jiān)聽(tīng)你的代碼的修改扔茅,并自動(dòng)刷新顯示修改后的結(jié)果,其實(shí)Webpack提供一個(gè)可選的本地開(kāi)發(fā)服務(wù)器秸苗,這個(gè)本地服務(wù)器基于node.js構(gòu)建召娜,可以實(shí)現(xiàn)你想要的這些功能,不過(guò)它是一個(gè)單獨(dú)的組件惊楼,在webpack中進(jìn)行配置之前需要單獨(dú)安裝它作為項(xiàng)目依賴

npm install --save-dev webpack-dev-server

devserver作為webpack配置選項(xiàng)中的一項(xiàng)玖瘸,以下是它的一些配置選項(xiàng),更多配置可參考這里

devserver的配置選項(xiàng)

功能描述

contentBase

默認(rèn)webpack-dev-server會(huì)為根文件夾提供本地服務(wù)器檀咙,如果想為另外一個(gè)目錄下的文件提供本地服務(wù)器雅倒,應(yīng)該在這里設(shè)置其所在目錄(本例設(shè)置到“public"目錄)

port

設(shè)置默認(rèn)監(jiān)聽(tīng)端口,如果省略,默認(rèn)為”8080“

inline

設(shè)置為true悼嫉,當(dāng)源文件改變時(shí)會(huì)自動(dòng)刷新頁(yè)面

historyApiFallback

在開(kāi)發(fā)單頁(yè)應(yīng)用時(shí)非常有用步咪,它依賴于HTML5 history API,如果設(shè)置為true殖演,所有的跳轉(zhuǎn)將指向index.html

把這些命令加到webpack的配置文件中,現(xiàn)在的配置文件webpack.config.js如下所示

module.exports = {? devtool: 'eval-source-map',? entry:? __dirname + "/app/main.js",? output: {? ? path: __dirname + "/public",? ? filename: "bundle.js"? },? devServer: {? ? contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄? ? historyApiFallback: true,//不跳轉(zhuǎn)? ? inline: true//實(shí)時(shí)刷新? } }

在package.json中的scripts對(duì)象中添加如下命令年鸳,用以開(kāi)啟本地服務(wù)器:

"scripts": {? ? "test": "echo \"Error: no test specified\" && exit 1",? ? "start": "webpack",? ? "server": "webpack-dev-server --open"? },

在終端中輸入npm run server即可在本地的8080端口查看結(jié)果

Loaders

鼎鼎大名的Loaders登場(chǎng)了趴久!

Loaders是webpack提供的最激動(dòng)人心的功能之一了。通過(guò)使用不同的loader搔确,webpack有能力調(diào)用外部的腳本或工具彼棍,實(shí)現(xiàn)對(duì)不同格式的文件的處理,比如說(shuō)分析轉(zhuǎn)換scss為css膳算,或者把下一代的JS文件(ES6座硕,ES7)轉(zhuǎn)換為現(xiàn)代瀏覽器兼容的JS文件,對(duì)React的開(kāi)發(fā)而言涕蜂,合適的Loaders可以把React的中用到的JSX文件轉(zhuǎn)換為JS文件华匾。

Loaders需要單獨(dú)安裝并且需要在webpack.config.js中的modules關(guān)鍵字下進(jìn)行配置,Loaders的配置包括以下幾方面:

test:一個(gè)用以匹配loaders所處理文件的拓展名的正則表達(dá)式(必須)

loader:loader的名稱(必須)

include/exclude:手動(dòng)添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)(可選)机隙;

query:為loaders提供額外的設(shè)置選項(xiàng)(可選)

不過(guò)在配置loader之前蜘拉,我們把Greeter.js里的問(wèn)候消息放在一個(gè)單獨(dú)的JSON文件里,并通過(guò)合適的配置使Greeter.js可以讀取該JSON文件的值,各文件修改后的代碼如下:

在app文件夾中創(chuàng)建帶有問(wèn)候信息的JSON文件(命名為config.json)

{? "greetText": "Hi there and greetings from JSON!"}

更新后的Greeter.js

var config = require('./config.json');module.exports = function() {? var greet = document.createElement('div');? greet.textContent = config.greetText;? return greet;};

注 由于webpack3.*/webpack2.*已經(jīng)內(nèi)置可處理JSON文件有鹿,這里我們無(wú)需再添加webpack1.*需要的json-loader旭旭。在看如何具體使用loader之前我們先看看Babel是什么?

Babel

Babel其實(shí)是一個(gè)編譯JavaScript的平臺(tái)葱跋,它可以編譯代碼幫你達(dá)到以下目的:

讓你能使用最新的JavaScript代碼(ES6持寄,ES7...)源梭,而不用管新標(biāo)準(zhǔn)是否被當(dāng)前使用的瀏覽器完全支持;

讓你能使用基于JavaScript進(jìn)行了拓展的語(yǔ)言稍味,比如React的JSX咸产;

Babel的安裝與配置

Babel其實(shí)是幾個(gè)模塊化的包,其核心功能位于稱為babel-core的npm包中仲闽,webpack可以把其不同的包整合在一起使用脑溢,對(duì)于每一個(gè)你需要的功能或拓展,你都需要安裝單獨(dú)的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)赖欣。

我們先來(lái)一次性安裝這些依賴包

// npm一次性安裝多個(gè)依賴模塊屑彻,模塊之間用空格隔開(kāi)npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react

在webpack中配置Babel的方法如下:

module.exports = {? ? entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件? ? output: {? ? ? ? path: __dirname + "/public",//打包后的文件存放的地方? ? ? ? filename: "bundle.js"http://打包后輸出文件的文件名? ? },? ? devtool: 'eval-source-map',? ? devServer: {? ? ? ? contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? historyApiFallback: true,//不跳轉(zhuǎn)? ? ? ? inline: true//實(shí)時(shí)刷新? ? },? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader",? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? presets: [? ? ? ? ? ? ? ? ? ? ? ? ? ? "env", "react"? ? ? ? ? ? ? ? ? ? ? ? ]? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? }? ? ? ? ]? ? }};

現(xiàn)在你的webpack的配置已經(jīng)允許你使用ES6以及JSX的語(yǔ)法了。繼續(xù)用上面的例子進(jìn)行測(cè)試顶吮,不過(guò)這次我們會(huì)使用React社牲,記得先安裝 React 和 React-DOM

npm install --save react react-dom

接下來(lái)我們使用ES6的語(yǔ)法,更新Greeter.js并返回一個(gè)React組件

//Greeter,jsimport React, {Component} from'react'import config from'./config.json';classGreeterextendsComponent{? render() {? ? return (? ? ? <div>? ? ? ? {config.greetText}? ? ? div>? ? );? }}exportdefault Greeter

修改main.js如下悴了,使用ES6的模塊定義和渲染Greeter模塊

// main.jsimport React from'react';import {render} from'react-dom';import Greeter from'./Greeter';render(<Greeter />, document.getElementById('root'));

重新使用npm start打包搏恤,如果之前打開(kāi)的本地服務(wù)器沒(méi)有關(guān)閉,你應(yīng)該可以在localhost:8080下看到與之前一樣的內(nèi)容湃交,這說(shuō)明react和es6被正常打包了熟空。

Babel的配置

Babel其實(shí)可以完全在 webpack.config.js 中進(jìn)行配置,但是考慮到babel具有非常多的配置選項(xiàng)搞莺,在單一的webpack.config.js文件中進(jìn)行配置往往使得這個(gè)文件顯得太復(fù)雜息罗,因此一些開(kāi)發(fā)者支持把babel的配置選項(xiàng)放在一個(gè)單獨(dú)的名為 ".babelrc" 的配置文件中。我們現(xiàn)在的babel的配置并不算復(fù)雜才沧,不過(guò)之后我們會(huì)再加一些東西迈喉,因此現(xiàn)在我們就提取出相關(guān)部分,分兩個(gè)配置文件進(jìn)行配置(webpack會(huì)自動(dòng)調(diào)用.babelrc里的babel配置選項(xiàng))温圆,如下:

module.exports = {? ? entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件? ? output: {? ? ? ? path: __dirname + "/public",//打包后的文件存放的地方? ? ? ? filename: "bundle.js"http://打包后輸出文件的文件名? ? },? ? devtool: 'eval-source-map',? ? devServer: {? ? ? ? contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? historyApiFallback: true,//不跳轉(zhuǎn)? ? ? ? inline: true//實(shí)時(shí)刷新? ? },? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? }? ? ? ? ]? ? }};

//.babelrc{? "presets": ["react", "env"]}

到目前為止挨摸,我們已經(jīng)知道了,對(duì)于模塊岁歉,Webpack能提供非常強(qiáng)大的處理功能得运,那那些是模塊呢。

一切皆模塊

Webpack有一個(gè)不可不說(shuō)的優(yōu)點(diǎn)刨裆,它把所有的文件都都當(dāng)做模塊處理澈圈,JavaScript代碼彬檀,CSS和fonts以及圖片等等通過(guò)合適的loader都可以被處理帆啃。

CSS

webpack提供兩個(gè)工具處理樣式表,css-loader 和 style-loader窍帝,二者處理的任務(wù)不同努潘,css-loader使你能夠使用類(lèi)似@import 和 url(...)的方法實(shí)現(xiàn) require()的功能,style-loader將所有的計(jì)算后的樣式加入頁(yè)面中,二者組合在一起使你能夠把樣式表嵌入webpack打包后的JS文件中。

繼續(xù)上面的例子

//安裝npm install --save-dev style-loader css-loader

//使用module.exports = {? ...? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader"? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? }};

請(qǐng)注意這里對(duì)同一個(gè)文件引入多個(gè)loader的方法疯坤。

接下來(lái)报慕,在app文件夾里創(chuàng)建一個(gè)名字為"main.css"的文件,對(duì)一些元素設(shè)置樣式

/* main.css */html {? box-sizing: border-box;? -ms-text-size-adjust: 100%;? -webkit-text-size-adjust: 100%;}*, *:before, *:after {? box-sizing: inherit;}body {? margin: 0;? font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}h1, h2, h3, h4, h5, h6, p, ul {? margin: 0;? padding: 0;}

我們這里例子中用到的webpack只有單一的入口压怠,其它的模塊需要通過(guò) import, require, url等與入口文件建立其關(guān)聯(lián)眠冈,為了讓webpack能找到”main.css“文件,我們把它導(dǎo)入”main.js “中菌瘫,如下

//main.jsimport React from'react';import {render} from'react-dom';import Greeter from'./Greeter';import'./main.css';//使用require導(dǎo)入css文件render(<Greeter />, document.getElementById('root'));

通常情況下蜗顽,css會(huì)和js打包到同一個(gè)文件中,并不會(huì)打包為一個(gè)單獨(dú)的css文件雨让,不過(guò)通過(guò)合適的配置webpack也可以把css打包為單獨(dú)的文件的雇盖。

上面的代碼說(shuō)明webpack是怎么把css當(dāng)做模塊看待的,咱們繼續(xù)看一個(gè)更加真實(shí)的css模塊實(shí)踐栖忠。

CSS module

在過(guò)去的一些年里崔挖,JavaScript通過(guò)一些新的語(yǔ)言特性,更好的工具以及更好的實(shí)踐方法(比如說(shuō)模塊化)發(fā)展得非常迅速庵寞。模塊使得開(kāi)發(fā)者把復(fù)雜的代碼轉(zhuǎn)化為小的狸相,干凈的,依賴聲明明確的單元捐川,配合優(yōu)化工具卷哩,依賴管理和加載管理可以自動(dòng)完成。

不過(guò)前端的另外一部分属拾,CSS發(fā)展就相對(duì)慢一些将谊,大多的樣式表卻依舊巨大且充滿了全局類(lèi)名,維護(hù)和修改都非常困難渐白。

被稱為CSS modules的技術(shù)意在把JS的模塊化思想帶入CSS中來(lái)尊浓,通過(guò)CSS模塊,所有的類(lèi)名纯衍,動(dòng)畫(huà)名默認(rèn)都只作用于當(dāng)前模塊栋齿。Webpack對(duì)CSS模塊化提供了非常好的支持,只需要在CSS loader中進(jìn)行簡(jiǎn)單配置即可襟诸,然后就可以直接把CSS的類(lèi)名傳遞到組件的代碼中瓦堵,這樣做有效避免了全局污染。具體的代碼如下

module.exports = {? ? ...? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true, // 指定啟用css modules? ? ? ? ? ? ? ? ? ? ? ? ? ? localIdentName: '[name]__[local]--[hash:base64:5]'// 指定css的類(lèi)名格式? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? }};

我們?cè)赼pp文件夾下創(chuàng)建一個(gè)Greeter.css文件來(lái)進(jìn)行一下測(cè)試

/* Greeter.css */.root {? background-color: #eee;? padding: 10px;? border: 3px solid #ccc;}

導(dǎo)入.root到Greeter.js中

import React, {Component} from'react';import config from'./config.json';import styles from'./Greeter.css';//導(dǎo)入classGreeterextendsComponent{? render() {? ? return (? ? ? <divclassName={styles.root}> //使用cssModule添加類(lèi)名的方法? ? ? ? {config.greetText}? ? ? div>? ? );? }}exportdefault Greeter

放心使用把歌亲,相同的類(lèi)名也不會(huì)造成不同組件之間的污染菇用。

CSS modules 也是一個(gè)很大的主題,有興趣的話可以去其官方文檔了解更多陷揪。

CSS預(yù)處理器

Sass 和 Less 之類(lèi)的預(yù)處理器是對(duì)原生CSS的拓展惋鸥,它們?cè)试S你使用類(lèi)似于variables, nesting, mixins, inheritance等不存在于CSS中的特性來(lái)寫(xiě)CSS杂穷,CSS預(yù)處理器可以這些特殊類(lèi)型的語(yǔ)句轉(zhuǎn)化為瀏覽器可識(shí)別的CSS語(yǔ)句,

你現(xiàn)在可能都已經(jīng)熟悉了卦绣,在webpack里使用相關(guān)loaders進(jìn)行配置就可以使用了耐量,以下是常用的CSS 處理loaders:

Less Loader

Sass Loader

Stylus Loader

不過(guò)其實(shí)也存在一個(gè)CSS的處理平臺(tái)-PostCSS,它可以幫助你的CSS實(shí)現(xiàn)更多的功能滤港,在其官方文檔可了解更多相關(guān)知識(shí)廊蜒。

舉例來(lái)說(shuō)如何使用PostCSS,我們使用PostCSS來(lái)為CSS代碼自動(dòng)添加適應(yīng)不同瀏覽器的CSS前綴溅漾。

首先安裝postcss-loader 和 autoprefixer(自動(dòng)添加前綴的插件)

npm install--save-dev postcss-loader autoprefixer

接下來(lái)劲藐,在webpack配置文件中添加postcss-loader,在根目錄新建postcss.config.js,并添加如下代碼之后樟凄,重新使用npm start打包時(shí)聘芜,你寫(xiě)的css會(huì)自動(dòng)根據(jù)Can i use里的數(shù)據(jù)添加不同前綴了。

//webpack.config.jsmodule.exports = {? ? ...? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? }}

// postcss.config.jsmodule.exports = {? ? plugins: [? ? ? ? require('autoprefixer')? ? ]}

至此缝龄,本文已經(jīng)談?wù)摿颂幚鞪S的Babel和處理CSS的PostCSS的基本用法汰现,它們其實(shí)也是兩個(gè)單獨(dú)的平臺(tái),配合webpack可以很好的發(fā)揮它們的作用叔壤。接下來(lái)介紹Webpack中另一個(gè)非常重要的功能-Plugins

插件(Plugins)

插件(Plugins)是用來(lái)拓展Webpack功能的瞎饲,它們會(huì)在整個(gè)構(gòu)建過(guò)程中生效,執(zhí)行相關(guān)的任務(wù)炼绘。

Loaders和Plugins常常被弄混嗅战,但是他們其實(shí)是完全不同的東西,可以這么來(lái)說(shuō)俺亮,loaders是在打包構(gòu)建過(guò)程中用來(lái)處理源文件的(JSX驮捍,Scss,Less..)脚曾,一次處理一個(gè)东且,插件并不直接操作單個(gè)文件,它直接對(duì)整個(gè)構(gòu)建過(guò)程其作用本讥。

Webpack有很多內(nèi)置插件珊泳,同時(shí)也有很多第三方插件,可以讓我們完成更加豐富的功能拷沸。

使用插件的方法

要使用某個(gè)插件色查,我們需要通過(guò)npm安裝它,然后要做的就是在webpack配置中的plugins關(guān)鍵字部分添加該插件的一個(gè)實(shí)例(plugins是一個(gè)數(shù)組)繼續(xù)上面的例子撞芍,我們添加了一個(gè)給打包后代碼添加版權(quán)聲明的插件秧了。

const webpack = require('webpack');module.exports = {...? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? },? ? plugins: [? ? ? ? new webpack.BannerPlugin('版權(quán)所有,翻版必究')? ? ],};

通過(guò)這個(gè)插件勤庐,打包后的JS文件顯示如下

這就是webpack插件的基礎(chǔ)用法了示惊,下面給大家推薦幾個(gè)常用的插件

HtmlWebpackPlugin

這個(gè)插件的作用是依據(jù)一個(gè)簡(jiǎn)單的index.html模板,生成一個(gè)自動(dòng)引用你打包后的JS文件的新index.html愉镰。這在每次生成的js文件名稱不同時(shí)非常有用(比如添加了hash值)米罚。

安裝

npm install --save-dev html-webpack-plugin

這個(gè)插件自動(dòng)完成了我們之前手動(dòng)做的一些事情,在正式使用之前需要對(duì)一直以來(lái)的項(xiàng)目結(jié)構(gòu)做一些更改:

移除public文件夾丈探,利用此插件录择,index.html文件會(huì)自動(dòng)生成,此外CSS已經(jīng)通過(guò)前面的操作打包到JS中了碗降。

在app目錄下隘竭,創(chuàng)建一個(gè)index.tmpl.html文件模板,這個(gè)模板包含title等必須元素讼渊,在編譯過(guò)程中动看,插件會(huì)依據(jù)此模板生成最終的html頁(yè)面,會(huì)自動(dòng)添加所依賴的 css, js爪幻,favicon等文件菱皆,index.tmpl.html中的模板源代碼如下:

<htmllang="en"><head><metacharset="utf-8"><title>Webpack Sample Projecttitle>head><body><divid='root'>div>body>html>

3.更新webpack的配置文件,方法同上,新建一個(gè)build文件夾用來(lái)存放最終的輸出文件

const webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {? ? entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件? ? output: {? ? ? ? path: __dirname + "/build",? ? ? ? filename: "bundle.js"? ? },? ? devtool: 'eval-source-map',? ? devServer: {? ? ? ? contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? historyApiFallback: true,//不跳轉(zhuǎn)? ? ? ? inline: true//實(shí)時(shí)刷新? ? },? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? },? ? plugins: [? ? ? ? new webpack.BannerPlugin('版權(quán)所有挨稿,翻版必究'),? ? ? ? new HtmlWebpackPlugin({? ? ? ? ? ? template: __dirname + "/app/index.tmpl.html"http://new 一個(gè)這個(gè)插件的實(shí)例仇轻,并傳入相關(guān)的參數(shù)? ? ? ? })? ? ],};

再次執(zhí)行npm start你會(huì)發(fā)現(xiàn),build文件夾下面生成了bundle.js和index.html奶甘。

Hot Module Replacement

Hot Module Replacement(HMR)也是webpack里很有用的一個(gè)插件篷店,它允許你在修改組件代碼后,自動(dòng)刷新實(shí)時(shí)預(yù)覽修改后的效果臭家。

在webpack中實(shí)現(xiàn)HMR也很簡(jiǎn)單疲陕,只需要做兩項(xiàng)配置

在webpack配置文件中添加HMR插件;

在Webpack Dev Server中添加“hot”參數(shù)钉赁;

不過(guò)配置完這些后鸭轮,JS模塊其實(shí)還是不能自動(dòng)熱加載的,還需要在你的JS模塊中執(zhí)行一個(gè)Webpack提供的API才能實(shí)現(xiàn)熱加載橄霉,雖然這個(gè)API不難使用窃爷,但是如果是React模塊,使用我們已經(jīng)熟悉的Babel可以更方便的實(shí)現(xiàn)功能熱加載姓蜂。

整理下我們的思路按厘,具體實(shí)現(xiàn)方法如下

Babel和webpack是獨(dú)立的工具

二者可以一起工作

二者都可以通過(guò)插件拓展功能

HMR是一個(gè)webpack插件,它讓你能瀏覽器中實(shí)時(shí)觀察模塊修改后的效果钱慢,但是如果你想讓它工作逮京,需要對(duì)模塊進(jìn)行額外的配額;

Babel有一個(gè)叫做react-transform-hrm的插件束莫,可以在不對(duì)React模塊進(jìn)行額外的配置的前提下讓HMR正常工作懒棉;

還是繼續(xù)上例來(lái)實(shí)際看看如何配置

const webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {? ? entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件? ? output: {? ? ? ? path: __dirname + "/build",? ? ? ? filename: "bundle.js"? ? },? ? devtool: 'eval-source-map',? ? devServer: {? ? ? ? contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? historyApiFallback: true,//不跳轉(zhuǎn)? ? ? ? inline: true,? ? ? ? hot: true? ? },? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? },? ? plugins: [? ? ? ? new webpack.BannerPlugin('版權(quán)所有草描,翻版必究'),? ? ? ? new HtmlWebpackPlugin({? ? ? ? ? ? template: __dirname + "/app/index.tmpl.html"http://new 一個(gè)這個(gè)插件的實(shí)例,并傳入相關(guān)的參數(shù)? ? ? ? }),? ? ? ? new webpack.HotModuleReplacementPlugin()//熱加載插件? ? ],};?

安裝react-transform-hmr

npm install --save-dev babel-plugin-react-transform react-transform-hmr

配置Babel

// .babelrc{? "presets": ["react", "env"],? "env": {? ? "development": {? ? "plugins": [["react-transform", {? ? ? "transforms": [{? ? ? ? "transform": "react-transform-hmr",? ? ? ? ? ? ? ? ? "imports": ["react"],? ? ? ? ? ? ? ? ? "locals": ["module"]? ? ? }]? ? }]]? ? }? }}

現(xiàn)在當(dāng)你使用React時(shí)策严,可以熱加載模塊了,每次保存就能在瀏覽器上看到更新內(nèi)容穗慕。

產(chǎn)品階段的構(gòu)建

目前為止,我們已經(jīng)使用webpack構(gòu)建了一個(gè)完整的開(kāi)發(fā)環(huán)境妻导。但是在產(chǎn)品階段逛绵,可能還需要對(duì)打包的文件進(jìn)行額外的處理,比如說(shuō)優(yōu)化倔韭,壓縮术浪,緩存以及分離CSS和JS。

對(duì)于復(fù)雜的項(xiàng)目來(lái)說(shuō)寿酌,需要復(fù)雜的配置胰苏,這時(shí)候分解配置文件為多個(gè)小的文件可以使得事情井井有條,以上面的例子來(lái)說(shuō)醇疼,我們創(chuàng)建一個(gè)webpack.production.config.js的文件碟联,在里面加上基本的配置,它和原始的webpack.config.js很像,如下

// webpack.production.config.jsconst webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {? ? entry: __dirname + "/app/main.js", //已多次提及的唯一入口文件? ? output: {? ? ? ? path: __dirname + "/build",? ? ? ? filename: "bundle.js"? ? },? ? devtool: 'null', //注意修改了這里僵腺,這能大大壓縮我們的打包代碼? ? devServer: {? ? ? ? contentBase: "./public", //本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? historyApiFallback: true, //不跳轉(zhuǎn)? ? ? ? inline: true,? ? ? ? hot: true? ? },? ? module: {? ? ? ? rules: [{? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? use: {? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? },? ? ? ? ? ? exclude: /node_modules/? ? ? ? }, {? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? use: ExtractTextPlugin.extract({? ? ? ? ? ? ? ? fallback: "style-loader",? ? ? ? ? ? ? ? use: [{? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? modules: true? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? }],? ? ? ? ? ? })? ? ? ? }]? ? },? ? plugins: [? ? ? ? new webpack.BannerPlugin('版權(quán)所有鲤孵,翻版必究'),? ? ? ? new HtmlWebpackPlugin({? ? ? ? ? ? template: __dirname + "/app/index.tmpl.html"http://new 一個(gè)這個(gè)插件的實(shí)例,并傳入相關(guān)的參數(shù)? ? ? ? }),? ? ? ? new webpack.HotModuleReplacementPlugin() //熱加載插件? ? ],};

//package.json{? "name": "test",? "version": "1.0.0",? "description": "",? "main": "index.js",? "scripts": {? ? "test": "echo \"Error: no test specified\" && exit 1",? ? "start": "webpack",? ? "server": "webpack-dev-server --open",? ? "build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"? },? "author": "",? "license": "ISC",? "devDependencies": {...? },? "dependencies": {? ? "react": "^15.6.1",? ? "react-dom": "^15.6.1"? }}

注意:如果是window電腦辰如,build需要配置為"build": "set NODE_ENV=production && webpack --config ./webpack.production.config.js --progress".謝謝評(píng)論區(qū)簡(jiǎn)友提醒普监。

優(yōu)化插件

webpack提供了一些在發(fā)布階段非常有用的優(yōu)化插件,它們大多來(lái)自于webpack社區(qū)琉兜,可以通過(guò)npm安裝凯正,通過(guò)以下插件可以完成產(chǎn)品發(fā)布階段所需的功能

OccurenceOrderPlugin :為組件分配ID,通過(guò)這個(gè)插件webpack可以分析和優(yōu)先考慮使用最多的模塊豌蟋,并為它們分配最小的ID

UglifyJsPlugin:壓縮JS代碼廊散;

ExtractTextPlugin:分離CSS和JS文件

我們繼續(xù)用例子來(lái)看看如何添加它們,OccurenceOrder 和 UglifyJS plugins 都是內(nèi)置插件梧疲,你需要做的只是安裝其它非內(nèi)置插件

npm install --save-dev extract-text-webpack-plugin

在配置文件的plugins后引用它們

// webpack.production.config.jsconst webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = {? ? entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件? ? output: {? ? ? ? path: __dirname + "/build",? ? ? ? filename: "bundle.js"? ? },? ? devtool: 'none',? ? devServer: {? ? ? ? contentBase: "./public",//本地服務(wù)器所加載的頁(yè)面所在的目錄? ? ? ? historyApiFallback: true,//不跳轉(zhuǎn)? ? ? ? inline: true,? ? ? ? hot: true? ? },? ? module: {? ? ? ? rules: [? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /(\.jsx|\.js)$/,? ? ? ? ? ? ? ? use: {? ? ? ? ? ? ? ? ? ? loader: "babel-loader"? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? exclude: /node_modules/? ? ? ? ? ? },? ? ? ? ? ? {? ? ? ? ? ? ? ? test: /\.css$/,? ? ? ? ? ? ? ? use: [? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ? loader: "style-loader"? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "css-loader",? ? ? ? ? ? ? ? ? ? ? ? options: {? ? ? ? ? ? ? ? ? ? ? ? ? ? modules: true? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }, {? ? ? ? ? ? ? ? ? ? ? ? loader: "postcss-loader"? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ]? ? ? ? ? ? }? ? ? ? ]? ? },? ? plugins: [? ? ? ? new webpack.BannerPlugin('版權(quán)所有允睹,翻版必究'),? ? ? ? new HtmlWebpackPlugin({? ? ? ? ? ? template: __dirname + "/app/index.tmpl.html"? ? ? ? }),? ? ? ? new webpack.optimize.OccurrenceOrderPlugin(),? ? ? ? new webpack.optimize.UglifyJsPlugin(),? ? ? ? new ExtractTextPlugin("style.css")? ? ],};

此時(shí)執(zhí)行npm run build可以看見(jiàn)代碼是被壓縮后的

緩存

緩存無(wú)處不在,使用緩存的最好方法是保證你的文件名和文件內(nèi)容是匹配的(內(nèi)容改變幌氮,名稱相應(yīng)改變)

webpack可以把一個(gè)哈希值添加到打包的文件名中缭受,使用方法如下,添加特殊的字符串混合體([name], [id] and [hash])到輸出文件名前

const webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = {..? ? output: {? ? ? ? path: __dirname + "/build",? ? ? ? filename: "bundle-[hash].js"? ? },? ...};

現(xiàn)在用戶會(huì)有合理的緩存了。

去除build文件中的殘余文件

添加了hash之后该互,會(huì)導(dǎo)致改變文件內(nèi)容后重新打包時(shí)米者,文件名不同而內(nèi)容越來(lái)越多,因此這里介紹另外一個(gè)很好用的插件clean-webpack-plugin。

安裝:

cnpm install clean-webpack-plugin --save-dev

使用:

引入clean-webpack-plugin插件后在配置文件的plugins中做相應(yīng)配置即可:

const CleanWebpackPlugin = require("clean-webpack-plugin");? plugins: [? ? ...// 這里是之前配置的其它各種插件new CleanWebpackPlugin('build/*.*', {? ? ? root: __dirname,? ? ? verbose: true,? ? ? dry: false? })? ]

關(guān)于clean-webpack-plugin的詳細(xì)使用可參考這里

總結(jié)

其實(shí)這是一年前的文章了蔓搞,趁周末重新運(yùn)行和修改了一下胰丁,現(xiàn)在所有的代碼都可以正常運(yùn)行,所用webpack基于最新的webpack3.5.3喂分。希望依舊能對(duì)你有幫助锦庸。

這是一篇好長(zhǎng)的文章,謝謝你的耐心妻顶,能仔細(xì)看到了這里酸员,大概半個(gè)月前我第一次自己一步步配置項(xiàng)目所需的Webpack后就一直想寫(xiě)一篇筆記做總結(jié)蜒车,幾次動(dòng)筆都不能讓自己滿意讳嘱,總覺(jué)得寫(xiě)不清楚。其實(shí)關(guān)于Webpack本文講述得仍不完全酿愧,不過(guò)相信你看完后已經(jīng)進(jìn)入Webpack的大門(mén)沥潭,能夠更好的探索其它的關(guān)于Webpack的知識(shí)了。

歡迎大家在文后發(fā)表自己的觀點(diǎn)討論嬉挡。

更新說(shuō)明

2017-12-11更新钝鸽,修改css module部分代碼及示例圖片,css module真的非常好用庞钢,希望大家都能用上拔恰。

2017年9月18日更新,添加了一個(gè)使用webpack配置多頁(yè)應(yīng)用的demo,可以點(diǎn)擊此處查看

2017年8月13日更新基括,本文依據(jù)webpack3.5.3將文章涉及代碼完全重寫(xiě)颜懊,所有代碼都在Mac上正常運(yùn)行過(guò)。希望依舊對(duì)你學(xué)習(xí)webpack有幫助风皿。

2017年8月16號(hào)更新:

最近在Gitchat上將發(fā)起了一場(chǎng)關(guān)于webpack的分享河爹,目的在于一起花最短的時(shí)間理解和學(xué)會(huì)webpack,感興趣的童鞋可以微信掃描注冊(cè)哈桐款。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咸这,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子魔眨,更是在濱河造成了極大的恐慌媳维,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遏暴,死亡現(xiàn)場(chǎng)離奇詭異侨艾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)拓挥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)唠梨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人侥啤,你說(shuō)我怎么就攤上這事当叭〔绻剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蚁鳖,是天一觀的道長(zhǎng)磺芭。 經(jīng)常有香客問(wèn)我,道長(zhǎng)醉箕,這世上最難降的妖魔是什么钾腺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮讥裤,結(jié)果婚禮上放棒,老公的妹妹穿的比我還像新娘。我一直安慰自己己英,他們只是感情好间螟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著损肛,像睡著了一般厢破。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上治拿,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天摩泪,我揣著相機(jī)與錄音,去河邊找鬼劫谅。 笑死见坑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的同波。 我是一名探鬼主播鳄梅,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼未檩!你這毒婦竟也來(lái)了戴尸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冤狡,失蹤者是張志新(化名)和其女友劉穎孙蒙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體悲雳,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挎峦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了合瓢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坦胶。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顿苇,到底是詐尸還是另有隱情峭咒,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布纪岁,位于F島的核電站凑队,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏幔翰。R本人自食惡果不足惜漩氨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遗增。 院中可真熱鬧叫惊,春花似錦、人聲如沸贡定。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缓待。三九已至,卻和暖如春渠牲,著一層夾襖步出監(jiān)牢的瞬間旋炒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工签杈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘫镇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓答姥,卻偏偏與公主長(zhǎng)得像铣除,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹦付,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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