React開發神器Webpack

編者按:自2013年Facebook發佈以來,React吸引了愈來愈多的開發者,基於它的衍生技術,如React Native、React Canvas等也層出不窮。InfoQ精心策劃「深刻淺出React」系列文章,爲讀者剖析React開發的技術細節。javascript

上一篇咱們對React有了一個整體的認識,在介紹其中的技術細節以前,咱們首先來了解一下用於React開發和模塊管理的主流工具Webpack。稱之爲React開發神器有點標題黨了,不過Webpack確實是筆者見過的功能最爲強大的前端模塊管理和打包工具。雖然Webpack是一個通用的工具,並不僅適合於React,可是不少React的文章或者項目都使用了Webpack,尤爲是react-hot-loader這樣的神器存在,讓Webpack成爲最主流的React開發工具。css

CommonJS和AMD是用於JavaScript模塊管理的兩大規範,前者定義的是模塊的同步加載,主要用於NodeJS;然後者則是異步加載,經過requirejs等工具適用於前端。隨着npm成爲主流的JavaScript組件發佈平臺,愈來愈多的前端項目也依賴於npm上的項目,或者自身就會發布到npm平臺。所以,讓前端項目更方便的使用npm上的資源成爲一大需求。因而誕生了相似browserify這樣的工具,代碼中可使用require函數直接以同步語法形式引入npm模塊,打包後再由瀏覽器執行。html

Webpack其實有點相似browserify,出自Facebook的Instagram團隊,但功能比browserify更爲強大。其主要特性以下:前端

  1. 同時支持CommonJSAMD模塊(對於新項目,推薦直接使用CommonJS);
  2. 串聯式模塊加載器以及插件機制,讓其具備更好的靈活性和擴展性,例如提供對CoffeeScript、ES6的支持;
  3. 能夠基於配置或者智能分析打包成多個文件,實現公共模塊或者按需加載;
  4. 支持對CSS,圖片等資源進行打包,從而無需藉助Grunt或Gulp;
  5. 開發時在內存中完成打包,性能更快,徹底能夠支持開發過程的實時打包需求;
  6. 對sourcemap有很好的支持,易於調試。

Webpack將項目中用到的一切靜態資源都視之爲模塊,模塊之間能夠互相依賴。Webpack對它們進行統一的管理以及打包發佈,其官方主頁用下面這張圖來講明Webpack的做用:java

能夠看到Webpack的目標就是對項目中的靜態資源進行統一管理,爲產品的最終發佈提供最優的打包部署方案。本文就將圍繞React對其相關用法作一個整體介紹,從而能讓你將其應用在本身的實際項目之中。node

安裝Webpack,並加載一個簡單的React組件

Webpack通常做爲全局的npm模塊安裝:react

npm install -g webpack

以後便有了全局的webpack命令,直接執行此命令會默認使用當前目錄的webpack.config.js做爲配置文件。若是要指定另外的配置文件,能夠執行:jquery

webpack —config webpack.custom.config.js

儘管Webpack能夠經過命令行來指定參數,但咱們一般會將全部相關參數定義在配置文件中。通常咱們會定義兩個配置文件,一個用於開發時,另一個用於產品發佈。生產環境下的打包文件不須要包含sourcemap等用於開發時的代碼。配置文件一般放在項目根目錄之下,其自己也是一個標準的CommonJS模塊。webpack

一個最簡單的Webpack配置文件webpack.config.js以下所示:git

module.exports = { entry:[ './app/main.js' ], output: { path: __dirname + '/assets/', publicPath: "/assets/", filename: 'bundle.js' } };

其中entry參數定義了打包後的入口文件,數組中的全部文件會按順序打包。每一個文件進行依賴的遞歸查找,直到全部相關模塊都被打包。output參數定義了輸出文件的位置,其中經常使用的參數包括:

  • path: 打包文件存放的絕對路徑
  • publicPath: 網站運行時的訪問路徑
  • filename: 打包後的文件名

如今來看如何打包一個React組件。假設有以下項目文件夾結構:

- react-sample
  + assets/
   - js/
     Hello.js
     entry.js
   index.html
   webpack.config.js

其中Hello.js定義了一個簡單的React組件,使用ES6語法:

var React = require('react'); class Hello extends React.Component { render() { return ( <h1>Hello {this.props.name}!</h1> ); } }

entry.js是入口文件,將一個Hello組件輸出到界面:

var React = require('react'); var Hello = require('./Hello'); React.render(<Hello name="Nate" />, document.body);

index.html的內容以下:

<html> <head></head> <body> <script src="/assets/bundle.js"></script> </body> </html>

在這裏Hello.js和entry.js都是JSX組件語法,須要對它們進行預處理,這就要引入webpack的JSX加載器。所以在配置文件中加入以下配置:

module: { loaders: [ { test: /\.jsx?$/, loaders: ['jsx?harmony']} ] }

加載器的概念稍後還會詳細介紹,這裏只須要知道它能將JSX編譯成JavaScript並加載爲Webpack模塊。這樣在當前目錄執行webpack命令以後,在assets目錄將生成bundle.js,打包了entry.js的內容。當瀏覽器打開當前服務器上的index.html,將顯示「Hello Nate!」。這是一個很是簡單的例子,演示瞭如何使用Webpack來進行最簡單的React組件打包。

