React 和 ES6 工做流之 Webpack的使用(第六部分)

這是React和ECMAScript2015系列文章的最後一篇,咱們將繼續探索React 和 Webpack的使用。css

下面是全部系列文章章節的連接:html

本篇文章Github源碼github

React JS

什麼是Webpack?

就像JSPM同樣,Webpack是你的前端應用的模塊管理的解決方案。web

使用Webpack,你可以用一種方便的方法徹底控制你的應用資源。

爲何Webpack這麼受歡迎?主要有如下幾個緣由:

  • Webpack使用npm做爲外部模塊源。若是你想添加React到你的項目中,只須要執行 npm install react便可。這是一個附加的優點,由於你已經知道如何將你喜歡的庫添加到你的項目中。

  • 你幾乎能夠加載全部的東西,而不僅是JavaScript。Webpack使用名字爲loaders的裝載機來完成加載。這是對應的loaders清單

  • Webpack有一個很強大的開發工具生態系統。像熱更新這樣的東西將戲劇性的改變你的開發流程。

  • 對於各類類型的任務有不少Webpack plugins。在大多數狀況下,你可使用已經存在的解決方案。

  • Webpack 有很漂亮的logo :)

Getting started

讓咱們開始從以前的系列文章中調整咱們的應用程序。

首先,咱們將要安裝初始的開發依賴。

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 presetstage-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 loaders

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,es2015stage-0presets。

如今,不管何時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 Middlewareexpress.js服務器。

添加熱刷新模塊

Webpack Dev Middleware已經包含了熱刷新的特性。不管何時,你的代碼發生變化,它都會當即刷新頁面。

若是想簡單的看看熱刷新的演示效果,能夠看看Dan Abramov視頻

爲了激活Hot Module Reloading,你首先得安裝必須得npm包。

npm install --save-dev webpack-hot-middleware

而後在webpack.config.dev.js文件中設置entryplugins:

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"
  },
  ...
}

運行項目

  • 運行npm start

  • 在瀏覽器中打開http://localhost:3000

  • 項目運行效果圖
    cover

Webpack生產環境配置

如今咱們可以在服務器上運行咱們的應用程序而且可以經過熱模塊更新刷新咱們的頁面。

可是若是咱們想要將產品部署到生產環境?沒問題,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.UglifyJsPluginUglifyJs壓縮。

  • 環境變量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
全棧部落完整系列教程:全棧部落完整電子書學習筆記

關注全棧部落官方公衆號,每晚十點接收系列原創技術推送
相關文章
相關標籤/搜索