webpack入門

寫在前面

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

一、概念

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

1.入口(Entry)

webpack 創(chuàng)建應(yīng)用程序所有依賴的關(guān)系圖(dependency graph)。圖的起點(diǎn)被稱之為入口起點(diǎn)(entry point)。入口起點(diǎn)告訴 webpack 從哪里開(kāi)始,并根據(jù)依賴關(guān)系圖確定需要打包的內(nèi)容??梢詫?yīng)用程序的入口起點(diǎn)認(rèn)為是根上下文(contextual root)app 第一個(gè)啟動(dòng)文件
在 webpack 中,我們使用 webpack 配置對(duì)象(webpack configuration object) 中的entry
屬性來(lái)定義入口
接下來(lái)我們看一個(gè)最簡(jiǎn)單的例子:webpack.config.js
module.exports = { entry: './path/to/my/entry/file.js'};

根據(jù)不同應(yīng)用程序的需要,聲明entry屬性有多種方式。

2.出口(Output)

將所有的資源(assets)歸攏在一起后,還需要告訴 webpack 在哪里打包應(yīng)用程序。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'
 }}

在上面的例子中,我們通過(guò)output.filename和output.path
屬性,來(lái)告訴 webpack bundle 的名稱,以及我們想要生成(emit)到哪里。
你可能看到項(xiàng)目生成(emitted 或 emit)貫穿我們整個(gè)文檔和插件 API。它是“生產(chǎn)(produced)”或“排放(discharged)”的特殊術(shù)語(yǔ)。

3.Loader

webpack 的目標(biāo)是,讓 webpack 聚焦于項(xiàng)目中的所有資源(asset),而瀏覽器不需要關(guān)注考慮這些(明確的說(shuō),這并不意味著所有資源(asset)都必須打包在一起)。webpack 把每個(gè)文件(.css, .html, .scss, .jpg, etc.) 都作為模塊處理。然而 webpack 自身只理解 JavaScript。
webpack loader 在文件被添加到依賴圖中時(shí),其轉(zhuǎn)換為模塊。**
在更高層面,在 webpack 的配置中 loader 有兩個(gè)目標(biāo)。
識(shí)別出(identify)應(yīng)該被對(duì)應(yīng)的 loader 進(jìn)行轉(zhuǎn)換(transform)的那些文件。(test
屬性)
轉(zhuǎn)換這些文件,從而使其能夠被添加到依賴圖中(并且最終添加到 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;

以上配置中,對(duì)一個(gè)單獨(dú)的 module 對(duì)象定義了rules屬性,里面包含兩個(gè)必須屬性:test和use。這告訴 webpack 編譯器(compiler) 如下信息:
“嘿,webpack 編譯器,當(dāng)你碰到「在require()/import語(yǔ)句中被解析為 '.txt' 的路徑」時(shí),在你對(duì)它打包之前,先使用 raw-loader轉(zhuǎn)換一下?!?/p>

重要的是要記得,在 webpack 配置中定義 loader 時(shí),要定義在module.rules中,而不是rules。然而,在定義錯(cuò)誤時(shí) webpack 會(huì)給出嚴(yán)重的警告。為了使你受益于此,如果沒(méi)有按照正確方式去做,webpack 會(huì)“給出嚴(yán)重的警告”

4.插件(Plugins)

然而由于 loader 僅在每個(gè)文件的基礎(chǔ)上執(zhí)行轉(zhuǎn)換,而插件(plugins)
更常用于(但不限于)在打包模塊的 “compilation” 和 “chunk” 生命周期執(zhí)行操作和自定義功能(查看更多)。webpack 的插件系統(tǒng)極其強(qiáng)大和可定制化。
想要使用一個(gè)插件,你只需要require()它,然后把它添加到plugins
數(shù)組中。多數(shù)插件可以通過(guò)選項(xiàng)(option)自定義。你也可以在一個(gè)配置文件中因?yàn)椴煌康亩啻问褂猛粋€(gè)插件,這時(shí)需要通過(guò)使用new
來(lái)創(chuàng)建它的一個(gè)實(shí)例。
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的兩個(gè)最核心的原理分別是:

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

三、幾處說(shuō)明

1.開(kāi)發(fā)模式和生產(chǎn)模式

在package.json文件加入如下的scripts項(xiàng):

"scripts": {
// 運(yùn)行npm run build 來(lái)編譯生成生產(chǎn)模式下的bundles
"build": "webpack --config webpack.config.prod.js",
// 運(yùn)行npm run dev來(lái)生成開(kāi)發(fā)模式下的bundles以及啟動(dòng)本地server
"dev": "webpack-dev-server"
}

2.webpack-dev-server

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

