您當前的位置:首頁(yè) > 新(xīn)聞資訊 > 産(chǎn)品動态 > 正文(wén)

【新(xīn)東網技(jì )術大咖帶您走進Webpack】談談React開發神器webpack是什麽鬼!

發布時間: 2016-09-23 15:13:35  
分(fēn)享到:

 

文(wén)/謝(xiè)海東 通信研發部

 

 

 
 
 
專欄介紹

新(xīn)東網自2001年成立以來,掌握大數據、雲計算、通信、物(wù)聯網及區(qū)塊鏈等信息技(jì )術,擁有(yǒu)一支逾16年經驗的強大IT團隊。為(wèi)沉澱企業技(jì )術實力,繼續發揮行業優勢,《東網快訊》特邀新(xīn)東網技(jì )術大咖帶您走進這些領先進信息技(jì )術,揭秘新(xīn)東網16年來的技(jì )術成果,每周五發布

 

 

 

= ̄ω ̄=什麽鬼?這麽長(cháng)!不想看啦!

 

别急呀!讓哥(gē)告訴你這是什麽鬼!!!

 

➤先看下面這個配置文(wén)件,如果每一項你都懂,那本文(wén)能(néng)帶給你的收獲也許就比較有(yǒu)限,你可(kě)以快速浏覽或直接跳過;如果你對很(hěn)多(duō)選項存在着疑惑,那花(huā)一段時間慢慢閱讀本文(wén),你的疑惑一定一個一個都會消失;如果你以前沒怎麽接觸過Webpack,而你又(yòu)對Webpack感興趣,那麽動手跟着本文(wén)中(zhōng)那個貫穿始終的例子寫一次,以後你會發現你已明明白白地穿越Webpack的大門。

 

 

 

基于上一期華西的老闫→_→介紹了React的衍生技(jì )術React Native(多(duō)麽無私啊,請猛戳我的姊妹篇吧!),這期我們就來談談用(yòng)于React開發和模塊管理(lǐ)的主流工(gōng)具(jù)Webpack。

 

雖然Webpack是一個通用(yòng)的工(gōng)具(jù),并不隻适合于React,但由于很(hěn)多(duō)涉及React的項目都使用(yòng)了Webpack,尤其是還有(yǒu)react-hot-loader這樣的神器存在,于是很(hěn)自然地,Webpack成為(wèi)了最主流的React開發工(gōng)具(jù)。

 

Webpack有(yǒu)點類似browserify,出自Facebook的Instagram團隊,但功能(néng)比browserify更為(wèi)強大。其主要特性如下:

 

1. 同時支持CommonJSAMD模塊(對于新(xīn)項目,推薦直接使用(yòng)CommonJS);

 

2. 串聯式模塊加載器以及插件機制,讓其具(jù)有(yǒu)更好的靈活性和擴展性,例如提供對CoffeeScript、ES6的支持;

 

3. 可(kě)以基于配置或者智能(néng)分(fēn)析打包成多(duō)個文(wén)件,實現公(gōng)共模塊或者按需加載;

 

4. 支持對CSS、圖片等資源進行打包,從而無需借助Grunt或Gulp;

 

5. 開發時在内存中(zhōng)完成打包,性能(néng)更快,完全可(kě)以支持開發過程的實時打包需求;

 

6. 對sourcemap有(yǒu)很(hěn)好的支持,易于調試。

 

Webpack将項目中(zhōng)用(yòng)到的一切靜态資源都視之為(wèi)模塊,模塊之間可(kě)以互相依賴。Webpack對它們進行統一的管理(lǐ)以及打包發布,其官方主頁(yè)用(yòng)下面這張圖來說明Webpack的作(zuò)用(yòng):

 


 

可(kě)以看到Webpack的目标就是對項目中(zhōng)的靜态資源進行統一管理(lǐ),為(wèi)産(chǎn)品的最終發布提供優秀的打包部署方案。

 

 

安(ān)裝(zhuāng)webpack 加載一個簡單的React組件
 

 

Webpack一般作(zuò)為(wèi)全局的npm模塊安(ān)裝(zhuāng):

npm install -g webpack

之後便有(yǒu)了全局的webpack命令,直接執行此命令會默認使用(yòng)當前目錄的webpack.config.js作(zuò)為(wèi)配置文(wén)件。如果要指定另外的配置文(wén)件,可(kě)以執行:

webpack —config webpack.custom.config.js

盡管Webpack可(kě)以通過命令行來指定參數,但我們通常會将所有(yǒu)相關參數定義在配置文(wén)件中(zhōng)。一般我們會定義兩個配置文(wén)件,一個用(yòng)于開發時,另外一個用(yòng)于産(chǎn)品發布。生産(chǎn)環境下的打包文(wén)件不需要包含sourcemap等用(yòng)于開發時的代碼。配置文(wén)件通常放在項目根目錄之下,其本身也是一個标準的CommonJS模塊。

 

