从头创建一个基于 React, webpack, babel 的模板项目

如果是一个刚接触 React 的新手,当学完了 React 的各种基本概念和语法之后,准备开始实际的开发工作时,他又会碰到各种新颖的名词:npm, webpack, babel, flux, es2015…… 如果以前接触过这些工具还好,否则为了建立一个简单的项目,还需要学习这一整套的流程,而在这中间又会碰上各种坑,这个过程将会非常痛苦。一个解决方案是去 GitHub 上寻找各种模板项目,用 React + webpack + … 作为关键字搜索可以发现许多别人创建的空项目,你可以在其基础上稍作修改然后开始开发。我在学习的过程中就是这么做的,但之后对其中的一些配置项仍是一知半解。

我在这里从头开始创建一个模板项目,并将过程记录下来,一方面希望能给看到这篇文章的人以帮助,另一方面也是加深我自己的理解。

最后完成的项目见这个项目:react-webpack-babel-boilerplate,在建立这个项目过程中的步骤可见另一个项目:create-react-boilerplate-steps,过程被分到了多个文件夹中。

项目结构

在开始之前先看看项目的文件结构:

- src
  - index.js
  - App.js
- .babelrc
- index.html
- package.json
- server.js
- webpack.config.js

以上就是我们的项目需要的全部文件了。

接下来我会将这个项目的构建过程分解成几个步骤,你只要按照这些步骤依次进行即可。

初始化项目

首先我们新建一个名为 react-webpack-boilerplate 的文件夹,然后需要在命令行下对项目进行初始化。

你需要到 Node.js 的官网上下载 node.js 并安装,如果你已经对 NPM 有了基本的了解,可以在命令行下进入项目目录运行:

npm init

之后你需要按照提示输入你的项目的基本信息,这里也可以一直回车,则选择默认选项,或者运行:

npm init -y    // 所有信息都按照默认设置

