React与Webpack很合拍.尽管你可以使用其它的React构建工具,但Webpack仍是一个不错的选择并且配置简单.在这章,我们将扩展我们的配置,之后我们将为我们进一步的开发应用设置一个好的起点.
Facebook的React改变了我们前端开发的方式.另外感谢React Native使开发并不局限于web中.React是小而强的.
React不是如Angular.js或Ember这样的框架. 框架往往提供了大量的开箱即用的解决方案. 对于React你将不得不从单独的库中进行组装.这两种方法都有其优点.使用框架更易上手,但是一切都必需循规蹈矩,基于库的方式给了你足够的灵活性.但同样也考验你对架构的把握.
React对web开发者提出了虚拟DOM的概念.React自已管理DOM方式与之前的所有库和框架都不同.React会在它认为最合适的时候分批的将改变应用于真实的DOM中.
React提供了一个高级的API用来生成虚拟DOM. 使用API可以生成复杂的结构非常笨重.因此我们通常不会手写它.作为替代,使用一些中间格式来转换到它.Facebook的JSX是一个流行的格式.
JSX是JavaScript的超集.允许你混合XMLish语法到JavaScript中.看下面的示例:
const Names = () => {
const names = ['John', 'Jill', 'Jack'];
return (
<div>
<h2>Names</h2>
{/* This is a list of names */}
<ul className="names">{
names.map(name =>
<li className="name">{name}</li>
)
}</ul>
</div>
);
};
如果你不了解JSX语法前你会觉得这很奇怪.但是当你开始了解它,就会明白为什么是这样的.
注意
render()
必需反回一个单节点.反回多个将不工作.
在JSX中我们混入了一些有点像HTML的JavaScript.注意我们怎样处理属性,我们使用className
替代传统HTML中的class
,尽管JSX起初使用会觉得有点奇怪,随着时间的推移将会习惯.
React开发者从DOM的局限性中摆脱了出来.React是高性能的.尽管这是有代价的.这个库不是你想的那样小.一般的小应用程序打包后在150-200k左右.使用gzip压缩过会变的很小.
Babel在社区中反响强列.它让我们可以现在就使用JavaScript的最新技术.它会把新的功能代码转变为浏览器知道的格式.你甚至可以用它来开发你自已语言的功能.Babel的内置的JSX支持将为我们带来便利.
Babel支持超过ES6标准的ES7的一些实验功能.其中一些功能是核心的,而另一些可能马上被废弃.这个分为下面几个阶段:
要非常小心阶段0的功能.它是不稳定的.如果你愿意冒险,你可以在练习时尝试它们.
Babel online 可以看到它生成的代码.
babel-loader
在Webpack中使用Babel可以通过babel-loader,它可以把基于ES6的模块定义打包成ES5规范.安装babel-loader
npm i babel-loader babel-core --save-dev
babel-core包含Babel的核心逻辑.
为了使它工作,我们需要一个配置来增加对babel-loader的声明.它使用正则表达式(/\.jsx?$/
)匹配.js
和.jsx
.
考虑到性能我们应该限制在./app目录内进行加载和操作.这种方式不会遍历node_modules目录.另一种方式是建一个排除规则exclude
把node_modeules排除.我觉得使用include
更有用,它看起来是明确的.
下面是相关的配置,可以使Babel工作:
webpack.config.js
...
const common = {
entry: {
app: PATHS.app
},
leanpub-start-insert
// Add resolve.extensions.
// '' is needed to allow imports without an extension.
// Note the .'s before extensions as it will fail to match without!!!
resolve: {
extensions: ['', '.js', '.jsx']
},
leanpub-end-insert
output: {
path: PATHS.build,
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.css$/,
loaders: ['style', 'css'],
include: PATHS.app
}
leanpub-start-insert
},
// Set up jsx. This accepts js too thanks to RegExp
{
test: /\.jsx?$/,
// Enable caching for improved performance during development
// It uses default OS directory by default. If you need something
// more custom, pass a path to it. I.e., babel?cacheDirectory=<path>
loaders: ['babel?cacheDirectory'],
// Parse only app files! Without this it will go through entire project.
// In addition to being slow, that will most likely result in an error.
include: PATHS.app
}
leanpub-end-insert
]
}
};
...
注意resolve.extensions
设置允许参考到没扩展名的JSX文件.但我会明确的使用扩展名,但你可以按你的想法来做.
另外,我们需要一个.babelrc文件.虽然你能通过设置Webpack(例如 babel?presets[]=react,presets[]=es2015
),但是它只限于Webpack.这就是为什么我们需要.babelrc的原因.这种思想也适用于其它工具.如ESLint.
Babel 6依赖plugins,有两种类型的插件:语法和变换.前者允许Babel解析附加的语法,而后者应用于转换.这种方式可以把先进的功能转为老版本环境可以理解的语法.
为了便于使用插件.Babel有一个presets概念,每个预置附带一组插件,使你不用分别连接它们,在这个例子里,我们将依赖ES2015和React预置.
npm i babel-preset-es2015 babel-preset-react --save-dev
除此之外,我们将使一些定制功能使项目更方便开发:
renderNote = (note) => {
自动约束这个renderNote
方法到到实例@DragDropContext(HTML5Backend)
.这个注解让我们可以附加功能到类和方法上.const {a, b, ...props} = this.props
,这个语法让我们简单的从对像中提取属性.为了更简单的配置这些功能.我创建了一个特定的预置包含它们.
预置是简单的npm模块导出为Babel配置.如果你想跨多个工程共享相同的设置.像这样管理预置很有用.安装:
npm i babel-preset-survivejs-kanban --save-dev
接下来我们需要设置.babelrc文件.
.babelrc
{
"presets": [
"es2015",
"react",
"survivejs-kanban"
]
}
Babel支持明确指定阶段.更清楚的依赖你可能想使用的任何自定义功能.这个文件使你的工程更易维护.更多配置请看 .babelrc options .
Babel preset是简单引入了其它presets和插件的Node.js的包. preset的想法是拉取它需要的依赖,并通过标准的Node.js接口暴露它.例如我们定义一个preset像:
index.js
module.exports = {
plugins: [
require('babel-plugin-syntax-class-properties'),
require('babel-plugin-syntax-decorators'),
require('babel-plugin-syntax-object-rest-spread'),
// You can pass parameters using an array syntax
[
require('babel-plugin-transform-regenerator'),
{
async: false,
asyncGenerators: false
}
]
]
};
这个例子,我们取得指定的插件到预置.你还可以得到这些插件直接从你的.babelrc通过plugins
字段.如果你只需要一两个这样做是方便的,配置开始增长,考虑到提取它们到一个preset中.你也可以定义presets
字段,为你的工程引入其它presets.
假设我们命名我们的包为babel-preset-survivejs-kanban,然后安装它作为我们的项目并连接它使用Babel配置.注意babel-preset前缀,它允许我们在多个相似的工程之间共享presets.
可以配置Webpack来使用Babel的transpiled(转译)特性.只要重命名webpack.config.js到webpack.config.babel.js且Webpack会采用Babel设置你的工程.它将遵守.babelrc中的设置.
为了使它工作.你需要安装babel-register到你的工程.Webpack内部依赖interpret来工作.
Webpack的机载器声明是比较灵活的.接下来的例子会给你带来灵感.第一个例子显示了怎么通过查询串传递参数:
{
test: /\.jsx?$/,
loaders: [
'babel?cacheDirectory,presets[]=react,presets[]=es2015,presets[]=survivejs-kanban'
],
include: PATHS.app
}
考虑到查询串这种方式是不易读的,使用组合loader
和query
字段:
{
test: /\.jsx?$/,
loader: 'babel',
query: {
cacheDirectory: true,
presets: ['react', 'es2015', 'survivejs-kanban']
},
include: PATHS.app
}
首先,我们需要定义一个App
.它是我们程序的核心.它代表了我们应用程序的高级视图和入口.我们开始使用React的基于类的组件定义语法:
app/components/App.jsx
import React from 'react';
import Note from './Note.jsx';
export default class App extends React.Component {
render() {
return <Note />;
}
}
你还可以使用语法
import React, {Component} from 'react';
从react
中引入,然后class App extends Component
必需引入React
因为JSX将语法将转成React.createElement
调用.
Note
我们还需要定义Note
组件.在这个例子中.我们只想显示一些文字.因为这个组件是简单的.我们能使用React的基于函数的组件定义.
app/components/Note.jsx
import React from 'react';
export default () => <div>Learn Webpack</div>;
虽然我们没有直接在代码中引用React.但最好记住最终JSX将转换为调用它.因此如果你移除了imort
语句,代码将中断.Babel插件babel-plugin-react-require 将自动生成import
语句.如果你不想写imports.
为了保证程序工作,我们将改变我们的inde.js用来渲染这个组件.因为里面有JSX内容,所以我们重命名它为index.jsx.我们将渲染内容使用react-dom包.
app/index.jsx
import './main.css';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
如果你没有使用npm-install-webpack-plugin.记住要安装react和react-dom到你的工程下面
npm i react react-dom -S
尝试运行npm start
,在浏览器中转到localhost:8080查看结果.
试着修改你的React组件.能看到Webpack强制刷新.这是我们下一步需要通过热加载解决的.
在继续之前, 删掉component.js文件吧.因为我们不需要它了.
避免直接渲染到
document.body
,这样做会发生奇怪的问题并且React也会给出一个警告.
注意到我们每次修改好浏览器都会闪一下.这样做意为着我们失去了之前的状态.虽然没关系.但当我们扩大我们的应用时将是痛苦的.有时我们测试时正好需要前一个状态.
babel-plugin-react-transform这里写链接内容让我们用多种手段来使用React.热加载就是其中之一.通过react-transform-hmr使其生效.
当有改变时react-transform-hmr 将一个一个更新React组件而不是强制完全刷新.因为它只是替换方法,它不会捕捉每一个可能的变化.包括改变类的构造函数,这将需要你强制刷新,但大多数情况下它工作是很好的.
babel-preset-react-hmre 这里写链接内容将会让我们安装简单.它有妥当的默认值且减少大量的需要维护配置.安装通过:
npm i babel-preset-react-hmre --save-dev
我们还需要让Babel知道HMR. 首先我们要通过Webpack的配置传递目标环境到Babel.这让我们能控制环境通过.babelrc.在这个例子里我们允许HMR只在开发环境中.如果你想允许一些插件到产品构建,道理是相同的.
一个简单的方式控制.babelrc是设置BABEL_ENV环境变量,作为npm生命周期事件.
webpack.config.js
leanpub-start-insert
process.env.BABEL_ENV = TARGET;
leanpub-end-insert
const common = {
...
};
...
此外,我们需要增加我们Babel的配置.包含我们开发过程中的插件.Babel确认值是这样的:
development
.配置BABEL_ENV='start'
.babelrc
{
"presets": [
"es2015",
"react",
"survivejs-kanban"
leanpub-start-delete
]
leanpub-end-delete
leanpub-start-insert
],
"env": {
"start": {
"presets": [
"react-hmre"
]
}
}
leanpub-end-insert
}
尝试再次执行npm start
并修改这个组件.注意到这次没有刷新浏览器.这是一个强大的功能.
你现在应该知道怎么在Webpack中设置React.执加载是使 Webpack 与众不同的特征之一.现在我们有了一个好的开发环境.我们能专注于React的开发.下一章你将学习怎么实现一个小的记事本应用.