webpack入門

寫在前面

第一次接觸webpack,是在一個react項目參與中,剛開始使用的時候,甚至不知道是做什么用的,只看到webpack.config.js文件中很多配置,但是都不太清楚是何用處,本地開發調試模式和build到生產環境都分不清,甚至曾經在服務器上用開發模式運行過一段時間,菜的摳腳啊!最早只知道jQuery操控dom,js、css引入到html,瀏覽器就渲染出頁面了。完全不了解webpack這種構建工具,還可以通過express搭建個native-server,來實時調試代碼修改,當然了這個功能一般都是webpack自動完成的。我們可以在本機端口,訪問到我們的頁面,代碼的修改保存可以實時刷新重新渲染。

一、概念

webpack 是一個現代 JavaScript 應用程序的模塊打包器(module bundler)。所謂的模塊就是在平時的前端開發中,用到一些靜態資源,如JavaScript、CSS、圖片等文件,webpack就將這些靜態資源文件稱之為模塊。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成少量的 bundle - 通常只有一個,由瀏覽器加載。
它是高度可配置的,但是,在開始前你需要先理解四個核心概念:入口(entry)、輸出(output)、loader、插件(plugins)。

1.入口(Entry)

webpack 創建應用程序所有依賴的關系圖(dependency graph)。圖的起點被稱之為入口起點(entry point)入口起點告訴 webpack 從哪里開始,并根據依賴關系圖確定需要打包的內容。可以將應用程序的入口起點認為是根上下文(contextual root)app 第一個啟動文件
在 webpack 中,我們使用 webpack 配置對象(webpack configuration object) 中的entry
屬性來定義入口
接下來我們看一個最簡單的例子:webpack.config.js
module.exports = { entry: './path/to/my/entry/file.js'};

根據不同應用程序的需要,聲明entry屬性有多種方式。

2.出口(Output)

將所有的資源(assets)歸攏在一起后,還需要告訴 webpack 在哪里打包應用程序。webpack 的output屬性描述了如何處理歸攏在一起的代碼(bundled code)。
webpack.config.js

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 的名稱,以及我們想要生成(emit)到哪里。
你可能看到項目生成(emitted 或 emit)貫穿我們整個文檔和插件 API。它是“生產(produced)”或“排放(discharged)”的特殊術語。

3.Loader

webpack 的目標是,讓 webpack 聚焦于項目中的所有資源(asset),而瀏覽器不需要關注考慮這些(明確的說,這并不意味著所有資源(asset)都必須打包在一起)。webpack 把每個文件(.css, .html, .scss, .jpg, etc.) 都作為模塊處理。然而 webpack 自身只理解 JavaScript
webpack loader 在文件被添加到依賴圖中時,其轉換為模塊。**
在更高層面,在 webpack 的配置中 loader 有兩個目標。
識別出(identify)應該被對應的 loader 進行轉換(transform)的那些文件。(test
屬性)
轉換這些文件,從而使其能夠被添加到依賴圖中(并且最終添加到 bundle 中)(use屬性)

webpack.config.js

const path = require('path');
const config = { 
   entry: './path/to/my/entry/file.js', 
   output: { 
       path: path.resolve(__dirname, 'dist'), 
       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 會“給出嚴重的警告”

4.插件(Plugins)

然而由于 loader 僅在每個文件的基礎上執行轉換,而插件(plugins)
更常用于(但不限于)在打包模塊的 “compilation” 和 “chunk” 生命周期執行操作和自定義功能(查看更多)。webpack 的插件系統極其強大和可定制化。
想要使用一個插件,你只需要require()它,然后把它添加到plugins
數組中。多數插件可以通過選項(option)自定義。你也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用new
來創建它的一個實例。
webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');//installed via npmconst 
webpack = require('webpack'); //to access built-in plugins
const path = require('path');
const config = {
   entry: './path/to/my/entry/file.js',
   output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'my-first-webpack.bundle.js' 
}, 
   module: {
      rules: [ {
          test: /\.txt$/,
          use: 'raw-loader' 
} ] },
    plugins: [ 
     new webpack.optimize.UglifyJsPlugin(), 
     new HtmlWebpackPlugin({template: './src/index.html'}) 
]};
module.exports = config;

二、Webpack的核心原理

Webpack的兩個最核心的原理分別是:

  1. 一切皆模塊正如js文件可以是一個“模塊(module)”一樣,其他的(如css、image或html)文件也可視作模 塊。因此,你可以require('myJSfile.js')亦可以require('myCSSfile.css')。這意味著我們可以將事物(業務)分割成更小的易于管理的片段,從而達到重復利用等的目的。
  2. 按需加載傳統的模塊打包工具(module bundlers)最終將所有的模塊編譯生成一個龐大的bundle.js文件。但是在真實的app里邊,“bundle.js”文件可能有10M到15M之大可能會導致應用一直處于加載中狀態。因此Webpack使用許多特性來分割代碼然后生成多個“bundle”文件,而且異步加載部分代碼以實現按需加載。

三、幾處說明

1.開發模式和生產模式

在package.json文件加入如下的scripts項:

"scripts": {
// 運行npm run build 來編譯生成生產模式下的bundles
"build": "webpack --config webpack.config.prod.js",
// 運行npm run dev來生成開發模式下的bundles以及啟動本地server
"dev": "webpack-dev-server"
}

2.webpack-dev-server

我們每修改一次就要需要輸入 npm run dev 是一件非常無聊的事情,幸運的是,我們可以把讓他自己運行,那就是使用webpack-dev-server。

除了提供模塊打包功能,Webpack還提供了一個基于Node.js Express框架的開發服務器,它是一個靜態資源Web服務器,對于簡單靜態頁面或者僅依賴于獨立服務的前端頁面,都可以直接使用這個開發服務器進行開發。在開發過程中,開發服務器會監聽每一個文件的變化,進行實時打包,并且可以推送通知前端頁面代碼發生了變化,從而可以實現頁面的自動刷新。

  • 安裝:
    npm install --save-dev webpack-dev-server
  • 調整npm的package.json中scripts 部分開發命令的配置
{
  "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "pub": "webpack --config webpack.pub.config.js",
      "dev": "webpack-dev-server  --config webpack.dev.config.js --devtool eval --progress --colors --hot --content-base src"
  }
}

