webpack 從入門到工程實踐

from:https://www.jianshu.com/p/9349c30a6b3e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendationcss

GitChat技術雜談html

前言前端

本文較長,爲了節省你的閱讀時間,在文前列寫做思路以下:node

什麼是 webpack,它要解決的是什麼問題?react

對webpack的主要配置項進行分析,雖然不會涉及太多細節,可是期待在本節能讓咱們知曉若是咱們有什麼需求,咱們該從哪些配置項着手修改?jquery

分析 create-react-app 的基礎配置文件。webpack

分享一些本身工做中對webpack的實踐。git

本文的初衷是和你一塊兒理清 webpack 的使用邏輯,以便能更加容易的編寫及拓展本身項目所需的配置文件。es6

不過也得提早說明本文可能並非一篇好的能夠跟着操做的教程(想跟着一步步作的童鞋能夠看官方示例(github

https://webpack.js.org/guides/)和 webpack 入門,看這篇就夠了(

http://www.jianshu.com/p/42e11515c10f)。

換個角度看待 webpack

近年來,前端技術蓬勃發展,咱們想在 js 更方便的實現 html , 社區就出現了jsx,咱們以爲原生的css不夠好用,社區就提出了scss,less,針對前端項目愈來愈強的模塊化開發需求,社區出現了AMD, CommonJS , ES2015 import 等等方案。

遺憾的是,這些方案大多並不直接被瀏覽器支持,每每伴隨這些方案而生的還有另一些,讓這些新技術應用於瀏覽器的方案,咱們用 babel 來轉換下一代的 js,轉換 jsx;咱們用各類工具轉換 scss,less爲css;

咱們發現項目愈來愈複雜,代碼體積愈來愈大,又要開始尋找各類優化,壓縮,分割方案。前端工程化這個過程,真是讓咱們大費精力。咱們也大可能是在尋找前端模塊化解決方案的過程當中知曉了webpack。

的確,webpack的流行得益於野性生長的前端,其本質是一種前端模塊化打包解決方案,可是更重要的是它又是一個能夠融合運用各類前端新技術的平臺,明白webpack的使用哲學後,只須要簡單的配置,咱們就能夠爲所欲爲的在webpack項目中使用jsx/ts 使用babel/postcss等平臺提供的衆多其它功能,只需經過一條命令由源碼構建最終可用文件。

能夠不誇張的說webpack爲前端的工程化開發提供了一套相對容易和完整的解決方案。一些知名的腳手架工具,也大多基於webpack(好比create-react-app)。

webpack好難!我第一次複製別人的配置文件到個人項目中,發現以本身僅有的JS知識徹底看不懂時,也有這種感受。

後來發現有這種感受實際上是由於本身看待webpack的角度錯了,對大多數前端開發者而言,以往咱們接觸的各類庫,要麼相似jQuery,經過$符在前端項目中直接運行,所作的事情只在前端生效,要麼相似express.js,在node.js項目中直接require後就可使用,所作的事情只在後端生效。

webpack的不一樣之處就在於,雖然咱們的配置文件位於前端項目中,但實際上它卻運行於node.js,以後的處理結果又供前端使用(也可能供node使用)。因此學習以前,咱們轉變一下思惟,從node.js的角度來看webpack,不少事情就會簡單起來。

咱們對下圖必定不陌生,假設如今咱們手中有一系列相互關聯的文件js,jsx,css,less,jpg,咱們一步步的看看爲了把它們轉換爲項目最終須要的,瀏覽器可識別的文件,webpack都作了什麼。

顯示大圖

對 webpack 主要配置項的分析

若是不去考究細節,咱們大可把webpack簡化理解爲一個函數,配置文件則是其參數,傳入合理的參數後,運行函數就能獲得咱們想要的結果。

webpack也只是一個打包工具,它可不是什麼智能ai,咱們該從哪兒輸入文件,咱們想把輸出結果放哪裏,輸出結果應該長什麼樣,它都不知道。而咱們目前和webpack函數交互的惟一方法就是經過參數,這就涉及到webpack配置對象中兩個重要概念entry和output了,所以,咱們的配置對象至少具有如下結構:

// 第一階段{

entry:{},

output:{}

}

入口配置 entry

理想狀態是,咱們把全部本身編寫的文件都交給webpack,讓它找明裏面的關係,進過必定處理後,給出最終咱們想要的結果。

遺憾的是,webpack也不會機械學習,咱們手頭的一堆文件之間的關係是本身肯定的,通常咱們的項目都會存在一個或幾個主文件,其它的全部的文件(模塊)都直接或間接的連接到了這些文件。咱們在entry項中須要填寫的就是這些主文件的信息。

不過咱們也不要嫌棄webpack笨,經過咱們給的主文件路徑,經過分析它能構建最合適的依賴關係,這意味着只有用過的代碼纔會被打包,好比咱們在一個文件中寫了五個模塊,可是實際只用了其中一個,打包後的代碼只會包含引用過的模塊。

webpack中不少地方的配置都有多種寫法,這也是其讓人疑惑的地方之一,很遺憾,咱們的第一個配置對象entry就是如此。

entry能夠是三種值:

字符串:如entry:'./src/index.js',字符串也能夠是函數的返回值,如entry: () => './demo',單一入口占位符[name]值爲 main(關於佔位符,稍後詳述);

數組形式,如[react,react-dom],能夠把數組中的多個文件打包轉換爲一個chunk;

對象形式,若是咱們須要配置的是多頁應用,或者咱們要抽離出指定的模塊作爲公共代碼,就須要採用這種形式了,屬性名是佔位符[name]的值,屬性值能夠是上面的字符串和數組,以下:

// 值得注意的是入口文件有幾個就會生成幾個獨立的依賴圖譜。entry:{

main:'./src/index.js',

second:'./src/index2.js',

vendor: ['react','react-dom']

}

