當前位置:
首頁 > 最新 > 為什麼我們要做三份 Webpack 配置文件

為什麼我們要做三份 Webpack 配置文件

作者:Henry Li

在知乎上我們常常會看到有同學發問:BAT 等大型網站的前端工程是如何組織管理的?這的確是一個可以發散的很廣的 Q&A,我想如果要我回答這個問題,不如先從 Webpack 配置說起。

時至今日,Webpack 已經成為前端工程必備的基礎工具之一,不僅被廣泛用於前端工程發布前的打包,還在開發中擔當本地前端資源伺服器(assets server)、模塊熱更新(hot module replacement)、API Proxy 等角色,結合 ESLint 等代碼檢查工具,還可以實現在對源代碼的嚴格校驗檢查。

正如上文中提到的,前端從開發到部署前都離不開 Webpack 的參與,而 Webpack 的默認配置文件只有一個,即 webpack.config.js,那麼問題來了,開發期和部署前應該使用同一份 Webpack 配置嗎?答案肯定是否定的,既然 webpack.config.js 是一個 JS 文件,我們當然可以在文件里寫 JavaScript 業務邏輯,通過讀取環境變數 NODE_ENV 來判斷當前是在開發(dev)時還是最終的生產環境(production),然而很多同學習慣把這兩者的配置都混寫在根目錄下的 webpack.config.js,通過很多零散的 if…else 來「臨時」決定某一個 plugin 或者某一個 loader 的配置項,隨著 loaders 和 plugins 的不斷增加,久而久之 webpack.config.js 變得原來越隆長,代碼的可讀性和可維護性也大大下降。

我想通過本文來介紹一種用 3 個 JS 文件來配置 Webpack 的方法,這裡借鑒了很多開源項目的配置,同時也結合了我們自己在開發中碰到的種種問題解決方案。

本文中提及的配置基於 Webpack 2 或以上,建議使用 3.0 及以上版本

開發環境與生產環境的區別

開發環境

NODE_ENV 為 development

啟用模塊熱更新(hot module replacement)

額外的 webpack-dev-server 配置項,API Proxy 配置項

輸出 Sourcemap

生產環境

NODE_ENV 為 production

將 React、jQuery 等常用庫設置為 external,直接採用 CDN 線上的版本

樣式源文件(如 css、less、scss 等)需要通過 ExtractTextPlugin 獨立抽取成 css 文件

啟用 post-css

啟用 optimize-minimize(如 uglify 等)

中大型的商業網站生產環境下,是絕對不能有 console.log() 的,所以要為 babel 配置 Remove console transform

這裡需要說明的是因為開發環境下啟用了 hot module replacement,為了讓樣式源文件的修改也同樣能被熱替換,不能使用 ExtractTextPlugin,而轉為隨 JS Bundle 一起輸出。

你需要三份配置文件

在 base 文件里,你需要將開發環境和生產環境中通用的配置集中放在這裡:

constCleanWebpackPlugin=require( clean-webpack-plugin );

constpath=require( path );

constwebpack=require( webpack );

// 配置常量

// 源代碼的根目錄(本地物理文件路徑)

constSRC_PATH=path.resolve( ./src );

// 打包後的資源根目錄(本地物理文件路徑)

constASSETS_BUILD_PATH=path.resolve( ./build );

// 資源根目錄(可以是 CDN 上的絕對路徑,或相對路徑)

constASSETS_PUBLIC_PATH= /assets/ ;