一個最簡單的Webpack配置文(wén)件webpack.config.js如下所示:

module.exports = {

  entry:[

    './app/main.js'

  ],

  output: {

    path: __dirname + '/assets/',

    publicPath: "/assets/",

    filename: 'bundle.js'

  }

};

其中(zhōng)entry參數定義了打包後的入口文(wén)件,數組中(zhōng)的所有(yǒu)文(wén)件會按順序打包。每個文(wén)件進行依賴的遞歸查找,直到所有(yǒu)相關模塊都被打包。output參數定義了輸出文(wén)件的位置,其中(zhōng)常用(yòng)的參數包括:

 

· path: 打包文(wén)件存放的絕對路徑

· publicPath: 網站運行時的訪問路徑

· filename: 打包後的文(wén)件名(míng)

 

現在來看如何打包一個React組件。假設有(yǒu)如下項目文(wén)件夾結構:

- react-sample

  + assets/

   - js/

     Hello.js

     entry.js

   index.html

   webpack.config.js

其中(zhōng)Hello.js定義了一個簡單的React組件,使用(yòng)ES6語法:

var React = require('react');

class Hello extends React.Component {

  render() {

    return (

      <h1>Hello {this.props.name}!h1>

    );

  }

}

module.exports= Hello;

entry.js是入口文(wén)件,将一個Hello組件輸出到界面:

var ReactDOM = require('react-dom');

var React = require('react');

var Hello = require('./Hello');

ReactDOM.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組件語法,需要對它們進行預處理(lǐ),這就要引入webpack的JSX加載器。因此在配置文(wén)件中(zhōng)加入如下配置:

module: {

  loaders: [

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

  ]

}

加載器的概念稍後還會詳細介紹,這裏隻需要知道它能(néng)将JSX編譯成JavaScript并加載為(wèi)Webpack模塊。這樣在當前目錄執行webpack命令之後,在assets目錄将生成bundle.js,打包了entry.js的内容。當浏覽器打開當前服務(wù)器上的index.html,将顯示“Hello Nate!”。這是一個非常簡單的例子,演示了如何使用(yòng)Webpack來進行簡單的React組件打包。

 

加載AMD或CommonJS模塊
 

 

在實際項目中(zhōng),代碼以模塊進行組織,AMD是在CommonJS的基礎上考慮了浏覽器的異步加載特性而産(chǎn)生的,可(kě)以讓模塊異步加載并保證執行順序。而CommonJS的require函數則是同步加載。在Webpack中(zhōng)更加推薦CommonJS方式去加載模塊,這種方式語法更加簡潔直觀。即使在開發時,我們也是加載Webpack打包後的文(wén)件,通過sourcemap去進行調試。

 

除了項目本身的模塊,我們也需要依賴第三方的模塊,現在比較常用(yòng)的第三方模塊基本都通過npm進行發布,使用(yòng)它們已經無需單獨下載管理(lǐ),需要時執行npm install即可(kě)。例如,我們需要依賴jQuery,隻需執行:

npm install jquery —save-dev

更多(duō)情況下我們是在項目的package.json中(zhōng)進行依賴管理(lǐ),然後通過直接執行npm install來安(ān)裝(zhuāng)所有(yǒu)依賴。這樣在項目的代碼倉庫中(zhōng)并不需要存儲實際的第三方依賴庫的代碼。

 

安(ān)裝(zhuāng)之後,在需要使用(yòng)jquery的模塊中(zhōng)需要在頭部進行引入:

var $ = require('jquery');

$('body').html('Hello Webpack!');

 

可(kě)以看到,這種以CommonJS的同步形式去引入其它模塊的方式代碼更加簡潔。浏覽器并不會實際的去同步加載這個模塊,require的處理(lǐ)是由Webpack進行解析和打包的,浏覽器隻需要執行打包後的代碼。Webpack自身已經可(kě)以完全處理(lǐ)JavaScript模塊的加載,但是對于React中(zhōng)的JSX語法,這就需要使用(yòng)Webpack的擴展加載器來處理(lǐ)了。

 

 

Webpack開發服務(wù)器
 

 

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

 

Webpack開發服務(wù)器需要單獨安(ān)裝(zhuāng),同樣是通過npm進行:

npm install -g webpack-dev-server

之後便可(kě)以運行webpack-dev-server命令來啓動開發服務(wù)器,然後通過localhost:8080/webpack-dev-server/訪問到頁(yè)面了。默認情況下服務(wù)器以當前目錄作(zuò)為(wèi)服務(wù)器目錄。在React開發中(zhōng),我們通常會結合react-hot-loader來使用(yòng)開發服務(wù)器,因此這裏不做太多(duō)介紹,隻需要知道有(yǒu)這樣一個開發服務(wù)器可(kě)以用(yòng)于開發時的内容實時打包和推送。

 