加載AMD或CommonJS模塊

在實際項目中,代碼以模塊進行組織,AMD是在CommonJS的基礎上考慮了瀏覽器的異步加載特性而產生的,可讓模塊異步加載並保證執行順序。而CommonJS的require函數則是同步加載。在Webpack中筆者更加推薦CommonJS方式去加載模塊,這種方式語法更加簡潔直觀。即便在開發時,咱們也是加載Webpack打包後的文件,經過sourcemap去進行調試。

除了項目自己的模塊,咱們也須要依賴第三方的模塊,如今比較經常使用的第三方模塊基本都經過npm進行發佈,使用它們已經無需單獨下載管理,須要時執行npm install便可。例如,咱們須要依賴jQuery,只需執行:

npm install jquery —save-dev

更多狀況下咱們是在項目的package.json中進行依賴管理,而後經過直接執行npm install來安裝全部依賴。這樣在項目的代碼倉庫中並不須要存儲實際的第三方依賴庫的代碼。

安裝以後,在須要使用jquery的模塊中須要在頭部進行引入:

var $ = require('jquery'); $('body').html('Hello Webpack!');

能夠看到,這種以CommonJS的同步形式去引入其它模塊的方式代碼更加簡潔。瀏覽器並不會實際的去同步加載這個模塊,require的處理是由Webpack進行解析和打包的,瀏覽器只須要執行打包後的代碼。Webpack自身已經能夠徹底處理JavaScript模塊的加載,可是對於React中的JSX語法,這就須要使用Webpack的擴展加載器來處理了。

Webpack開發服務器

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

Webpack開發服務器須要單獨安裝,一樣是經過npm進行:

npm install -g webpack-dev-server

以後即可以運行webpack-dev-server命令來啓動開發服務器,而後經過localhost:8080/webpack-dev-server/訪問到頁面了。默認狀況下服務器以當前目錄做爲服務器目錄。在React開發中,咱們一般會結合react-hot-loader來使用開發服務器,所以這裏不作太多介紹,只須要知道有這樣一個開發服務器能夠用於開發時的內容實時打包和推送。詳細配置和用法能夠參考官方文檔

Webpack模塊加載器(Loaders)

Webpack將全部靜態資源都認爲是模塊,好比JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖片等等,從而能夠對其進行統一管理。爲此Webpack引入了加載器的概念,除了純JavaScript以外,每一種資源均可以經過對應的加載器處理成模塊。和大多數包管理器不同的是,Webpack的加載器之間能夠進行串聯,一個加載器的輸出能夠成爲另外一個加載器的輸入。好比LESS文件先經過less-load處理成css,而後再經過css-loader加載成css模塊,最後由style-loader加載器對其作最後的處理,從而運行時能夠經過style標籤將其應用到最終的瀏覽器環境。

對於React的JSX也是如此,它經過jsx-loader來載入。jsx-loader專門用於載入React的JSX文件,Webpack的加載器支持參數,jsx-loader就能夠添加?harmony參數使其支持ES6語法。爲了讓Webpack識別什麼樣的資源應該用什麼加載器去載入,須要在配置文件進行配置:經過正則表達式對文件名進行匹配。例如:

module: { preLoaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'jsxhint' }], loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'react-hot!jsx-loader?harmony' }, { test: /\.less/, loader: 'style-loader!css-loader!less-loader' }, { test: /\.(css)$/, loader: 'style-loader!css-loader' }, { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }] }

能夠看到,該使用什麼加載器徹底取決於這裏的配置,即便對於JSX文件,咱們也能夠用js做爲後綴,從而全部的JavaScript均可以經過jsx-loader載入,由於jsx自己就是徹底兼容JavaScript的,因此即便沒有JSX語法,普通JavaScript模塊也可使用jsx-loader來載入。

加載器之間的級聯是經過感嘆號來鏈接,例如對於LESS資源,寫法爲style-loader!css-loader!less-loader。對於小型的圖片資源,也能夠將其進行統一打包,由url-loader實現,代碼中url-loader?limit=8192含義就是對於全部小於8192字節的圖片資源也進行打包。這在必定程度上能夠替代Css Sprites方案,用於減小對於小圖片資源的HTTP請求數量。

除了已有加載器,你也能夠本身實現本身的加載器,從而可讓Webpack統一管理項目特定的靜態資源。如今也已經有不少第三方的加載器實現常見靜態資源的打包管理,能夠參考Webpack主頁上的加載器列表

React開發神器:react-hot-loader

Webpack自己具備運行時模塊替換功能,稱之爲Hot Module Replacement (HMR)。當某個模塊代碼發生變化時,Webpack實時打包將其推送到頁面並進行替換,從而無需刷新頁面就實現代碼替換。這個過程相對比較複雜,須要進行多方面考慮和配置。而如今針對React出現了一個第三方react-hot-loader加載器,使用這個加載器就能夠輕鬆實現React組件的熱替換,很是方便。其實正是由於React的每一次更新都是全局刷新的虛擬DOM機制,讓React組件的熱替換能夠成爲通用的加載器,從而極大提升開發效率。