在dev的配置中做了以上改變之后,webpack-dev-server 會在 localhost:8080 建立一個 Web 服務器。
幾個參數的解釋:
--devtool eval:為你的代碼創建源地址。當有任何報錯的時候可以讓你更加精確地定位到文件和行號
--progress:顯示合并代碼進度
--colors -- hot:命令行中顯示顏色
--content-base 指向設置的輸出目錄
--手動訪問 http://localhost:8080
簡單來說,當你運行 npm run dev的時候,webpack會幫你會啟動一個 Web 服務器,然后監聽文件修改,然后自動重新合并你的代碼。真的非常簡潔。

注意點
用webpack-dev-server生成bundle.js文件是在內存中的,并沒有實際生成;
如果引用的文件夾中已經有bundle.js就不會自動刷新了,你需要先把bundle.js文件手動刪除(后期有插件可以完成);
用webstorm的同學注意了,因為webstorm是自動保存的,所以可能識別的比較慢,你需要手動的ctrl+s一下;

幾個報錯

  • webpack版本的問題
    如果webpack使用的1.x的版本,那么webpack-dev-server也要使用1.x的版本,否則會報如下錯誤:Connot find module 'webpack/bin/config-yargs'。
  • 端口占用問題
    如果已經有一個工程中使用了webpack-dev-server,并且在運行中,沒有關掉的話,那么8080端口就被占用了,此時如果在另一個工程中使用webpack-dev-server就會報錯:Error: listen EADDRINUSE 127.0.0.1:8080。
    webpack-dev-server(有利于在開發模式下編譯)
    這是一個基于Express.js框架開發的web server,默認監聽8080端口。server內部調用Webpack,這樣做的好處是提供了額外的功能如熱更新“Live Reload”以及熱替換“Hot Module Replacement”(即HMR)。
    webpack-dev-server的“hot” 和 “inline”選項
    “inline”選項會為入口頁面添加“熱加載”功能,“hot”選項則開啟“熱替換(Hot Module Reloading)”,即嘗試重新加載組件改變的部分(而不是重新加載整個頁面)。如果兩個參數都傳入,當資源改變時,webpack-dev-server將會先嘗試HRM(即熱替換),如果失敗則重新加載整個入口頁面。

// 當資源發生改變,以下三種方式都會生成新的bundle,但是又有區別:
// 1. 不會刷新瀏覽器
$ webpack-dev-server
//2. 刷新瀏覽器
$ webpack-dev-server --inline
//3. 重新加載改變的部分,HRM失敗則刷新頁面
$ webpack-dev-server --inline --hot

3.“entry”:值分別是字符串、數組和對象的情況

  • 數組類型
    添加多個彼此不互相依賴的文件,你可以使用數組格式的值。
  • 對象
    現在,假設你的應用是多頁面的(multi-page application)而不是SPA,有多個html文件(index.html和profile.html)。然后你通過一個對象告訴Webpack為每一個html生成一個bundle文件。
    以下的配置將會生成兩個js文件:indexEntry.js和profileEntry.js分別會在index.html和profile.html中被引用。
  • 混合類型
    enter對象里使用數組類型,例如下面的配置將會生成3個文件:vender.js(包含三個文件),index.js和profile.js文件。
57ece0f30001dd1c08000371.png

4. output:“path”項和“publicPath”項output項

告訴webpack怎樣存儲輸出結果以及存儲到哪里。output的兩個配置項“path”和“publicPath”可能會造成困惑。
“path”僅僅告訴Webpack結果存儲在哪里,然而“publicPath”項則被許多Webpack的插件用于在生產模式下更新內嵌到css、html文件里的url值。

5..babelrc 文件