npm init 命令的作用就是初始化一个项目,你可以发现项目文件夹内多出了一个 package.json 文件,它的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 按照默认设置生成的 package.json 文件
{
"name": "react-webpack-boilerplate",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

项目的运行需要依赖各种 npm 包,需要在文件的 “dependencies” 或 “devDependencies” 项下写明 npm 包的名称及版本。现在这个项目还是空的,所以这里还没有这两项。

由于直接用 npm 安装的速度很慢,我们可以考虑用 cnpm install 命令来代替 npm install 命令。

CNPM 是淘宝建立的一个 NPM 镜像,由于服务器在国内,速度自然也就快多了。

// 安装 cnpm
$ npm install -g cnpm --registry=https://registry.npm.taobao.org

完成后,你就可以用 cnpm install 来安装所需的 npm 包了。

淘宝 NPM 镜像

安装及配置 webpack

webpack 是什么?

webpack 是一个打包工具,它能够将各种资源如 js、css、图片作为模块载入并进行打包。你可以从GitHub上这个教程
开始学习 webpack 的基本用法。

webpack 的安装

在命令行下面运行:

$ npm install webpack --save-dev

这一句的作用的是给项目安装 webpack,现在查看项目文件夹,会发现多出了一个 node_modules 文件夹,这里就是存放项目所依赖的 npm 包的地方。

上面的这条命令后面跟着的 --save-dev,表示将会把 webpack 的名称写入 package.json 文件中。我们打开 package.json 文件,发现文件中多了一项:

"devDependencies": {
    "webpack": "^1.13.2"
}

--save-dev 表示会写入 “devDependencies” 项中,而 --save 写入的是 “dependencies” 项。

webpack 的测试

你可以选择将 webpack 安装在全局中,或者安装在这一个项目中。全局安装时需要在命令后面加上 -global-g 参数,这样你可以直接在命令行中使用 webpack 命令了。

选择全局安装:

$ cnpm install webpack -g 

既然我们已经将 webpack 安装在了全局,那就先在命令行下测试一下它的打包功能。

新建 index.js 文件,里面只有一行:

1
console.log('This is a javascript file for test.');

运行一个最简单的 webpack 命令:

$ webpack index.js bundle.js

它的意思是将 index.js 文件打包成名为 bundle.js 的文件,后一个参数是你想要生成的文件名称。

运行之后会发现多出了一个 bundle.js 文件,用 node 运行该文件测试一下:

$ node bundle.js
This is a javascript file for test.    // 输出了正确的结果

可以新建一个 html 文件,引入 bundle.js:

1
2
3
4
5
6
7
8
9
10
// index.html
<!DOCTYPE html>
<html>
<head>
<title>react webpack boilerplate</title>
</head>
<body>
<script src="./bundle.js"></script>
</body>
</html>

打开 index.html,你可以在浏览器的控制台中看到输出结果。

webpack 的配置文件

我们可以在命令行下完成各种复杂的 webpack 操作,不过更方便的方法是利用配置文件。

新建 webpack.config.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 一个简单的 webpack.config.js 文件
var path = require('path');
var webpack = require('webpack');
module.exports = {
// webpack 进行打包的入口文件,这里 webpack 从根目录下的 index.js 开始进行打包
entry: [
'./index'
],
// webpack 打包后的输出文件的路径
output: {
path: path.join(__dirname, 'dist'), // 文件放至当前路径下的 dist 文件夹
filename: 'bundle.js', // 将打包后的输出文件命名为 bundle.js
}
}

html 文件的引用路径相应地改成:

1
<script src="./dist/bundle.js"></script>

再将 webpack 安装在本地:

$ npm install webpack --save-dev

现在你可以运行 webpack 看看效果了。

我们在上面的过程虽然称之为打包,但是我们只不过将一个 index.js 文件变成了 bundle.js 文件,文件的体积甚至还变大了。那看一下如果我们有两个 js 文件是如何处理的。

新建 a.js:

1
2
var a = 1;
module.exports = a;

在 index.js 中加入下列语句:

1
2
var a = require('./a.js');
console.log('a = ', a);

再次用 webpack 命令打包,生成了新的 bundle.js 文件。好了,现在我们不需要用 script 标签分别来引入 index.jsa.js 两个文件了,直接引入 bundle.js 这一个就行了。如果我们有许多引用的资源,经过这样的打包过程,自然就方便多了。

以上的步骤可以在 step1 文件夹内查看。

安装 web-dev-server,并用其开启一个服务器

首先运行如下命令,安装 web-dev-server:

$ npm i webpack-dev-server --save-dev

添加如下的 server.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
// server.js
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config.js');
var server = new WebpackDevServer(webpack(config));
server.listen(3000, 'localhost', function(err, result) {
if (err) {
return console.log(err);
}
return console.log('listening at locahost:3000...');
})
1
var server = new WebpackDevServer(webpack(config));

这一句创建了一个 webpack dev server,这是一个 node.js express 服务器,关于它的用法可以见这里的文档

在 webpack.config.js 的 output 这一项中加上:

1
publicPath: '/static/'

server.js 修改:

1
2
3
var server = new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath
});

好了,现在你可以运行:

$ node server.js

然后打开浏览器进入 http://localhost:3000 查看效果了(当然,现在除了在控制台输出了信息之外什么都没干)

package.json 文件内加上这么一项:

1
2
3
"scripts": {
"start": "node server.js"
},

你可以用 npm start 来代替上面的 node server.js 了,这两者是等价的。

上述步骤可以到 step2 文件夹中查看,运行下列命令:

$ npm i
$ npm start

webpack 的参考资料:
https://webpack.github.io/docs/tutorials/getting-started/
https://medium.com/@rajaraodv/webpack-the-confusing-parts-58712f8fcad9#.81jvwhvmo
http://www.cnblogs.com/skylar/p/webpack-module-bundler.html

关于 Babel

Babel 是什么?

