這是React和ECMAScript2015系列文章的最後一篇,咱們將繼續探索React 和 Webpack的使用。css
下面是全部系列文章章節的連接:html
React類,方法綁定(第三部分)react
ES6中React Mixins的使用(第四部分)webpack
本篇文章Github源碼github
React | JS |
---|---|
![]() |
![]() |
就像JSPM同樣,Webpack
是你的前端應用的模塊管理的解決方案。web
使用Webpack,你可以用一種方便的方法徹底控制你的應用資源。
爲何Webpack這麼受歡迎?主要有如下幾個緣由:
Webpack使用npm做爲外部模塊源。若是你想添加React到你的項目中,只須要執行 npm install react
便可。這是一個附加的優點,由於你已經知道如何將你喜歡的庫添加到你的項目中。
你幾乎能夠加載全部的東西,而不僅是JavaScript。Webpack
使用名字爲loaders
的裝載機來完成加載。這是對應的loaders清單
Webpack有一個很強大的開發工具生態系統。像熱更新這樣的東西將戲劇性的改變你的開發流程。
對於各類類型的任務有不少Webpack plugins
。在大多數狀況下,你可使用已經存在的解決方案。
Webpack 有很漂亮的logo :)
讓咱們開始從以前的系列文章中調整咱們的應用程序。
首先,咱們將要安裝初始的開發依賴。
npm install --save-dev webpack npm install --save-dev babel-core npm install --save-dev babel-preset-es2015 babel-preset-react babel-preset-stage-0
在上面的列表中,webpack
是自解釋型的。Babel
是用於將ES6轉換成ES5(若是你閱讀了前面的React and ES6
系列文章,你應該對ES6和ES5很是熟悉)。自從babel 6後你必須爲每個額外的語言特徵安裝獨立的包。這些包叫作presets
。咱們安裝es2015 preset
,react preset
和stage-0 preset
。對於更多關於babel 6
的信息,你能夠閱讀這篇文章。
下一步,安裝非開發依賴(react和react-dom包):
npm install --save react react-dom
如今在你的項目中基於Webpack最重要的一步。在你的項目根目錄下面建立webpack.config.dev.js
文件。這個文件將用來打包你全部的在一個bundle(或者多個bundle)裏面的JavaScript(在大多數項目中不僅是JavaScript),打包完就能夠在用戶的瀏覽器中正式運行。
webpack.config.dev.js
的內容以下:
var path = require('path'); var webpack = require('webpack'); var config = { devtool: 'cheap-module-eval-source-map', entry: [ './app.js' ], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', publicPath: '/dist/' }, plugins: [ new webpack.NoEmitOnErrorsPlugin() ] }; module.exports = config;
以上代碼的亮點:
Line 5. 在提升應用程序的各類調試策略中,咱們有一個選擇,你能夠點擊這裏瞭解更多關於cheap-module-eval-source-map
的內容。
Lines 6-8. 這裏咱們定義了app.js
爲應用程序的主入口。
Lines 9-13. 這個配置制定Webpack
將打包全部的模塊成文件bundle.js
,而且將bundle.js
文件放到dist/
路徑下面。
用Webpack
幾乎能夠加載全部的東西到你的代碼中(這裏是清單)。Webpack使用的名字叫作Webpack裝載機。
你能夠制定文件擴展名關聯到特別的裝載機。
在咱們的案例中,咱們將使用babel-loader
來將ES2015 / ES6
的代碼轉換成ES5
.首先,咱們須要安裝npm
依賴包。
npm install --save-dev babel-loader
而後,經過添加一些新的裝載機關鍵字到出口對象中來調整webpack.config.dev.js
文件的配置。
var config = { ... add the below code as object key ... module: { loaders: [ { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ } ] } }; module.exports = config;
這裏須要重點注意的是,咱們經過exclude
關鍵字的設置禁止Webpack
解析node_modules
文件夾裏面的文件。
接下來咱們在項目的根目錄下面添加.babelrc
文件。
{ "presets": ["react", "es2015", "stage-0"] }
這個文件是配置babel
以便可以使用前面咱們添加的react
,es2015
和stage-0
presets。
如今,不管何時Webpack
遇到,好比:import CartItem from './cartItem.js'
,它將加載這個文件而且將ES6
轉換成ES5
。
Webpack
開發服務器爲了運行這個程序,咱們須要在服務器上運行這些文件。
幸運的是,Webpack
生態系統已經提供全部你須要的東西。你可使用Webpack開發服務器或者Webpack開發中間件,好比:Express.js。
咱們將使用後者。優點是在內存中處理文件時速度快。
讓咱們安裝npm
模塊:
npm install --save-dev webpack-dev-middleware express
下一步,在根目錄下面添加server.js
文件:
var path = require('path'); var express = require('express'); var webpack = require('webpack'); var config = require('./webpack.config.dev'); var app = express(); var compiler = webpack(config); var port = 3000; app.use(require('webpack-dev-middleware')(compiler, { noInfo: true, publicPath: config.output.publicPath })); app.use(require('webpack-hot-middleware')(compiler)); app.get('*', function (req, res) { res.sendFile(path.join(__dirname, 'index.html')); }); app.listen(port, function onAppListening(err) { if (err) { console.error(err); } else { console.info('==> Webpack development server listening on port'); } });
這是典型的使用Webpack Dev Middleware
的express.js
服務器。
Webpack Dev Middleware
已經包含了熱刷新的特性。不管何時,你的代碼發生變化,它都會當即刷新頁面。
若是想簡單的看看熱刷新的演示效果,能夠看看Dan Abramov
的視頻。
爲了激活Hot Module Reloading
,你首先得安裝必須得npm
包。
npm install --save-dev webpack-hot-middleware
而後在webpack.config.dev.js
文件中設置entry
和plugins
:
var config = { entry: [ './app.js', 'webpack-hot-middleware/client' ], ... plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ] }; module.exports = config;
若是想對React 應用更進一步使用模塊刷新其實有不少種方法。
其中一個簡單的方法就是安裝babel-preset-react-hmre
模塊。
npm install --save-dev babel-preset-react-hmre
調整.babelrc
文件的內容:
{ "presets": ["react", "es2015", "stage-0"], "env": { "development": { "presets": ["react-hmre"] } } }
到這一步,這個應用就具有熱刷新的功能。
建立index.html
文件
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>React and ES6 Part 6</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.2/css/foundation.min.css"> </head> <body> <nav class="top-bar" data-topbar role="navigation"> <section class="top-bar-section"> <ul class="left"> <li class="active"> <a href="http://egorsmirnov.me/2016/04/11/react-and-es6-part6.html" target="_blank"> Blog post at egorsmirnov.me: React and ES6 - Part 6, React and ES6 Workflow with Webpack </a> </li> </ul> </section> </nav> <div class="root"></div> <script src="/dist/bundle.js"></script> </body> </html>
建立app.js
文件
import React from 'react'; import ReactDOM from 'react-dom'; import CartItem from './cartItem.js'; const order = { title: 'Fresh fruits package', image: 'http://images.all-free-download.com/images/graphiclarge/citrus_fruit_184416.jpg', initialQty: 3, price: 8 }; ReactDOM.render( < CartItem title={order.title} image={order.image} initialQty={order.initialQty} price={order.price } />, document.querySelector('.root') ) ;
建立cartItem.js
文件
import React from 'react'; export default class CartItem extends React.Component { static propTypes = { title: React.PropTypes.string.isRequired, price: React.PropTypes.number.isRequired, initialQty: React.PropTypes.number }; static defaultProps = { title: 'Undefined Product', price: 100, initialQty: 0 }; state = { qty: this.props.initialQty, total: 0 }; constructor(props) { super(props); } componentWillMount() { this.recalculateTotal(); } increaseQty() { this.setState({qty: this.state.qty + 1}, this.recalculateTotal); } decreaseQty() { let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0; this.setState({qty: newQty}, this.recalculateTotal); } recalculateTotal() { this.setState({total: this.state.qty * this.props.price}); } render() { return ( <article className="row large-4"> <figure className="text-center"> <p> <img src={this.props.image}/> </p> <figcaption> <h2>{this.props.title}</h2> </figcaption> </figure> <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p> <p className="large-4 column"> <button onClick={this.increaseQty.bind(this)} className="button success">+</button> <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button> </p> <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p> <h3 className="large-12 column text-center"> Total: ${this.state.total} </h3> </article> ); } }
package.json
如今已經將前面全部碎片化的代碼已經整合在一個項目中。
咱們須要在package.json
文件的scripts
區域添加一些腳本。
{ "name": "awesome-application", "version": "1.0.0", ... "scripts": { "start": "node server.js" }, ... }
如今咱們可以在服務器上運行咱們的應用程序而且可以經過熱模塊更新刷新咱們的頁面。
可是若是咱們想要將產品部署到生產環境?沒問題,Webpack
有對應的解決方案。
建立webpack.config.prod.js
文件,文件內容爲:
var path = require('path'); var webpack = require('webpack'); var config = { devtool: 'source-map', entry: [ './app.js' ], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js' }, plugins: [ new webpack.optimize.OccurrenceOrderPlugin(), new webpack.DefinePlugin({ 'process.env': { 'NODE_ENV': JSON.stringify('production') } }), new webpack.optimize.UglifyJsPlugin({ compressor: { warnings: false } }) ], module: { loaders: [ { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ } ] } }; module.exports = config;
它和開發模式下的配置文件有點類似,可是有如下不一樣點:
熱刷新的功能再也不有,由於在生產環境中不須要這個功能。
JavaScript bundle
被依賴於webpack.optimize.UglifyJsPlugin
的UglifyJs
壓縮。
環境變量NODE_ENV
被設置成production
。這須要屏蔽來自React開發環境中的警告。
下一步,更新package.json
文件中的scripts
:
{ ... "scripts": { "start": "node server.js", "clean": "rimraf dist", "build:webpack": "NODE_ENV=production webpack --progress --colors --config webpack.config.prod.js", "build": "npm run clean && npm run build:webpack" }, ... }
到如今爲止,若是你在控制檯運行npm run build
,壓縮文件bundle.js
將被建立而且放在dist/
路徑下面。這個文件準備在生產環境中使用。
咱們剛纔學到的東西只是Webpack
的一些基礎。
Webpack
是一個很容易入門的工具,可是要想精通,須要點時間好好研究研究。
社羣品牌:從零到壹全棧部落
定位:尋找共好,共同窗習,持續輸出全棧技術社羣
業界榮譽:IT界的邏輯思惟
文化:輸出是最好的學習方式
官方公衆號:全棧部落
社羣發起人:春哥(從零到壹創始人,交流微信:liyc1215)
技術交流社區:全棧部落BBS
全棧部落完整系列教程:全棧部落完整電子書學習筆記
關注全棧部落官方公衆號,每晚十點接收系列原創技術推送 |
---|
![]() |