babal-loader使用”presets“配置項來標識如何將ES6語法轉成ES5以及如何轉換React的JSX成js文件。我們可以用如下的方式使用”query“參數傳入配置:

module: {
 loaders: [ { test: /\.jsx?$/,
 exclude: /(node_modulesbower_components)/, 
 loader: 'babel',
 query: { presets: ['react', 'es2015'] } } ]
 }

然而在很多項目里babal的配置可能比較大,因此你可以把babal-loader的配置項單獨保存在一個名為”.babelrc“的文件中,在執行時babal-loader將會自動加載.babelrc文件。
所以在很多例子里,你可能會看到:

//webpack.config.js module:
 {
 loaders: [ { test: /\.jsx?$/, 
exclude: /(node_modulesbower_components)/, 
loader: 'babel' } ] 
} 
//.bablerc 
{ presets: ['react', 'es2015'] }

6.plugin插件

插件一般都是用于輸出bundle的node模塊。
例如,uglifyJSPlugin獲取bundle.js然后壓縮和混淆內容以減小文件體積。
類似的extract-text-webpack-plugin內部使用css-loader和style-loader來收集所有的css到一個地方最終將結果提取結果到一個獨立的”styles.css“文件,并且在html里邊引用style.css文件。

//webpack.config.js 
// 獲取所有的.css文件,合并它們的內容然后提取css內容到一個獨立的”styles.css“里
 var ETP = require("extract-text-webpack-plugin");
 module: {
 loaders: [ {
    test: /\.css$/, 
    loader:ETP.extract("style-loader","css-loader") } 
] }, 
    plugins: [ new ExtractTextPlugin("styles.css") //Extract to styles.css file ]
 }

注意:如果你只是想把css使用style標簽內聯到html里,你不必使用extract-text-webpack-plugin,僅僅使用css loader和style loader即可:

module: {
 loaders: [{
     test: /\.css$/, 
     loader: 'style!css' // (short for style-loader!css-loader)
 }]

7.加載器和插件

加載器就是webpack準備的一些預處理工具,比如編譯jsx和es6的加載器,處理sass等....

使用加載器的步驟也很簡單,首先是安裝依賴,然后在配置文件的module中加一個字段module字段,在module寫上loaders,在loaders中寫上相應的配置。
常用加載器:

  • 編譯jsx和ES6到原生js
    安裝以下的依賴
    npm install --save-dev babel-loader babel-core babel-preset-es2015 babel-preset-react
    修改開發配置文件
module: {
  loaders: [
      {
          test: /\.jsx?$/, // 用正則來匹配文件路徑,這段意思是匹配 js 或者 jsx
          loader: 'babel',// 加載模塊 "babel" 是 "babel-loader" 的縮寫
          query: {
              presets: ['es2015', 'react']
          }
      }
  ]
}
  • 加載CSS
    加載 CSS 需要 css-loader 和 style-loader,他們做兩件不同的事情,css-loader會遍歷 CSS 文件,然后找到 url() 表達式然后處理他們,style-loader 會把原來的 CSS 代碼插入頁面中的一個 style 標簽中。

Loader處理單獨的文件級別并且通常作用于包生成之前或生成的過程中。
而插件則是處理包(bundle)或者chunk級別,且通常是bundle生成的最后階段。一些插件如commonschunkplugin甚至更直接修改bundle的生成方式。

四、webpack的特點

  • 對 CommonJS 、AMD 、ES6的語法做了兼容;
  • 對js、css、圖片等資源文件都支持打包;
  • 串聯式 模塊加載器 以及 插件機制 ,讓其具有更好的靈活性和擴展性,例如提供對CoffeeScript、ES6的支持;
  • 有獨立的配置文件webpack.config.js;
  • 可以將代碼切割成不同的chunk,實現按需加載,降低了初始化時間;
  • 支持 SourceUrls 和 SourceMaps,易于調試;
  • 具有強大的Plugin接口,大多是內部插件,使用起來比較靈活;
  • webpack 使用異步 IO 并具有多級緩存。這使得 webpack 很快且在增量編譯上更加快;
    webpack最常用與spa應用,主要是vue和React,其實它就非常像Browserify,但是將應用打包為多個文件。如果單頁面應用有多個頁面,那么用戶只從下載對應頁面的代碼. 當他么訪問到另一個頁面, 他們不需要重新下載通用的代碼。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容

  • 前言 眾所周知目前比較火的工具就是gulp和webpack,但webpack和gulp卻有所不同,本人兩者的底層研...
    cduyzh閱讀 1,382評論 0 13
  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,206評論 7 35
  • GitChat技術雜談 前言 本文較長,為了節省你的閱讀時間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,704評論 7 110
  • Webpack 第一章 Webpack 簡介 Instagram團隊在進行前端開發的過程中,發現當項目組成員越來越...
    whitsats閱讀 635評論 0 1
  • 1.為什么要使用webpack 現今的很多網頁其實可以看做是功能豐富的應用,它們擁有著復雜的JavaScript代...
    YINdevelop閱讀 512評論 0 5