除了提供模塊打包功能,Webpack還提供了一個(gè)基于Node.js Express框架的開(kāi)發(fā)服務(wù)器,它是一個(gè)靜態(tài)資源Web服務(wù)器,對(duì)于簡(jiǎn)單靜態(tài)頁(yè)面或者僅依賴于獨(dú)立服務(wù)的前端頁(yè)面,都可以直接使用這個(gè)開(kāi)發(fā)服務(wù)器進(jìn)行開(kāi)發(fā)。在開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)服務(wù)器會(huì)監(jiān)聽(tīng)每一個(gè)文件的變化,進(jìn)行實(shí)時(shí)打包,并且可以推送通知前端頁(yè)面代碼發(fā)生了變化,從而可以實(shí)現(xiàn)頁(yè)面的自動(dòng)刷新。

  • 安裝:
    npm install --save-dev webpack-dev-server
  • 調(diào)整npm的package.json中scripts 部分開(kāi)發(fā)命令的配置
{
  "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 會(huì)在 localhost:8080 建立一個(gè) Web 服務(wù)器。
幾個(gè)參數(shù)的解釋:
--devtool eval:為你的代碼創(chuàng)建源地址。當(dāng)有任何報(bào)錯(cuò)的時(shí)候可以讓你更加精確地定位到文件和行號(hào)
--progress:顯示合并代碼進(jìn)度
--colors -- hot:命令行中顯示顏色
--content-base 指向設(shè)置的輸出目錄
--手動(dòng)訪問(wèn) http://localhost:8080
簡(jiǎn)單來(lái)說(shuō),當(dāng)你運(yùn)行 npm run dev的時(shí)候,webpack會(huì)幫你會(huì)啟動(dòng)一個(gè) Web 服務(wù)器,然后監(jiān)聽(tīng)文件修改,然后自動(dòng)重新合并你的代碼。真的非常簡(jiǎn)潔。

注意點(diǎn)
用webpack-dev-server生成bundle.js文件是在內(nèi)存中的,并沒(méi)有實(shí)際生成;
如果引用的文件夾中已經(jīng)有bundle.js就不會(huì)自動(dòng)刷新了,你需要先把bundle.js文件手動(dòng)刪除(后期有插件可以完成);
用webstorm的同學(xué)注意了,因?yàn)閣ebstorm是自動(dòng)保存的,所以可能識(shí)別的比較慢,你需要手動(dòng)的ctrl+s一下;

幾個(gè)報(bào)錯(cuò)

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

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

3.“entry”:值分別是字符串、數(shù)組和對(duì)象的情況

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

4. output:“path”項(xiàng)和“publicPath”項(xiàng)output項(xiàng)

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

5..babelrc 文件

babal-loader使用”presets“配置項(xiàng)來(lái)標(biāo)識(shí)如何將ES6語(yǔ)法轉(zhuǎn)成ES5以及如何轉(zhuǎn)換React的JSX成js文件。我們可以用如下的方式使用”query“參數(shù)傳入配置:

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

然而在很多項(xiàng)目里babal的配置可能比較大,因此你可以把babal-loader的配置項(xiàng)單獨(dú)保存在一個(gè)名為”.babelrc“的文件中,在執(zhí)行時(shí)babal-loader將會(huì)自動(dòng)加載.babelrc文件。
所以在很多例子里,你可能會(huì)看到:

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

6.plugin插件

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

//webpack.config.js 
// 獲取所有的.css文件,合并它們的內(nèi)容然后提取css內(nèi)容到一個(gè)獨(dú)立的”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標(biāo)簽內(nèi)聯(lián)到html里,你不必使用extract-text-webpack-plugin,僅僅使用css loader和style loader即可:

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

7.加載器和插件

加載器就是webpack準(zhǔn)備的一些預(yù)處理工具,比如編譯jsx和es6的加載器,處理sass等....

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

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

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

四、webpack的特點(diǎn)

  • 對(duì) CommonJS 、AMD 、ES6的語(yǔ)法做了兼容;
  • 對(duì)js、css、圖片等資源文件都支持打包;
  • 串聯(lián)式 模塊加載器 以及 插件機(jī)制 ,讓其具有更好的靈活性和擴(kuò)展性,例如提供對(duì)CoffeeScript、ES6的支持;
  • 有獨(dú)立的配置文件webpack.config.js;
  • 可以將代碼切割成不同的chunk,實(shí)現(xiàn)按需加載,降低了初始化時(shí)間;
  • 支持 SourceUrls 和 SourceMaps,易于調(diào)試;
  • 具有強(qiáng)大的Plugin接口,大多是內(nèi)部插件,使用起來(lái)比較靈活;
  • webpack 使用異步 IO 并具有多級(jí)緩存。這使得 webpack 很快且在增量編譯上更加快;
    webpack最常用與spa應(yīng)用,主要是vue和React,其實(shí)它就非常像Browserify,但是將應(yīng)用打包為多個(gè)文件。如果單頁(yè)面應(yīng)用有多個(gè)頁(yè)面,那么用戶只從下載對(duì)應(yīng)頁(yè)面的代碼. 當(dāng)他么訪問(wèn)到另一個(gè)頁(yè)面, 他們不需要重新下載通用的代碼。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 眾所周知目前比較火的工具就是gulp和webpack,但webpack和gulp卻有所不同,本人兩者的底層研...
    cduyzh閱讀 1,444評(píng)論 0 13
  • 無(wú)意中看到zhangwnag大佬分享的webpack教程感覺(jué)受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,359評(píng)論 7 35
  • GitChat技術(shù)雜談 前言 本文較長(zhǎng),為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,872評(píng)論 7 110
  • Webpack 第一章 Webpack 簡(jiǎn)介 Instagram團(tuán)隊(duì)在進(jìn)行前端開(kāi)發(fā)的過(guò)程中,發(fā)現(xiàn)當(dāng)項(xiàng)目組成員越來(lái)越...
    whitsats閱讀 681評(píng)論 0 1
  • 1.為什么要使用webpack 現(xiàn)今的很多網(wǎng)頁(yè)其實(shí)可以看做是功能豐富的應(yīng)用,它們擁有著復(fù)雜的JavaScript代...
    YINdevelop閱讀 542評(píng)論 0 5

友情鏈接更多精彩內(nèi)容