因为在 React 的项目中会用到 ES6(即ES2015) 的语法,而 ES6 还没有得到广泛的支持,所以我们需要借用 Babel 这个工具将我们使用了 ES6 语法的代码转换成 ES5 的语法,从而可以在更广泛的环境下运行。

Babel 的官方网站

怎么安装 Babel?

在我们的项目中,需要安装这样的几个 package:

$ npm install --save-dev babel-loader babel-core

在 Babel 6.x 之前的版本中,Babel 会执行一些默认的转换规则,而在 6.x 之后的版本,你需要显示地告诉 babel 你想执行什么转换。最简单的方法就是使用 preset,比如说 ES2015 Preset。

$ npm install babel-preset-es2015 --save-dev

Presets are sharable .babelrc configs or simply an array of babel plugins.

我们这里还需要另一个名为 stage-0 的 Preset:

$ npm install babel-preset-es2015 --save-dev

参考文档:
Babel 的安装
Presets

现在只要添加一个 .babelrc 文件即可,babelrc 是 Babel 的配置文件,你可以在这个页面找到你可以加入文件中的各种选项。

这里只需在 .babelrc 中写入如下内容即可:

{
  "presets": ["es2015", "stage-0"]
}

将 webpack.config.js 文件做如下的修改:

1
2
3
4
5
6
7
8
9
10
module: {
loaders: [{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, 'src')
}]
},
devServer: {
stats: 'errors-only'
}

这样就可以在我们的项目使用 babel 来进行转码了。

这一步可在 step 3 中查看。

安装 React

既然你在看这篇文章,相信你已经对 React 有了一定的了解,React 是做什么的就不用再多介绍了。

安装 React 的 package 十分简单:

$ npm install react react-dom --save

为了能够让 babel 对 react 进行处理,再安装一个 babel-preset-react

$ npm install babel-preset-react

同时在 .babelrc 文件中也加入这一项:

"presets": ["es2015", "stage-0", "react"]

新建一个基本的 React 组件试试看:

首先在 index.html 中加上这样的一个元素:

1
<div id="app"></div>

React 元素将在上面渲染。

在 src 文件夹下新建两个文件 App.js index.js

1
2
3
4
5
6
7
8
9
./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(
<App />,
document.getElementById('app')
);
1
2
3
4
5
6
7
8
9
10
11
12
./src/App.js
import React, { Component } from 'react';
export default class App extends Component {
render() {
console.log('%c%s', 'font-size:20px;color:red', 'Something happened.');
return (
<div>This is a react boilerplate project with webpack and es6.</div>
);
};
}

现在你可以 npm start 看看效果了。

以上内容可以在 step4 文件夹中查看。

安装 react-hot-loader

其实到上一步为止,我们已经完成了一个完整的 react 模板项目,不过还缺一点,就是当我们在编辑器中修改了文件的时候,需要在浏览器里手动刷新,才能看到结果。我们可以利用一个名为 react-hot-loader 的工具来帮助我们实现浏览器自动刷新的效果。

首先安装 react-hot-loader:

npm install react-hot-loader@^1.3.0 --save-dev

这里加上了版本号是因为默认安装最新的 react-hot-loader v3.0.0-beta 版本,设置会和下面的有所区别。

在 server.js 中加一行:

1
2
3
4
5
var server = new WebpackDevServer(webpack(config), {
stats: config.devServer.stats,
hot: true,
publicPath: config.output.publicPath
});

对 webpack.config.js 做如下修改:

1
2
3
4
5
6
7
8
9
10
11
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/index'
],
loaders: [{
test: /\.js$/,
loaders: ['react-hot', 'babel'],
include: path.join(__dirname, 'src')
}]

并加入一个插件:

1
2
3
plugins: [
new webpack.HotModuleReplacementPlugin()
]

这一步的内容可以查看 step5 文件夹。


到此我们的这个模板项目就完成了。你可以在GitHub上的这个项目中看到完整的项目代码。