好吧,千辛萬苦,咱們在一堆各類類型的文件中找到了入口文件,這裏咱們假設爲./src/index.js,此時咱們的配置對象以下:

// 第二階段{

entry:{

main:'./src/index.js'

},

output:{}

}

webpack依據入口文件來構建依賴體系,每一個入口文件在打包完成後都具有其獨立的依賴圖譜,在此咱們暫時稱這些由主入口配置生成的文件爲主js文件。

輸出配置 output

output 配置項做用於打包文件的輸出階段,其做用在於告知 webpack 以何種方式輸出打包文件,關於output,webpack提供了衆多的可配置選項,咱們簡單介紹下最經常使用的選項。

output 基本配置項

咱們都另存過文件,當咱們另存一個文件時,咱們須要肯定另存的文件名和另存的路徑,webpack 將打包後的結果導出的過程就相似於此,此過程由 output 配置項控制,其最基本配置包括filename和path兩項。這兩項用以決定上述主js文件的存儲行爲。

不過咱們程序的首頁每每不需用到某個主js文件的全部代碼,實際開發中,咱們經常使用必定方法對代碼進行分割,方便按需加載,提高體驗。這類不具有獨立依賴的文件,咱們稱之爲chunkfile。chunkfile的命名,在output中對應chunkFilename項;

此外output的publicPath項,用於控制打包文件的相對或者絕對引用路徑,配置不當每每形成在運行時找不到文件。

咱們補充配置對象中output的配置,以下:

// 第三階段{

entry:{

main:'./src/index.js'

},

output:{

path: path.join(__dirname,'./dist'),

name:'js/bundle-[name]-[hash].js',

chunkFilename:'js/[name].chunk.js',

publicPath:'/dist/'

}

}

上述代碼中用到了佔位符[name],咱們對佔位符作統一解釋:

webpack中常見的佔位符有多種,常見的以下:

[name]:表明打包後文件的名稱,在entry或代碼中(以後會看到)肯定;

[id]:webpack給塊分配的內部chunk id,若是你沒有隱藏,你能在打包後的命令行中看到;

[hash]:每次構建過程當中,生成的惟一 hash 值;

[chunkhash]: 依據於打包生成文件內容的 hash 值,內容不變,值不變;

[ext]: 資源擴展名,如js,jsx,png等等;

output 其它配置

output配置項生效於保存這個過程,除了上面的基本配置,若是你想對這個階段的打包文件進行更改,均可在此配置項中進行相關設置。

好比output提供了衆多關於hash的屬性,讓咱們對[hash]佔位符的值有更加精細的控制,如生成方式,使用的算法,預設的長度等等;如chunkLoadTimeout屬性則容許咱們設置chunk文件的請求超時時間。

工具都是依賴於需求來使用的,若是你此階段有別的需求,可點擊更多配置尋找解決方案。

咱們已經知道了webpack中基本的輸入和輸出配置,可是webpack對各模塊的處理過程,目前爲止,對咱們仍是一個謎。考慮到webpack執行於node.js環境,其自己只能理解js文件,而咱們輸入的倒是一大堆不一樣格式的文件,毫無疑問,要作的第一件事情是對各種模塊進行處理,這就涉及到webpack中第三個重要配置對象了—-module。