module.exports={

context:SRC_PATH,// 設置源代碼的默認根路徑

resolve:{

extensions:[ .js , .jsx ]// 同時支持 js 和 jsx

},

entry:{

// 注意 entry 中的路徑都是相對於 SRC_PATH 的路徑

vendor: ./vendor ,

a:[ ./entry-a ],

b:[ ./entry-b ],

c:[ ./entry-c ]

},

output:{

path:ASSETS_BUILD_PATH,

publicPath:ASSETS_PUBLIC_PATH,

filename: ./[name].js

},

module:{

rules:[

{

enforce: pre ,// ESLint 優先順序高於其他 JS 相關的 loader

test: /.jsx?$/,

exclude: /node_modules/,

loader: eslint-loader

},

{

test: /.jsx?$/,

exclude: /node_modules/,

// 建議把 babel 的運行時配置放在 .babelrc 里,從而與 eslint-loader 等共享配置

loader: babel-loader

},

{

test: /.(pngjpggif)$/,

use:

[

{

loader: url-loader ,

options:

{

limit:8192,

name: images/[name].[ext]

}

}

]

},

{

test: /.woff(2)?(?v=[-9].[-9].[-9])?$/,

use:

[

{

loader: url-loader ,

options:

{

limit:8192,

mimetype: application/font-woff ,

name: fonts/[name].[ext]

}

}

]

},

{

test: /.(ttfeotsvg)(?v=[-9].[-9].[-9])?$/,

use:

[

{

loader: file-loader ,

options:

{

limit:8192,

mimetype: application/font-woff ,

name: fonts/[name].[ext]

}

}

]

}

]

},

plugins:[

// 每次打包前,先清空原來目錄中的內容

newCleanWebpackPlugin([ASSETS_BUILD_PATH],{verbose:false}),

// 啟用 CommonChunkPlugin

newwebpack.optimize.CommonsChunkPlugin({

names: vendor ,

minChunks:Infinity

})

]

};

這是用於開發環境的 Webpack 配置,繼承自 base:

constwebpack=require( webpack );

// 讀取同一目錄下的 base config

// 添加 webpack-dev-server 相關的配置項

config.devServer={

contentBase: ./ ,

hot:true,

publicPath: /assets/

};

// 有關 Webpack 的 API 本地代理,另請參考 https://webpack.github.io/docs/webpack-dev-server.html#proxy

{

test: /.less$/,

use:[

style-loader ,

css-loader ,

less-loader

],

exclude: /node_modules/

}

);

// 真實場景中,React、jQuery 等優先走全站的 CDN,所以要放在 externals 中

config.externals={

react: React ,

react-dom : ReactDOM

};

// 添加 Sourcemap 支持

newwebpack.SourceMapDevToolPlugin({

filename: [file].map ,

exclude:[ vendor.js ]// vendor 通常不需要 sourcemap

})

);

// Hot module replacement

Object.keys(config.entry).forEach((key)=>{

// 這裡有一個私有的約定,如果 entry 是一個數組,則證明它需要被 hot module replace

if(Array.isArray(config.entry[key])){

config.entry[key].unshift(

webpack-dev-server/client?http://0.0.0.0:8080 ,

webpack/hot/only-dev-server

);

}

});

newwebpack.HotModuleReplacementPlugin()

);

module.exports=config;

這是用於生產環境的 webpack 配置,同樣繼承自 base:

constwebpack=require( webpack );

constExtractTextPlugin=require( extract-text-webpack-plugin );

// 讀取同一目錄下的 base config

{

test: /.less$/,

use:ExtractTextPlugin.extract(

{

use:[

css-loader ,

less-loader

],

fallback: style-loader

}

),

exclude: /node_modules/

}

);

// 官方文檔推薦使用下面的插件確保 NODE_ENV

newwebpack.DefinePlugin({

process.env.NODE_ENV :JSON.stringify(process.env.NODE_ENV production )

}),

// 啟動 minify

newwebpack.LoaderOptionsPlugin({minimize:true}),

// 抽取 CSS 文件

newExtractTextPlugin({

filename: [name].css ,

allChunks:true,

ignoreOrder:true

})

);

module.exports=config;

現在在你的工程文件夾里應該已經有三個 Webpack 配置文件,它們分別是:

最後,你還需要在 package.json 里添加相應的配置:

{

...

"scripts":{

"build":"webpack --optimize-minimize",

"start":"npm run dev"// 或添加你自己的 start 邏輯

},

...

}

和很多項目一樣,在開發環境下的時候,你需要使用 npm run dev 來啟動,而在生產環境中,則用 npm run build 來發布。

題外話,在真實場景中,我們不會直接使用 webpack-dev-server,而採用 express + webpack/webpack-dev-middleware,配置方法與上面所述的完全相同。

覺得本文對你有幫助?請分享給更多人

關注「前端大全」,提升前端技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端大全 的精彩文章:

Web 的現狀:網頁性能提升指南
你知道URL、URI和URN三者之間的區別嗎?
V8 內存分配與垃圾回收
2000塊都沒人的蘋果SE
原來炸雞隻是副業!肯德基竟然推出了這麼多好玩的黑科技產品

TAG:前端大全 |