要使用react-hot-loader,首先經過npm進行安裝:

npm install —save-dev react-hot-loader

以後,Webpack開發服務器須要開啓HMR參數hot,爲了方便,咱們建立一個名爲server.js的文件用以啓動Webpack開發服務器:

var webpack = require('webpack'); var WebpackDevServer = require('webpack-dev-server'); var config = require('../webpack.config'); new WebpackDevServer(webpack(config), { publicPath: config.output.publicPath, hot: true, noInfo: false, historyApiFallback: true }).listen(3000, '127.0.0.1', function (err, result) { if (err) { console.log(err); } console.log('Listening at localhost:3000'); });

爲了熱加載React組件,咱們須要在前端頁面中加入相應的代碼,用以接收Webpack推送過來的代碼模塊,進而能夠通知全部相關React組件進行從新Render。加入這個代碼很簡單:

entry: [ 'webpack-dev-server/client?http://127.0.0.1:3000', // WebpackDevServer host and port 'webpack/hot/only-dev-server', './scripts/entry' // Your appʼs entry point ]

須要注意的是,這裏的client?http://127.0.0.1:3000須要和在server.js中啓動Webpack開發服務器的地址匹配。這樣,打包生成的文件就知道該從哪裏去獲取動態的代碼更新。下一步,咱們須要讓Webpack用react-hot-loader去加載React組件,如上一節所介紹,這經過加載器配置完成:

loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'react-hot!jsx-loader?harmony' }, … ]

作完這些配置以後,使用Node.js運行server.js:

node server.js

便可啓動開發服務器並實現React組件的熱加載。爲了方便,咱們也能夠在package.json中加入一節配置:

"scripts": { "start": "node ./js/server.js" }

從而經過npm start命令便可啓動開發服務器。示例代碼也上傳在Github上,你們能夠參考。

這樣,React的熱加載開發環境即配置完成,任何修改只要以保存,就會在頁面上馬上體現出來。不管是對樣式修改,仍是對界面渲染的修改,甚至事件綁定處理函數的修改,均可以馬上生效,不得不說是提升開發效率的神器。

將Webpack開發服務器集成到已有服務器

儘管Webpack開發服務器能夠直接用於開發,但實際項目中咱們可能必須使用本身的Web服務器。這就須要咱們能將Webpack的服務集成到已有服務器,來使用Webpack提供的模塊打包和加載功能。要實現這一點其實很是容易,只須要在載入打包文件時指定完整的URL地址,例如:

<script src="http://127.0.0.1:3000/assets/bundle.js"></script>

這就告訴當前頁面應該去另一個服務器得到腳本資源文件,在以前咱們已經在配置文件中指定了開發服務器的地址,所以打包後的文件也知道應該經過哪一個地址去創建Socket IO來動態加載模塊。整個資源架構以下圖所示:

打包成多個資源文件

將項目中的模塊打包成多個資源文件有兩個目的:

  1. 將多個頁面的公用模塊獨立打包,從而能夠利用瀏覽器緩存機制來提升頁面加載效率;
  2. 減小頁面初次加載時間,只有當某功能被用到時,纔去動態的加載。

Webpack提供了很是強大的功能讓你可以靈活的對打包方案進行配置。首先來看如何建立多個入口文件:

{ entry: { a: "./a", b: "./b" }, output: { filename: "[name].js" }, plugins: [ new webpack.CommonsChunkPlugin("init.js") ] }

能夠看到,配置文件中定義了兩個打包資源「a」和「b」,在輸出文件中使用方括號來得到輸出文件名。而在插件設置中使用了CommonsChunkPlugin,Webpack中將打包後的文件都稱之爲「Chunk」。這個插件能夠將多個打包後的資源中的公共部分打包成單獨的文件,這裏指定公共文件輸出爲「init.js」。這樣咱們就得到了三個打包後的文件,在html頁面中能夠這樣引用:

<script src="init.js"></script> <script src="a.js"></script> <script src="b.js"></script>

除了在配置文件中對打包文件進行配置,還能夠在代碼中進行定義:require.ensure,例如:

require.ensure(["module-a", "module-b"], function(require) { var a = require("module-a"); // ... });

Webpack在編譯時會掃描到這樣的代碼,並對依賴模塊進行自動打包,運行過程當中執行到這段代碼時會自動找到打包後的文件進行按需加載。

小結

本文結合React介紹了Webpack的基本功能和用法,但願能讓你們對這個新興而強大的模塊管理工具備一個整體的認識,並能將其應用在實際的項目開發中。筆者也將其應用在以前提供的React示例組件項目中,你們能夠參考。除了這裏介紹的功能,Webpack還有許多強大的特性,例如插件機制、支持動態表達式的require、打包文件的智能重組、性能優化、代碼混淆等等。限於篇幅再也不一一介紹,其官方文檔也很是完善,須要時能夠參考。

相關文章
相關標籤/搜索