對模塊的處理:module 的配置

使用webpack時,咱們經常據說,對webpack而言,全部的文件都是模塊,前文中我也經常混用模塊和文件,不過本質上模塊和文件仍是不一樣的,webpack裏,文件能夠當作模塊,而模塊卻不必定是一個獨立的文件。咱們先看看webpack內置支持的模塊類型:

ES2015 import(webpack2開始內置支持)。

CommonJS require。

AMD define 和 require 語句。

css/less/sass 中的@ import。

樣式中的 url(...) 和 html 文件中的

咱們知道 webpack 只能處理 js 文件,咱們的瀏覽器也可能不支持一些最新的 js 語法,基於此,咱們須要對傳入的模塊進行必定的預處理,這就涉及到 webpack 的又一核心概念 —- loader,使用loader,webpack容許咱們打包任何JS以外的靜態資源。

loader 的做用和基本用法

webpack中,loader的配置主要在module.rules中進行,module.rules是一個數組,咱們能夠把每一項看作一個Rule,每一個Rule主要作了如下兩件事:

識別文件類型,以肯定具體處理該數據的 loader,(Rule.test屬性)。

使用相關loader對文件進行相應的操做轉換,(Rule.use屬性)。

還記得前面咱們說過,咱們手頭的文件類型有 js,jsx,css,less,jpg 嗎?咱們看看在 webpack 中該如何處理和轉換它們。

注:如下 loader 使用前需經過 npm/cnpm/yarn 安裝:

module: {

rules: [{

test: /(\.jsx|\.js)$/,

use: {

loader: "babel-loader",

options: {

presets: ["es2015", "react"]

}

},

exclude: /node_modules/

}, {

test: /\.css$/,

use: ["style-loader", "css-loader"]

}, {

test: /\.less$/,

use: ["style-loader", "css-loader", "less-loader"]

}]

},

這就是 webpack 中 loader 的基本用法了,在 module.rules 數組中進行配置便可,module.rules 是一個數組,裏面每一項(一個Rule)表示以必定的規則匹配和處理某種或某幾種類型的文件。具體說來:

Rule.test:表示匹配規則,它是一個正則表達式。

Rule.use:表示針對匹配的文件將使用的處理loader,其值能夠是字符串,數組和對象,當是對象形式時,咱們可使用options等命令進行進一步的配置。

Rule中的其它一些規則也大多圍繞匹配條件和應用結果展開,如Rule.exclude和Rule.include表示應該匹配或不該該匹配某資源;Rule.oneOf表示對該資源只應用第一個匹配的loader;Rule.enforce則用於指定loader的種類。

loader 能夠作什麼

webpack 的強大之處在於,能夠輕鬆在其中應用其它平臺提供的功能,好比說 babel,postcss 自己都是獨立的平臺。在webpack 中只須要添加 babel-loader 和 postcss-loader 就可使用。

這兩個平臺自己也提供衆多的配置項,默認分別可在 .babelrc 和 postcss.config.js 中完成,webpack 並不影響這些配置文件的使用。

不過須要說明的可能不少童鞋是在學習webpack時才接觸這兩個平臺,致使在這兩個平臺上遇到的問題誤覺得是webpack的問題。

除了上述的轉換編譯,經過 loader,webpack 還容許咱們實現如下功能:

轉換編譯:

script-loader/babel-loader/ts-loader/coffee-loader等。

處理樣式:

style-loader/css-loader/less-loader/sass-loader/postcss-loader等。

處理文件:

raw-loader/url-loader/file-loader/等。

處理數據:csv-loader/xml-loader等。

處理模板語言:

html-loader/pug-loader/jade-loader/markdown-loader等。

清理和測試:

mocha-loader/eslint-loader等。

關於各個loader更詳細的介紹,可點擊loaders查看。

module.noParse

關於 module,另外一個經常使用的配置項爲module.noParse,經過它,咱們在構建過程當中能夠忽略大型的 library 以提升構建效率。

咱們來整理一下此階段,咱們的配置對象代碼,以下:

// 第四階段{

entry: {

main: './src/index.js'

},

output: {

path: path.join(__dirname, './dist'),

name: 'js/bundle-[name].js',

chunkFilename: 'js/[name].chunk.js',

publicPath: '/dist/'

}, module: {

rules: [{

test: /(\.jsx|\.js)$/,

use: {

loader: "babel-loader",

options: {

presets: ["es2015", "react"]

}

},

exclude: /node_modules/

}, {

test: /\.css$/,

use: ["style-loader", "css-loader"]

}, {

test: /\.less$/,

use: ["style-loader", "css-loader", "less-loader"]

}]

}

}

