这是React和ECMAScript2015系列文章的最后一篇,我们将继续探索React 和 Webpack的使用。

下面是所有系列文章章节的链接:

本篇文章Github源码

React JS

什么是Webpack?

就像JSPM一样,Webpack是你的前端应用的模块管理的解决方案。

使用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

  • 项目运行效果图

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
全栈部落完整系列教程:全栈部落完整电子书学习笔记

关注全栈部落官方公众号,每晚十点接收系列原创技术推送

React 和 ES6 工作流之 Webpack的使用(第六部分)的更多相关文章

  1. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  2. webpack+react+redux+es6

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  3. webpack+react+redux+es6开发模式---续

    一.前言 之前介绍了webpack+react+redux+es6开发模式 ,这个项目对于一个独立的功能节点来说是没有问题的.假如伴随着源源不断的需求,前段项目会涌现出更多的功能节点,需要独立部署运行 ...

  4. Vue ES6 Jade Scss Webpack Gulp

    一直以来非常庆幸曾经有翻过<代码大全2>:这使我崎岖编程之路少了很多不必要的坎坷.它在软件工艺的话题中有写到一篇:"首先是为人写程序,其次才是机器(Write Programs ...

  5. React与ES6(一)开篇介绍

    React与ES6系列: React与ES6(一)开篇介绍 React和ES6(二)ES6的类和ES7的property initializer React与ES6(三)ES6类和方法绑定 React ...

  6. React与ES6(四)ES6如何处理React mixins

    React与ES6系列: React与ES6(一)开篇介绍 React和ES6(二)ES6的类和ES7的property initializer React与ES6(三)ES6类和方法绑定 React ...

  7. React与ES6(三)ES6类和方法绑定

    React与ES6系列: React与ES6(一)开篇介绍 React和ES6(二)ES6的类和ES7的property initializer React与ES6(三)ES6类和方法绑定 React ...

  8. React和ES6(二)ES6的类和ES7的property initializer

    React与ES6系列: React与ES6(一)开篇介绍 React和ES6(二)ES6的类和ES7的property initializer React与ES6(三)ES6类和方法绑定 React ...

  9. React Native & ES6 & emoji

    React Native & ES6 & emoji && 逻辑运算符 https://developer.mozilla.org/zh-CN/docs/Web/Jav ...

随机推荐

  1. 【有奖调研】来,聊聊TTS音色定制这件事儿

    音色个性化定制,一个能让文字转语音服务(TTS)在用户交互过程中注入温度的技术. 文能在营销及内容交付中让品牌保持一致性,武能让开发者"音"量加持,创新开发. 这个100%钢铁纯技 ...

  2. unittest简介01

    前言 熟悉java的应该都清楚常见的单元测试框架Junit和TestNG,python里面也有单元测试框架-unittest,相当于是一个python版的junit. 一.unittest简介 1.先 ...

  3. webgl未使用独立显卡报告

    0x00 楔子 最近客户在使用我们的的三维可视化平台的时候,总是会出现浏览器崩溃,webgl context lost的情况. 这种请一般出现在电脑配置很低的电脑上,比如没有独显,只有很小现存的集成显 ...

  4. redis php使用实例

    redis的操作很多的,以前看到一个比较全的博客,但是现在找不到了.查个东西搜半天,下面整理一下php处理redis的例子,个人觉得常用一些例子.下面的例子都是基于php-redis这个扩展的. 1, ...

  5. CF1601C题解

    赛时一小时,赛后十分钟. 题意:给定一个序列 \(a\) 和一个集合 \(b\),问将 \(b\) 中所有元素插入 \(a\) 后逆序对最少是多少. 观察样例解释,发现 \(b\) 已经被排序过了,于 ...

  6. .NET6: 开发基于WPF的摩登三维工业软件 (10) - 机器人

    基于前文介绍的Ribbon界面.插件化.MVVM模式等内容,我们搭建了一个软件雏形.本文将综合之前的内容在RapidCAX框架中集成Robot组件,实现一个简单的机器人正向模拟模块. 1 目标 基于M ...

  7. ybt1130:找第一个只出现一次的字符

    1130:找第一个只出现一次的字符 时间限制: 1000 ms         内存限制: 65536 KB提交数: 62333     通过数: 23786 [题目描述] 给定一个只包含小写字母的字 ...

  8. AQS详解之独占锁模式

    AQS介绍 AbstractQueuedSynchronizer简称AQS,即队列同步器.它是JUC包下面的核心组件,它的主要使用方式是继承,子类通过继承AQS,并实现它的抽象方法来管理同步状态,它分 ...

  9. CSI 工作原理与JuiceFS CSI Driver 的架构设计详解

    容器存储接口(Container Storage Interface)简称 CSI,CSI 建立了行业标准接口的规范,借助 CSI 容器编排系统(CO)可以将任意存储系统暴露给自己的容器工作负载.Ju ...

  10. Git-rebase使用原理

    使用 Git 已经好几年了,却始终只是熟悉一些常用的操作.对于 Git Rebase 却很少用到,直到这一次,不得不用. 一.起因 上线构建的过程中扫了一眼代码变更,突然发现,commit 提交竟然多 ...