Webpack模塊加載器(Loaders)
 

 

Webpack将所有(yǒu)靜态資源都認為(wèi)是模塊,比如JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖片等等,從而可(kě)以對其進行統一管理(lǐ)。為(wèi)此Webpack引入了加載器的概念,除了純JavaScript之外,每一種資源都可(kě)以通過對應的加載器處理(lǐ)成模塊。和大多(duō)數包管理(lǐ)器不一樣的是,Webpack的加載器之間可(kě)以進行串聯,一個加載器的輸出可(kě)以成為(wèi)另一個加載器的輸入。比如LESS文(wén)件先通過less-load處理(lǐ)成css,然後再通過css-loader加載成css模塊,最後由style-loader加載器對其做最後的處理(lǐ),從而運行時可(kě)以通過style标簽将其應用(yòng)到最終的浏覽器環境。

 

對于React的JSX也是如此,它通過jsx-loader來載入。jsx-loader專門用(yòng)于載入React的JSX文(wén)件,Webpack的加載器支持參數,jsx-loader就可(kě)以添加?harmony參數使其支持ES6語法。為(wèi)了讓Webpack識别什麽樣的資源應該用(yòng)什麽加載器去載入,需要在配置文(wén)件進行配置:通過正則表達式對文(wén)件名(míng)進行匹配。例如:

 

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'

    }]

  }

 

可(kě)以看到,該使用(yòng)什麽加載器完全取決于這裏的配置,即使對于JSX文(wén)件,我們也可(kě)以用(yòng)js作(zuò)為(wèi)後綴,從而所有(yǒu)的JavaScript都可(kě)以通過jsx-loader載入,因為(wèi)jsx本身就是完全兼容JavaScript的,所以即使沒有(yǒu)JSX語法,普通JavaScript模塊也可(kě)以使用(yòng)jsx-loader來載入。

 

加載器之間的級聯是通過感歎号來連接,例如對于LESS資源,寫法為(wèi)style-loader!css-loader!less-loader。對于小(xiǎo)型的圖片資源,也可(kě)以将其進行統一打包,由url-loader實現,代碼中(zhōng)url-loader?limit=8192含義就是對于所有(yǒu)小(xiǎo)于8192字節的圖片資源也進行打包。這在一定程度上可(kě)以替代Css Sprites方案,用(yòng)于減少對于小(xiǎo)圖片資源的HTTP請求數量。

 

除了已有(yǒu)加載器,也可(kě)以自己實現自己的加載器,從而可(kě)以讓Webpack統一管理(lǐ)項目特定的靜态資源。

 

React開發神器:react-hot-loader
 

 

Webpack本身具(jù)有(yǒu)運行時模塊替換功能(néng),稱之為(wèi)Hot Module Replacement (HMR)。當某個模塊代碼發生變化時,Webpack實時打包将其推送到頁(yè)面并進行替換,從而無需刷新(xīn)頁(yè)面就實現代碼替換。這個過程相對比較複雜,需要進行多(duō)方面考慮和配置。而現在針對React出現了一個第三方react-hot-loader加載器,使用(yòng)這個加載器就可(kě)以輕松實現React組件的熱替換,非常方便。其實正是因為(wèi)React的每一次更新(xīn)都是全局刷新(xīn)的虛拟DOM機制,讓React組件的熱替換可(kě)以成為(wèi)通用(yòng)的加載器,從而極大提高開發效率。

 

要使用(yòng)react-hot-loader,首先通過npm進行安(ān)裝(zhuāng):

npm install —save-dev react-hot-loader

之後,Webpack開發服務(wù)器需要開啓HMR參數hot,為(wèi)了方便,我們創建一個名(míng)為(wèi)server.js的文(wén)件用(yòng)以啓動Webpack開發服務(wù)器:

 

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');

});

為(wèi)了熱加載React組件,我們需要在前端頁(yè)面中(zhōng)加入相應的代碼,用(yòng)以接收Webpack推送過來的代碼模塊,進而可(kě)以通知所有(yǒu)相關React組件進行重新(xīn)Render。加入這個代碼很(hěn)簡單:

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中(zhōng)啓動Webpack開發服務(wù)器的地址匹配。這樣,打包生成的文(wén)件就知道該從哪裏去獲取動态的代碼更新(xīn)。下一步,我們需要讓Webpack用(yòng)react-hot-loader去加載React組件,如前面所介紹,這通過加載器配置完成:

loaders: [{

    test: /\.js$/,

    exclude: /node_modules/,

    loader: 'react-hot!jsx-loader?harmony'

  },

  …

]

做完這些配置之後,使用(yòng)Node.js運行server.js:

node server.js

即可(kě)啓動開發服務(wù)器并實現React組件的熱加載。為(wèi)了方便,我們也可(kě)以在package.json中(zhōng)加入一節配置:

"scripts": {

  "start": "node ./js/server.js"

}

從而通過npm start命令即可(kě)啓動開發服務(wù)器。

這樣,React的熱加載開發環境即配置完成,任何修改隻要以保存,就會在頁(yè)面上立刻體(tǐ)現出來。無論是對樣式修改,還是對界面渲染的修改,甚至事件綁定處理(lǐ)函數的修改,都可(kě)以立刻生效,不得不說是提高開發效率的神器。

 

将Webpack開發服務(wù)器集成到已有(yǒu)服務(wù)器
 

 

盡管Webpack開發服務(wù)器可(kě)以直接用(yòng)于開發,但實際項目中(zhōng)我們基本都使用(yòng)自己的Web服務(wù)器。這就需要我們能(néng)将Webpack的服務(wù)集成到已有(yǒu)服務(wù)器,來使用(yòng)Webpack提供的模塊打包和加載功能(néng)。要實現這一點其實非常容易,隻需要在載入打包文(wén)件時指定完整的URL地址,例如:

 

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

 

這就告訴當前頁(yè)面應該去另外一個服務(wù)器獲得腳本資源文(wén)件,在之前我們已經在配置文(wén)件中(zhōng)指定了開發服務(wù)器的地址,因此打包後的文(wén)件也知道應該通過哪個地址去建立Socket IO來動态加載模塊。整個資源架構如下圖所示: 

 

打包成多(duō)個資源文(wén)件
 

 

将項目中(zhōng)的模塊打包成多(duō)個資源文(wén)件有(yǒu)兩個目的:

 

1. 将多(duō)個頁(yè)面的公(gōng)用(yòng)模塊獨立打包,從而可(kě)以利用(yòng)浏覽器緩存機制來提高頁(yè)面加載效率;

2. 減少頁(yè)面初次加載時間,隻有(yǒu)當某功能(néng)被用(yòng)到時,才去動态的加載。

Webpack提供了非常強大的功能(néng)讓你能(néng)夠靈活的對打包方案進行配置。首先來看如何創建多(duō)個入口文(wén)件:

{

  entry: { a: "./a", b: "./b" },

  output: { filename: "[name].js" },

  plugins: [ new webpack.CommonsChunkPlugin("init.js") ]

}

可(kě)以看到,配置文(wén)件中(zhōng)定義了兩個打包資源“a”和“b”,在輸出文(wén)件中(zhōng)使用(yòng)方括号來獲得輸出文(wén)件名(míng)。而在插件設置中(zhōng)使用(yòng)了CommonsChunkPlugin,Webpack中(zhōng)将打包後的文(wén)件都稱之為(wèi)“Chunk”。這個插件可(kě)以将多(duō)個打包後的資源中(zhōng)的公(gōng)共部分(fēn)打包成單獨的文(wén)件,這裏指定公(gōng)共文(wén)件輸出為(wèi)“init.js”。這樣我們就獲得了三個打包後的文(wén)件,在html頁(yè)面中(zhōng)可(kě)以這樣引用(yòng):

<script src="init.js">script>

<script src="a.js">script>

<script src="b.js">script>

除了在配置文(wén)件中(zhōng)對打包文(wén)件進行配置,還可(kě)以在代碼中(zhōng)進行定義:require.ensure,例如:

require.ensure(["module-a", "module-b"], function(require) {

  var a = require("module-a");

  // ...

});

Webpack在編譯時會掃描到這樣的代碼,并對依賴模塊進行自動打包,運行過程中(zhōng)執行到這段代碼時會自動找到打包後的文(wén)件進行按需加載。

 

來小(xiǎo)結一下
 

 

結合React介紹了Webpack的基本功能(néng)和用(yòng)法,希望能(néng)讓大家對這個新(xīn)興而強大的模塊管理(lǐ)工(gōng)具(jù)有(yǒu)一個總體(tǐ)的認識,并能(néng)将其應用(yòng)在實際的項目開發中(zhōng)。

 

分(fēn)享到:
地址:福建省福州市銅盤路軟件大道89号軟件園A區(qū)26号樓 電(diàn)話:0591-83519233 傳真:0591-87882335 E-mail:doone@doone.com.cn
版權所有(yǒu) 新(xīn)東網科(kē)技(jì )有(yǒu)限公(gōng)司 京ICP證000000号 閩公(gōng)網安(ān)備 35010202001006号