進過這一階段的處理,咱們的代碼其實已經能夠輸出使用了。不過這樣的輸出可能還不能讓人滿意,咱們想要抽離公共代碼;咱們想統一修改全部代碼中的某些值;咱們還想對代碼進行壓縮,去除全部的console… , 總之這一階段的代碼仍是存在很大的改進空間的,這就是plugin的用武之地了。

plugins 的配置

webpack 稱plugins爲其backbone,一切loader不能作的處理均可由plugins來作。此評價足見其重要性。

鑑於插件如此重要,webpack內置了衆多的經常使用的plugins,無需額外安裝就可直接使用。咱們先看看plugins的基本配置方法,而後再分類介紹一下經常使用的plugins。

plugins 的使用方法

plugins是一個數組,數組中的每一項都是某一個plugin的實例,plugins數組甚至能夠存在一個插件的多個實例。

下面代碼中,分別展現了webpack內置插件和第三方插件的使用方法:

// 第三方插件須要在安裝後引入const CleanWebpackPlugin = require("clean-webpack-plugin");

{

...

plugins:[ new webpack.DefinePlugin({ "process.env": {

NODE_ENV: JSON.stringify("production")

}

}), new CleanWebpackPlugin(["js"], {

root: __dirname + "/stu/",

verbose: true,

dry: false

})

]

}

