本身配置過webpack的人應該都知道,webpack真的好複雜,一開始作項目都是拿別人現成的作作小修改,可是別人的終究沒有本身配的舒服。因此我打算寫這篇文章,從個人配置中帶你們瞭解webpack配置,簡化讀webpack官方頁面的複雜度。javascript
首先咱們須要明確咱們要求webpack作什麼?css
1.所寫即所得,咱們在編譯器中寫了代碼能立刻呈如今調試器上(熱更新服務)html
2.本地開發獲取數據存在的跨域問題(代理,解決跨域)vue
3.使用es678,less,sass等(翻譯,讓瀏覽器懂得咱們的代碼)java
4.提升項目性能,好比壓縮代碼,壓縮圖片等(項目優化)node
5.若是與輸入相關的需求,找entry(好比多頁面就有多個入口) 6.若是與輸出相關的需求,找output(好比你須要定義輸出文件的路徑、名字等等) 7.若是與模塊尋址相關的需求,找resolve(好比定義別名alias) 8.若是與轉譯相關的需求,找loader(好比處理sass處理es678N) 9.若是與構建流程相關的需求,找plugin(好比我須要在打包完成後,將打包好的文件複製到某個目錄,而後提交到git上) 接下來咱們來看下webpack的一些基本配置react
1.entry(項目入口)webpack
2.output(出口文件)git
3.modules(模塊處理)es6
4.plugin(插件)
5.resolve
6.devserver
7.mode,
8devtool
主要有三種方式
1.字符串形式
entry: '.src/main.js'
複製代碼
2.數組形式
entry: [react, react-dom]
複製代碼
3.對象形式
entry: {
main:'./src/index2.js',
second: './src/index2.js',
vendor:['react', 'react-dom']
}
複製代碼
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
複製代碼
在上面的示例中,咱們經過 output.filename 和 output.path 屬性,來告訴 webpack bundle 的名稱,以及咱們想要 bundle 生成(emit)到哪裏。 Path.resolve是什麼,引入的path模塊是幹什麼用的
Nodejs
該path.resolve()方法將一系列路徑或路徑段解析爲絕對路徑。
給定的路徑序列從右到左處理,隨後每一個path路徑都被預先加載,直到構造出絕對路徑。例如,給定路徑段的序列:/foo,/bar,baz,調用path.resolve('/foo', '/bar', 'baz')將返回/bar/baz。
若是在處理path完全部給定段以後還沒有生成絕對路徑,則使用當前工做目錄。 生成的路徑已規範化,而且除非將路徑解析爲根目錄,不然將刪除尾部斜槓。 零長度path段被忽略。
若是沒有path傳遞段,path.resolve()將返回當前工做目錄的絕對路徑。
path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// Returns: '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// If the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'
_dirname
複製代碼
當前模塊的目錄名稱。這是同樣 path.dirname()的__filename。
示例:node example.js從中運行/Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
複製代碼
咱們在配置output是經常使用的幾個
filename 這個是輸出文件的名稱,字符串類型,若是隻有一個輸出文件,能夠寫成靜態名稱。例如
output:{
filename:'bundle.js'
}
複製代碼
固然了,在咱們平常工做中,通常狀況下是不會有這種狀況的,當項目很大的時候,若是不分塊打包,bundle.js會驚人的大,項目越大,bundle.js就會越大,這不是咱們今天討論的重點,之後再說
多個chunk的時候怎麼辦呢
webpack會爲每一個生成的Chunk取一個名稱,Chunk的名稱和Entry的配置有關:
然而,當經過多個入口起點(entry point)、代碼拆分(code splitting)或各類插件(plugin)建立多個
bundle,應該使用如下一種替換方式,來賦予每一個 bundle 一個惟一的名稱…… 使用入口名稱:
output:{
filename: "[name].bundle.js"
}
複製代碼
使用內部 chunk id
output:{
filename: "[id].bundle.js"
}
複製代碼
使用每次構建過程當中,惟一的 hash 生成
output:{
filename: "[name].[hash].bundle.js"
}
複製代碼
使用基於每一個 chunk 內容的 hash:
output:{
filename: "[chunkhash].bundle.js"
}
複製代碼
這裏多出來幾個陌生詞彙hash、chunkhash,它們是什麼? hash、chunkhash和contenthash三者的區別 hash
hash的值是相同的,若是都使用hash的話,由於這是工程級別的,即每次修改任何一個文件,全部文件名的hash至都將改變。因此一旦修改了任何一個文件,整個項目的文件緩存都將失效。因此對於沒有改變的模塊而言,這樣作顯然不恰當,由於緩存失效了嘛。此時,chunkhash的用途隨之而來。 chunkhash
只有被修改了的文件的文件名,hash值修改
filename: '[name]-[chunkhash].js'
當咱們使用mini-css-extract-plugin拆分css的時候,就須要使用chunkhash,我一個js文件裏面引入了css文件。這時要是我修改了js,但沒修改css,能夠經過chunkhash緩存css文件
contenthash
對css使用了chunkhash以後,咱們測試會發現,若是修改了js,css文件名的hash值確實沒變,但這時要是咱們修改css文件的話,咱們就會發現css文件名的chunkhash值竟然沒變化,這樣就致使咱們的非覆蓋發佈css文件失效了。因此這裏須要注意就是css文件必須使用contenthash。
上面介紹的 id、name、hash、chunkhash等都是webpack內置變量, id是惟一標示,不會重複,從0開始, name 是模塊名稱,是你本身起的,在配置路由懶加載的時候能夠本身命名 官網介紹的很清楚,我就再也不這裏囉嗦了, chunkFilename
官網解釋:此選項決定了非入口(non-entry) chunk 文件的名稱, 什麼場景須要呢?
在按需加載(異步)模塊的時候,也就是路由懶加載,這樣的文件是沒有被列在entry中的,
好比
{
entry: {
"index": "pages/index.jsx"
},
output: {
filename: "[name].min.js",
chunkFilename: "[name].min.js"
}
}
const myModel = r => require.ensure([], () => r(require('./myVue.vue')), 'myModel')
複製代碼
上面的例子,經過filename輸出的是index.min.js 異步加載的模塊是要以文件形式加載哦,因此這時生成的文件名是以chunkname配置的,經過chunkFilename輸出的是myModel.min.js 因此chunkFilename也很重要哦!!! path path是配置輸出文件存放在本地的目錄,字符串類型,是絕對路徑
output:{
path: path.resolve(__dirname, 'dist/assets')
}
複製代碼
__dirname,這個昨天說過,能夠回顧一下,就是當前文件所在的文件夾的名字 publicPath 對構建出的資源進行異步加載(圖片,文件),該選項的值是以 runtime(運行時) 或 loader(載入時) 所建立的每一個 URL 爲前綴。所以,在多數狀況下,此選項的值都會以/結束。 默認值是一個空字符串 "",即相對路徑,配置錯誤會致使404 簡單說,就是靜態文件託管在cdn上 舉個栗子: 若是你這麼配置:
output:{
filename:'[name]_[chunkhash:8].js',
publicPath:'https://www.qdtalk.com/assets/'
}
複製代碼
打包編譯後,html頁面就是這樣的
path 和publicPath都支持字符串模板
const path = require('path');
module.exports = {
entry: main: './src/main.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
// webpack 配置
const path = require('path');
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
模塊處理主要是對loader的配置
此處引用官網對loader的定義
loader 讓 webpack 可以去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 能夠將全部類型的文件轉換爲webpack可以處理的有效模塊,而後你就能夠利用webpack的打包能力,對它們進行處理。
本質上,webpack loader 將全部類型的文件,轉換爲應用程序的依賴圖(和最終的 bundle)能夠直接引用的模塊。 注意,loader 可以 import 導入任何類型的模塊(例如 .css 文件),這是 webpack 特有的功能,其餘打包程序或任務執行器的可能並不支持。咱們認爲這種語言擴展是有很必要的,由於這可使開發人員建立出更準確的依賴關係圖。 在更高層面,在 webpack 的配置中 loader 有兩個目標:
webpack.config.js
const path = require('path');
const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};
module.exports = config;
複製代碼
以上配置中,對一個單獨的 module 對象定義了 rules 屬性,裏面包含兩個必須屬性:test 和 use。這告訴 webpack 編譯器(compiler) 以下信息:
「嘿,webpack 編譯器,當你碰到「在 require()/import 語句中被解析爲 '.txt' 的路徑」時,在你對它打包以前,先使用 raw-loader 轉換一下。」 重要的是要記得,在 webpack 配置中定義 loader 時,要定義在 module.rules 中,而不是 rules。然而,在定義錯誤時 webpack 會給出嚴重的警告。爲了使你受益於此,若是沒有按照正確方式去作,webpack 會「給出嚴重的警告」
在webpack中有許許多多的loader,此處我按照官方文檔解釋下和我理解的用法 loader 特性
import txt from './file.txt';
複製代碼
val-loader 加載的模塊必須使用如下函數接口,將 default export 導出爲一個函數。
function answer () {
return {
code: 'module.exports = 42;'
}
};
module.exports = answer;
複製代碼
url-loader
url-loader 功能相似於 file-loader,可是在文件大小(單位 byte)低於指定的限制時,能夠返回一個 DataURL。 file-loader 將文件發送到輸出文件夾,並返回(相對)URL(不會再對文件作處理) JSON
轉換編譯(Transpiling)
React開發過程當中咱們須要將jsx或者es6代碼轉譯成es5代碼。 咱們須要用到babel-loader
{
'test': /\.(js|jsx)$/, // babel 轉換爲兼容性的 js
'exclude': /node_modules/,
'loader': 'babel-loader',
'query': {
'presets': ['react', 'latest', 'stage-0', 'react-hmre']
},
'include': path.resolve(__dirname, '../client')
},
複製代碼
注:exclude是不轉換node_modules的代碼,query === options 此處注意babel版本6和7設置上有區別。 若是引用錯誤會報錯 模板(Templating)
樣式
{
'test': /\.less$/,
'loader': ['style-loader', 'css-loader', {
'loader': 'less-loader',
'options': {
'javascriptEnabled': true
}
}]
},
複製代碼
此處的執行順序。less-loader -> css-loader -> style-loader 清理和測試(Linting && Testing)
框架(Frameworks)
核心重點(敲黑板)
做用:能夠處理各類任務,從打包優化和壓縮,一直到從新定義環境中的變量 loader不須要require. plugin須要
loader 被用於轉換某些類型的模塊,而插件則能夠用於執行範圍更廣的任務。插件的範圍包括,從打包優化和壓縮,一直到從新定義環境中的變量。插件接口功能極其強大,能夠用來處理各類各樣的任務。
想要使用一個插件,你只須要 require() 它,而後把它添加到 plugins 數組中。多數插件能夠經過選項(option)自定義。你也能夠在一個配置文件中由於不一樣目的而屢次使用同一個插件,這時須要經過使用 new 操做符來建立它的一個實例。 下面是我配置的用於開發環境的plugins實例
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 經過 npm 安裝
const webpack = require('webpack'); // 用於訪問內置插件
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
複製代碼
'plugins': [
new webpack.optimize.OccurrenceOrderPlugin(), // 調整模塊的打包順序,用到次數更多的會出如今文件的前面
new webpack.DefinePlugin({ // DefinePlugin 容許建立一個在編譯時能夠配置的全局常量。
'process.env.NODE_ENV': JSON.stringify('development')
}),
new HtmlWebpackPlugin({ // HtmlWebpackPlugin簡化了HTML文件的建立,以便爲你的webpack包提供服務。這對於在文件名中包含每次會隨着編譯而發生變化哈希的 webpack bundle 尤爲有用。 你可讓插件爲你生成一個HTML文件,使用lodash模板提供你本身的模板,或使用你本身的loader。
'hash': true,
'title': 'Demo',
'filename': 'index.html',
'template': path.resolve(__dirname, '../views/index.ejs'),
'inject': 'body'
}),
new webpack.HotModuleReplacementPlugin(), // 啓用熱更新
new webpack.NoEmitOnErrorsPlugin(), // 輸出階段遇到編譯錯誤跳過
new webpack.NamedModulesPlugin(), // 當開啓 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境。
new webpack.ProgressPlugin(), // 輸出構建進度
]
複製代碼