一種插件其實就是一種函數,經過傳入不一樣的參數,插件可按咱們的需求實現不一樣的功能。不過插件數量衆多,咱們甚至還能夠本身來寫插件,每一個插件還有本身特定的配置規則,這也是webpack讓人以爲難學的地方之一,不過好在做爲一個工具,對於咱們大多數人最須要掌握的plugins並非那麼多,其它的待真的有相關需求再邊查邊學也不遲,webpack的插件列表可參看這裏(

https://webpack.js.org/plugins/)。

經常使用 plugins 的介紹

plugins功能衆多,可是大多數plugin的功能主要集中在兩方面:

對前一階段打包後的代碼進行處理,如添加替換一些內容,分割代碼爲多塊,添加一些全局設置等。

輔助輸出,如自動生成帶有連接的index.html,對生成文件存儲文件夾作必定的清理等。

對代碼進行處理

BannerPlugin:給代碼添加版權信息,如在plugins數組中添加new BannerPlugin(‘GitChat’)後能在打包生成的全部文件前添加註釋GitChat詳見(

https://webpack.js.org/plugins/banner-plugin/)。

CommonsChunkPlugin,用於抽離代碼,具備多種用途 詳情查看CommonsChunkPlugin(

https://webpack.js.org/plugins/commons-chunk-plugin)。

抽離不一樣文件的共享代碼,減小chunk間的重複代碼,有效利用緩存。

抽離可能整個項目都在使用的第三方模塊,好比 react react-dom。

將多個子 chunk 中的共用代碼打包進父 chunk 或使用異步加載的單獨chunk。

抽離 Manifest 這類每次打包都會變化的內容,減輕打包時候的壓力,提高構建速度。

CompressionWebpackPlugin:使用配置的算法(如gzip)壓縮打包生成的文件,詳見(

https://webpack.js.org/plugins/compression-webpack-plugin)。

DefinePlugin:建立一個在編譯時可配置的全局常量,若是你自定義了一個全局變量PRODUCTION,可在此設置其值來區分開發仍是生產環境詳見(

https://webpack.js.org/plugins/define-plugin/)。

EnvironmentPlugin:其實是DefinePlugin插件中對process.env進行設置的簡寫形式,如new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG'])將設置process.env.NODE_ENV='DEBUG',EnvironmentPlugin(

https://webpack.js.org/plugins/environment-plugin/)。

ExtractTextWebpackPlugin:抽離css文件爲單獨的css文件,詳見(

https://webpack.js.org/plugins/extract-text-webpack-plugin)。

ProvidePlugin:全局自動加載模塊,如添加new webpack.ProvidePlugin({$: 'jquery', jQuery: 'jquery'})後,則全局不用在導入jquery就能夠直接使用$,ProvidePlugin(

https://webpack.js.org/plugins/provide-plugin/)。

UglifyjsWebpackPlugin:使用前須要先安裝,基於UglifyJS壓縮代碼,支持其全部配置 UglifyjsWebpackPlugin(

https://webpack.js.org/plugins/uglifyjs-webpack-plugin/)。

輔助輸出打包後的代碼

HtmlWebpackPlugin:使用前須要先安裝,爲你自動生成一個html文件,該文件將自動依據entry的配置引入依賴,若是你的文件名中添加了[hash]等佔位符,這將很是有用, 詳見(

https://webpack.js.org/plugins/html-webpack-plugin/)。

CleanWebpackPlugin:使用前須要先安裝,此插件容許你在配置之後,每次打包時,清空所配置的文件夾,若是你每次打包的文件名不一樣,這將很是有用 GitHub - clean-webpack-plugin(

https://github.com/johnagan/clean-webpack-plugin)。

經過上述對不一樣插件的描述,你必定大體明白了,插件能夠作什麼,以後在開發的過程當中,若是你遇到的什麼須要在此階段解決的問題,大可搜索看看是否有相關的插件,推薦查閱awesome-webpack(

https://github.com/webpack-contrib/awesome-webpack#webpack-plugins)。

學習了插件之後,如今咱們的配置對象是以下這樣:

// 第5階段{

entry: {

main: './src/index.js'

},

output: {

path: path.join(__dirname, './dist'),

name: 'js/bundle-[name].js',

chunkFilename: 'js/[name].chunk.js',

publicPath: '/dist/'

}, module: {

rules: [{

test: /(\.jsx|\.js)$/,

use: {

loader: "babel-loader",

options: {

presets: ["es2015", "react"]

}

},

exclude: /node_modules/

}, {

test: /\.css$/,

use: ["style-loader", "css-loader"]

}, {

test: /\.less$/,

use: ["style-loader", "css-loader", "less-loader"]

}]

},

plugins: [ new webpack

.optimize

.CommonsChunkPlugin({

name: 'vendor',

filename: "js/[name]-[chunkhash].js"

}), new webpack.optimize.CommonsChunkPlugin({

name: "manifest",

minChunks: Infinity

}), new webpack.ProvidePlugin({

Promise: "exports-loader?global.Promise!es6-promise",

fetch: "exports-loader?self.fetch!whatwg-fetch"

}), new HtmlWebpackPlugin({

filename: "index.html",

template: "app/index.html",

inject: "body"

}), new CleanWebpackPlugin(["js"], {

root: __dirname + "/stu/",

verbose: true,

dry: false

}), new webpack.DefinePlugin({ "process.env": {

NODE_ENV: JSON.stringify("production")

}

})

]

}

至此,從輸入entry->處理loaders/plugins->輸出output,咱們講解了 webpack 的核心功能,不過webpack還提供其它的一些配置項,這些配置項大多從兩方面起做用,輔助開發、對構建過程當中的一些細節作調整。對這些屬性,下面只作簡單的介紹。

其它的一些配置

輔助開發的相關屬性

devtool:

打包後的代碼和原始的代碼每每存在較大的差別,此選項控制是否生成,以及如何生成 source map,用以幫助你進行調試,詳情可查看 Devtool(

https://webpack.js.org/configuration/devtool/)。

devServer:

經過配置devServer選項,你能夠開啓一個本地服務器,webpack爲此本地服務器提供了很是多的配置選項,查看 dev-server (

https://webpack.js.org/configuration/dev-server/),你會發現經過合適的配置,你能夠擁有全部本地服務器可提供的功能。

watch:

啓用 Watch 模式後,webpack 將持續監放任何已解析文件的更改,從新構建文件,Watch 模式默認關閉,在開發時候若是開啓會很方便。

watchOptions:

一組用來定製 Watch 模式的選項: 詳見 watch(

https://webpack.js.org/configuration/watch/)。

performance:

本配置讓你設置打包後命令行中該如何展現性能提示,好比是否開啓提示,資源若是超過某個大小時該警告仍是報錯,詳見 performance (

https://webpack.js.org/configuration/performance/)。

stats:

本選項讓你配置打包過程當中輸出的內容,如沒有輸出none,標準輸出normal,所有輸出verbose,只輸出錯誤errors-only等等。

精細配置相關屬性

content:設置基礎路徑,默認使用當前目錄。

resolve:

肯定模塊如何被解析,webpack已經提供了合理的默認值,不過經過你的自定義配置,能夠對模塊解析實現更加精細的控制,如對某些經常使用模塊能夠經過設置別名以更容易引用,也可在此處設置可被忽略的後綴名,詳見 resolve(

https://webpack.js.org/configuration/resolve/)。

target:

告知 webpack 須要打包的代碼執行的環境,針對 node 和 web 打包過程會有所不一樣,詳見Target(

https://webpack.js.org/configuration/target/)。

externals:

讓打包生成的代碼中不添加某依賴項,而讓這些依賴項直接從用戶環境中獲取,在進行庫的開發時很是有用。

node:

是一個對象,其中每一個屬性都是 Node.js 全局變量或模塊的名稱,每一項的設置值均可以是(true/mock/empty/false)中的一種,以肯定這些node中的對象在其它環境中是否可用。

此外webpack還具有其它一些用的比較少的配置對象,詳見 Other Options(

https://webpack.js.org/configuration/other-options/)。

至此,咱們瞭解了webpack經常使用的配置項及其意義。爲了檢測咱們的學習成果,咱們一塊兒分析一箇中等項目中的webpack配置文件。配置文件來自於create-react-app,使用create-react-app新建項目後,執行npm run eject可看到多個配置文件,這裏咱們選擇webpack.dev.js。

分析 create-react-app 中 webpack 的配置

const path = require('path');const webpack = require('webpack');const HtmlWebpackPlugin = require('html-webpack-plugin');const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');const eslintFormatter = require('react-dev-utils/eslintFormatter');const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');module.exports = {

devtool: 'cheap-module-source-map',

entry: [ require.resolve('react-dev-utils/webpackHotDevClient'), require.resolve('./polyfills'), require.resolve('react-error-overlay'), 'src/index.js'

],

output: {

path: '/build/',

pathinfo: true,

filename: 'static/js/bundle.js',

chunkFilename: 'static/js/[name].chunk.js',

publicPath: '',

devtoolModuleFilenameTemplate: info =>

path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),

},

resolve: {

modules: ['node_modules'],

extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'],

alias: { 'react-native': 'react-native-web',

},

plugins: [ new ModuleScopePlugin('/src'),

],

}, module: {

strictExportPresence: true,

rules: [{

test: /\.(js|jsx)$/,

enforce: 'pre',

use: [{

options: {

formatter: eslintFormatter,

},

loader: require.resolve('eslint-loader'),

}, ],

include: 'src',

}, {

exclude: [/\.html$/,/\.(js|jsx)$/,/\.css$/,/\.json$/,/\.bmp$/,/\.gif$/,/\.jpe?g$/,/\.png$/],

loader: require.resolve('file-loader'),

options: {

name: 'static/media/[name].[hash:8].[ext]',

},

}, {

test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],

loader: require.resolve('url-loader'),

options: {

limit: 10000,

name: 'static/media/[name].[hash:8].[ext]',

},

}, {

test: /\.(js|jsx)$/,

include: 'src',

loader: require.resolve('babel-loader'),

options: {

cacheDirectory: true,

},

}, {

test: /\.css$/,

use: [ require.resolve('style-loader'), {

loader: require.resolve('css-loader'),

options: {

importLoaders: 1,

},

}, {

loader: require.resolve('postcss-loader'),

options: {

...

},

},

],

}, ],

},

plugins: [ new InterpolateHtmlPlugin({

NODE_ENV:'development',

PUBLIC_URL:''

}), new HtmlWebpackPlugin({

inject: true,

template: 'public/index.html',

}), new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env':{

NODE_ENV:"development",

PUBLIC_URL:'" "'

}

}), new webpack.HotModuleReplacementPlugin(), new CaseSensitivePathsPlugin(), new WatchMissingNodeModulesPlugin(paths.appNodeModules), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),

],

node: {

dgram: 'empty',

fs: 'empty',

net: 'empty',

tls: 'empty',

},

performance: {

hints: false,

},

};

對可能和你看到的webpack.config.dev.js有所不一樣的說明:

npm run reject 以前,對create-react-app的一些設置會影響這裏看到的配置文件。

原始的 webpack.config.dev.js 中,部分值由外部函數生成,相關值,在上述代碼中直接改成了肯定的結果,如env.raw在上述代碼中被替換爲:

{

NODE_ENV:'development',

PUBLIC_URL:''

}

create-react-app在開發環境並不生成真實的文件到硬盤,上述代碼中的部分路徑可能有誤,見諒。

推薦在看下面的分析前,花三分鐘看看上述文件,若是都能看得懂,那麼恭喜你,你已經明白webpack的運做方式了,快去本身的項目中實踐吧,若是還有疑惑,也沒關係,咱們一塊兒來分析。

webpack.config.dev.js 執行於 node 環境

首先,咱們應該明確webpack.config.dev.js執行於node環境,目的在於返回webpack須要的配置對象,所以其中可使用node提供的一些特殊變量和語法,好比__dirname,又如引入模塊時採用CommonJS模式。

此文件的開頭,首先經過require語句引入了path,webpack和一系列webpack插件,除了HtmlWebpackPlugin在前文中咱們見過,其它的咱們都不曾見過,其實這些大可能是create-react-app針對webpack已有的插件改進或新開發的插件,因此不熟悉也正常,隨後咱們將一個個的弄清楚它們是幹嗎的。

對 module.exports 的分析

devtool

此處的配置值爲cheap-module-source-map,表明不帶列映射的 SourceMap,將加載的 Source Map 簡化爲每行單獨映射。

entry

此處的entry是一個數組,表明着四項的代碼都會添加到打包結果之中。

webpackHotDevClient能夠被看作具備更好體驗的WebpackDevServer。

./ployfill.js用以在瀏覽器中支持

promise/fetch/object-assign。

react-error-overlay在開發環境中使用,強制顯示錯誤頁面。

./src/index.js則是咱們的app的主入口。

output

在實際使用create-react-app的過程當中,咱們並看不見開發環境的打包結果,所以此處的說明僅供參考。

path指定,打包後文件存放的位置爲/build/。

pathinfo爲true,在打包文件後,在其中所包含引用模塊的信息,這在開發環境中有利於調試。

filename指定了打包的名字和基本的引用路徑static/js/bundle.js。

chunkFilename:指定了非入口文件的名稱static/js/[name].chunk.js。

publicPath:指定服務器讀取時的路徑,此處設置爲。

devtoolModuleFilenameTemplate:這裏是一個函數,指定了map位於磁盤的位置。

resolve

modules:指定了模塊的搜索的位置,這裏設置爲node_modules。

extensions:指明在引用模塊時哪些後綴名能夠忽略,這裏忽略的文件名包括.js/.jsx/.web.js/.web.jsx等。

alias:建立 import 或 require 的別名,使得部分模塊的引用變得簡單,安裝上文的設置,如今咱們能夠直接引用react-native和react-native-web了。

plugins:此處使用了 ModuleScopePlugin 的實例,用以限制本身編寫的模塊只能從src目錄中引入。

modules

strictExportPresence:這裏設置爲 true,代表文件中若是缺乏 exports 時會直接報錯而不是警告。

rules:

Rule1:對 js/jsx 文件前置使用 eslintFormatter,設置 formatter 格式爲 eslintFormatter。

Rule2:對exclude中的衆多文件類型不使用file-loader,並設置其它文件打包後的名稱按'static/media/[name].[hash:8].[ext]'格式設置。

Rule3: 對js/jsx文件調用babel-loader處理轉換。

Rule4: 對css文件,按順序調用style-loader,css-loader,postcss-loader進行處理。

plugins

這裏的一些插件,有的可能咱們還比較陌生,咱們一一介紹。

InterpolateHtmlPlugin:和HtmlWebpackPlugin串行使用,容許在index.html中添加變量。

HtmlWebpackPlugin:自動生成帶有入口文件引用的index.html。

NamedModulesPlugin:當開啓 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境。

DefinePlugin:這裏咱們設置了process.env.NODE_ENV的值爲development。

HotModuleReplacementPlugin:啓用模塊熱替換。

CaseSensitivePathsPlugin:若是路徑有誤則直接報錯。

WatchMissingNodeModulesPlugin:此插件容許你安裝庫後自動從新構建打包文件。

new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/):忽略所匹配的moment.js。

node

設置node的dgram/fs/let/tls模塊的的值,若是在其它環境中使用時值爲empty。

performance

hints: false:不提示測試環境的打包結果。

上文一直討論的是,webpack各設置項的基本意義,目的在於讓你在有相關需求時,能知道該從哪一項下手查詢。不過看到這裏,若是你以前從未上手操做過webpack可能依舊不知道該如何使用,下面我分析一下,我在本身的項目中是如何使用的。

一些工程實踐建議

官方文檔的 guides (

https://doc.webpack-china.org/guides/) 部分已經就如何實踐提出了較多的建議,建議閱讀如下內容前先行閱讀。

結合 npm 使用

webpack在安裝後有多種調用方法。

在命令行中直接傳入參數使用(這個實際我用的比較少)。

自定義 webpack.config.js文件,在其中完成配置,而後在命令行中執行webpack --config webpack.config.js來使用,配置文件能夠是任何其它名稱(若是是webpack.config.js,咱們直接使用webpack命令)。

結合npm使用,在package.json文件中的scripts對象中添加相關命令使用,以後經過npm run使用,以下:

"scripts": { "build:prod": "webpack --progress --colors --watch --config webpack.prod.js", "build:dev": "webpack --progress --colors --watch --config webpack.dev.js"}

上面咱們分別構建了webpack.prod.js和webpack.dev.js來分別生成開發環境和生產環境的代碼,在命令行中執行npm run build:prod和npm run build:dev便可生成對應代碼。

爲生產環境指定合理的緩存

關於緩存,官方文檔中有一節講解的很是詳細,請參見 緩存(

https://doc.webpack-china.org/guides/caching)。

合理分割代碼

webpack提供了三種分割代碼的方法,分別是經過entry,經過CommonsChunkPlugin插件和經過動態import(在webpack1.x中時也經常使用require.ensure來依據路由分割代碼)。

entry的配置經常使用於多頁應用,CommonsChunkPlugin的使用前文已作簡要敘述,下面簡單敘述下代碼分割原則及我實際工做中是如何使用動態import來分割代碼的。

分割原則

目前工做中主要依據兩個原則來分隔代碼:

前端路由:依據路由對應的頁面進行分割,這種分割以後的體驗相似於小程序中每次打開新頁加載對應頁面的js文件。

針對邏輯交互比較複雜的頁面,若是某個較複雜的組件需被某操做觸發後才呈現,也會把該組件分割出來。

分割方法

咱們知道動態import返回值實際上是一個Promise,基於此,對應於我用的React,我常採用如下函數輔助加載。

// lib.js 定義懶加載函數module.exports.withLazyLoading = function withLazyLoading(getComponent,Spinner = null) { return class LazyLoadingWrapper extends React.Component {

constructor(props) {

super(props); this.state = ({

Component: null,

})

}

componentWillMount() { const {onLoadingStart, onLoadingEnd, onError} = this.props;

onLoadingStart();

getComponent()

.then(esModule => { this.setState({Component: esModule.default})

})

.catch(err => {

onError(err, this.props)

})

}

render() { const {Component} = this.state; if (!Component) return Spinner; return

}

}

};

對代碼的分割方法以下:

// 在須要的地方調用懶加載函數import {withLazyLoading} from "lib";// import {Loading} from 'Loadings';

export default withLazyLoading(

() => { return import (/* webpackChunkName: "ConCard" */ "../../containers/ConCard.js")

}, Loading());

簡要的說明一下上述代碼的意義,懶加載函數withLazyLoading接受動態import的組件和一個加載動畫做爲參數,動態import的組件加載成功前顯示加載動畫組件,成功後顯示import的組件,經過自定義各類各樣的Spinner加載動畫,咱們能夠實現優雅的js文件加載過程。

觀察打包後文件的結構,合理進行優化

使用webpack --json > stats.json命令能夠生成一個包含依賴關係的json文件。webpack提供了多種可視化工具幫咱們分析這個文件,我最喜歡的工具插件是BundleAnalyzerPlugin,可經過下述方法引入該插件:

new BundleAnalyzerPlugin({

analyzerMode: 'static'})

添加此插件,再次構建完成時,瀏覽器中將自動打開一個相似下面這樣的網頁:

這樣咱們能夠輕易分析咱們的代碼分割是否合理,好比:

分割後文件過大的主要緣由是在於引入了那些模塊。

分析大多後的多文件中存不存在對某些比較大的模塊的重複引用,方便咱們進一步修正本身的配置文件。

上圖是我以前項目中的一張截圖,第一次見到這張圖時仍是給了我不少後期優化的思路的,引用chat.js的同時引入了moment.js,而實際上該頁面只有一張圖表,這讓我考慮另尋圖表解決方案,lodash,velocity在最初的項目中使用過,後逐步去除,屬於遺留代碼,如今還存在說明在局部可能仍是用到了,這都是以後編碼的改進方向。

後記

總以爲技術類的文章也是該有生命力的,花了很久寫完本文,回頭看發現有的內容仍是沒有表達或交待清楚。因此有任何建議,請隨意提出,咱們在Chat中繼續討論,我也將對本文作長期持續的修改。

針對webpack3.5.5官網文檔,使用mindNode製做了一個思惟導圖的草稿,此思惟導圖還需完善,以後將持續修改,在此處(

https://github.com/zhangwang1990/blogs/tree/master/sources/mindMaps)可查看,該思惟導圖示例以下。

另外,關於webpack1和webapck2的區別,官方文檔中有一部分作了詳細的講解(

https://webpack.js.org/guides/migrating/),因此本文中不作贅述,看完之後若是還有疑問,以後咱們再詳細討論。

做者:玄辭 連接:https://www.jianshu.com/p/9349c30a6b3e 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索