本文详细介绍了如何从零开始搭建一个 React 开发的脚手架,包含如何添加 Redux 以及 React Router 的环境。

本文代码地址:react-mobx-starter

建议将代码拉下来之后,配合本文一起查看,效果更佳。

代码下载命令:

1
git clone https://github.com/beichensky/react-mobx-starter.git

一、前情提要

本文的 Demo 分为两个环境,一个是开发环境,另一个是生产环境。

  • 开发环境中讲述的是如何配置出一个更好的、更方便的开发环境;
  • 而生产环境中讲述的是如何配置出一个更优化、更小版本的生产环境。

之前我也就 Webpack 的使用写了几篇文章,本文也是在 Webpack 的基础上进行开发,也是在之前的代码上进行的扩展。

建议:对于 Webpack 还不了解的朋友,可以先看一下 从零开始搭建一个 Webpack 开发环境配置(附 Demo)使用 Webpack 进行生产环境配置(附 Demo) 这两篇文章,可以更好的入手本文。

虽然本文是在之前文章上进行的扩展,但本文还是会详细的介绍每一步的配置。


二、创建项目结构

新建文件夹,命名为:react-mobx-starter

1
mkdir react-mobx-starter

初始化 package.json 文件

1
2
3
4
cd react-mobx-starter

# 直接生成默认的 package.json 文件
npm init -y

创建 src 目录,用来存放我们编写的代码
创建 public 目录,用来存放公共的文件
创建 webpack 目录,用来存放 webpack 配置文件

1
2
3
4
5
mkdir src

mkdir public

mkdir webpack

在 src 目录下
新建 pages 文件夹,用来存放书写的页面组件
新建 components 文件夹,用来存放公共组件
新建 utils 文件夹,用来存放常用的工具类

1
2
3
4
5
6
7
cd src

mkdir pages

mkdir components

mkdir utils

public 目录下新建 index.html 文件
src 目录下新建 index.js 文件
webpack 目录下创建 webpack.config.dev.jswebpack.config.prod.js

  • webpack.config.dev.js 用来编写 webpack 开发环境配置
  • webpack.config.prod.js 用来编写 webpack 生产环境配置

index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React + Mobx 全家桶脚手架</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

index.js

1
2
3
4
5
6
7
8
function createElement() {
const ele = document.createElement('div');
ele.innerHTML = 'Hello, React';
const root = document.querySelector('#root');
root.appendChild(ele);
}

createElement();

webpack.config.dev.jswebpack.config.prod.js 此时还没有书写内容,我们之后会详细的进行讲述。

我们看一下此时的项目结构,之后就可以进行 webpack 的配置了。

1
2
3
4
5
6
7
8
9
10
11
12
react-mobx-starter
├─ public/
└─ index.html
├─ src/
├─ components/
├─ pages/
├─ utils/
└─ index.js
├─ webpack/
├─ webpack.config.dev.js
└─webpack.config.prod.js
├─ package.json

三、React 开发环境配置

在 package.json 文件中添加一个执行脚本,用来执行 webpack 命令:

1
2
3
4
5
6
7
{
...,
"scripts": {
"start": "webpack --config webpack/webpack.config.dev.js"
},
...
}

安装 webpack 相关插件

安装 webpack 和 webpack-cli

1
npm install webpack webpack-cli --save-dev

入口和出口

使用 webpack 进行项目配置的时候,必须要有入口和出口,作为模块引入和项目输出。

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path');

const appSrc = path.resolve(__dirname, '../src');
const appDist = path.resolve(__dirname, '../dist');
const appPublic = path.resolve(__dirname, '../public');
const appIndex = path.resolve(appSrc, 'index.js');

module.exports = {
entry: appIndex,
output: {
filename: 'public/js/[name].[hash:8].js',
path: dist,
publicPath: '/'
}
}

添加 html-webpack-plugin 插件

执行 npm run start 脚本,可以看到 dist/public/js 目录下多了一个 js 文件,但是这个是由 hash 值命名的的,我们每次都手动引入到 index.html 文件里面过于麻烦,所以可以引入 html-webpack-plugin 插件。

html-webpack-plugin 插件有两个作用

  • 可以将 public 目录下的文件夹拷贝到 dist 输出文件夹下
  • 可以自动将 dist 下的 js 文件引入到 html 文件中

安装 html-webpack-plugin 插件

1
npm install html-webpack-plugin --save-dev

使用 html-webpack-plugin 插件

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

const path = require('path');
+ const HTMLWebpackPlugin = require('html-webpack-plugin');

const appSrc = path.resolve(__dirname, '../src');
const appDist = path.resolve(__dirname, '../dist');
const appPublic = path.resolve(__dirname, '../public');
const appIndex = path.resolve(appSrc, 'index.js');
+ const appHtml = path.resolve(appPublic, 'index.html');

module.exports = {
entry: appIndex,
output: {
filename: 'public/js/[name].[hash:8].js',
path: appDist,
publicPath: '/'
},
+ plugins: [
+ new HTMLWebpackPlugin({
+ template: appHtml,
+ filename: 'index.html'
+ })
+ ]
}

设置开发模式

webpack 配置中的 mode 属性,可以设置为 ‘development’ 和 ‘production’,我们目前是进行开发环境配置,所以可以设置为 ‘development’

webpack.config.dev.js

1
2
3
4
5
...
module.exports = {
+ mode: 'development',
...
}

设置 devtool

为了方便在项目出错时,迅速定位到错误位置,可以设置 devtool,生成资源映射,我们这里使用 inline-source-map,更多选择可以在这里查看区别。

webpack.config.dev.js

1
2
3
4
5
6
...
module.exports = {
mode: 'development',
+ devtool: 'inline-source-map',
...
}

使用 webpack-dev-server 启动项目服务

安装 webpack-dev-server

1
npm install webpack-dev-server --save-dev

配置 webpack-dev-server

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
+ devServer: {
+ contentBase: appPublic,
+ hot: true,
+ host: 'localhost',
+ port: 8000,
+ historyApiFallback: true,
+ // 是否将错误展示在浏览器蒙层
+ overlay: true,
+ inline: true,
+ // 打印信息
+ stats: 'errors-only',
+ // 设置代理
+ proxy: {
+ '/api': {
+ changeOrigin: true,
+ target: 'https://easy-mock.com/mock/5c2dc9665cfaa5209116fa40/example',
+ pathRewrite: {
+ '^/api/': '/'
+ }
+ }
+ }
+ },
...
}

修改一下 package.json 文件中的 start 脚本:

1
2
3
4
5
6
7
{
...,
"scripts": {
"start": "webpack-dev-server --config webpack/webpack.config.dev.js"
},
...
}

使用 friendly-errors-webpack-plugin 插件

friendly-errors-webpack-plugin 插件可以在命令行展示更有好的提示功能。

安装 friendly-errors-webpack-plugin

1
npm install friendly-errors-webpack-plugin --save-dev

使用 friendly-errors-webpack-plugin

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
+ const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');

...

module.exports = {
...
plugins: [
new HTMLWebpackPlugin({
template: appHtml,
filename: 'index.html'
}),
+ new FriendlyErrorsWebpackPlugin(),
]
}

启用热加载

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
+ const webpack = require('webpack');

...

module.exports = {
...
plugins: [
...
new FriendlyErrorsWebpackPlugin(),
+ new webpack.HotModuleReplacementPlugin()
]
}

运行项目,测试配置成果

执行 npm run start 命令,命令行提示成功后,在浏览器打开 http://localhost:8000,可以看到 Hello React,说明基本的Webpack 配置已经成功了。

配置 babel

我们现在 index.js 里面的代码量比较少,所以没有问题。但是我如果想在里面使用一些 ES6 的语法或者是还未被标准定义的 JS 特性,那么我们就需要使用 babel 来进行转换了。下面我们来配置一下 babel

安装 babel 相关插件

1
npm install babel-core babel-loader@7 --save-dev

使用 babel-loader

设置 cacheDirectory 属性,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
module.exports = {
...
plugins: [ ... ],
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader?cacheDirectory',
include: [ appSrc ],
exclude: /node_modules/
}
]
}
}

在项目根目录下新建 .babelrc 文件

.babelrc 文件是运行时控制文件,在项目编译的时候会自动读取 .babelrc 文件中的 babel 配置。

使用 babel 相关 presets

安装相关插件:

  • babel-preset-env:可以在项目中使用所有 ECMAScript 标准里的最新特性。
  • babel-preset-stage0:可以在项目中使用社区提出来的但还未被写入 ECMAScript 标准里的特性。
  • babel-preset-react:可以在项目中使用 react 语法。
1
npm install babel-preset-env babel-preset-stage-0 babel-preset-react --save-dev

配置 .babelrc 文件:

1
2
3
4
5
6
7
{
"presets": [
"env",
"stage-0",
"react"
]
}

使用 babel 相关 plugins

安装相关插件:

  • babel-plugin-transform-decorators-legacy:可以在项目中使用装饰器语法。
  • babel-plugin-transform-class-properties:可以在项目中使用新的 class 属性语法。
  • babel-plugin-transform-runtime:使用此插件可以直接使用 babel-runtime 中的代码对 js 文件进行转换,避免代码冗余。
  • babel-runtime:配合 babel-plugin-transform-runtime 插件成对使用
1
npm install  babel-plugin-transform-decorators-legacy babel-plugin-transform-class-properties babel-plugin-transform-runtime babel-runtime --save-dev

配置 .babelrc 文件:

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
"env",
"stage-0",
"react"
],
"plugins": [
"transform-decorators-legacy",
"transform-runtime",
"transform-class-properties"
]
}

这里需要注意 transform-decorators-legacy 插件的放置顺序,最好放在第一位,否则可能会出现某些注解失效的问题。

至此,babel 相关的基本配置完成了。之后我们就可以在项目中肆意使用各种新的 JS 特性了。

添加 css 相关 loader

js 文件相关的 babel-loader 配置好了,但是有时候我们想在项目中为元素添加一些样式,而 webpack 中认为一切都是模块,所以我们这时候也需要别的 loader 来解析一波样式代码了。

安装相关插件:

  • css-loader:处理 css 文件中的 url() 等。
  • style-loader:将 css 插入到页面的 style 标签。
  • less-loader:是将 less 文件编译成 css
  • postcss-loader:可以集成很多插件,用来操作 css。我们这里使用它集成 autoprefixer 来自动添加前缀。
1
npm install css-loader style-loader less less-loader postcss-loader autoprefixer --save-dev

配置样式相关 loader

  • 由于 React 无法直接使用类似 Vuescope 这种局部作用变量,所以我们可以使用 webpack 提供的 CSS Module
    2、由于等会儿会使用 antd,所以引入 antd 时需要开启 lessjavascript 选项,所以要将 less-loader 中的属性 javascriptEnabled 设置为 true

在 webpack.config.dev.js 中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
...
const autoprefixer = require('autoprefixer');

module.exports = {
...,
plugins: [...],
module: {
rules: [
...,
{
test: /\.(css|less)$/,
exclude: /node_modules/,
use: [{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[local].[hash:8]'
}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [autoprefixer()]
}
},
{
loader: 'less-loader',
options: {
javascriptEnabled: true
}
}
]
},
{
test: /\.(css|less)$/,
include: /node_modules/,
use: [{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [autoprefixer()]
}
},
{
loader: 'less-loader',
options: {
javascriptEnabled: true
}
}
]
},
]
}
}

添加其他模块解析 loader 配置

安装相关插件:

1
npm install file-loader csv-loader xml-loader html-loader markdown-loader --save-dev

在 webpack.config.dev.js 中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
...

module.exports = {
...,
plugins: [...],
module: {
rules: [
...,
// 解析图片资源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析数据资源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析数据资源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
'html-loader',
'markdown-loader'
]
}
]
}
}

额外的 webpack 配置优化

添加 resolve allias 属性,设置别名

在项目开发过程中,随着项目越来越大, 文件层级越来越深,引入文件的时候可能会需要一层一层的找路径,就会比较繁琐,我们可以使用 resolve 中的 alias 属性为一些常用的文件夹设置别名

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
··· 

module.exports = {
...,
plugins: [...],
module: {...},
+ resolve: {
+ alias: {
+ src: appSrc,
+ utils: path.resolve(__dirname, '../src/utils'),
+ pages: path.resolve(__dirname, '../src/pages'),
+ components: path.resolve(__dirname, '../src/components')
+ }
+ }
}

添加 resolve.modules 属性,指明第三方模块存放位置

我们知道,一般进行模块搜索时,会从当前目录下的 node_modules 一直搜索到磁盘根目录下的 node_modules。所以为了减少搜索步骤,我们可以设置 resolve.modules 属性强制只从项目的 node_modules 中查找模块。

webpack.config.dev.js

1
2
3
4
5
6
7
8
9
10
11
··· 

module.exports = {
...,
plugins: [...],
module: {...},
resolve: {
...,
+ modules: [path.resolve(__dirname, '../node_modules')],
}
}

安装 React、MObx 以及 React Router 相关插件

1
npm install react react-dom prop-types mobx mobx-react react-router-dom --save

引入 antd

按照 antd 官网的说明,直接在 .babelrc 文件中添加配置,之后即可在项目中正常使用了。

安装 antd 相关插件:

1
npm install antd moment --save

安装 babel-plugin-import 对组件进行按需加载:

1
npm install babel-plugin-import --save-dev

在 .babelrc 文件中添加 antd 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"presets": [
"env",
"stage-0",
"react"
],
"plugins": [
"transform-decorators-legacy",
+ [
+ "import",
+ {
+ "libraryName": "antd",
+ "style": true
+ }
+ ],
"transform-runtime",
"transform-class-properties"
]
}

四、进行 React 开发

基本上需要的插件目前都已经引入了,是时候进行开发了。

修改根目录下的 index.js 文件

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React from 'react';
import ReactDom from 'react-dom';
import { Provider } from 'mobx-react'
import { LocaleProvider } from 'antd';
import { HashRouter } from 'react-router-dom';
import zh_CN from 'antd/lib/locale-provider/zh_CN';
import 'moment/locale/zh-cn';

import GlobalModel from './GlobalModel';
// import App from './App';

const globalModel = new GlobalModel();

const App = () => {
return <div>开发环境配置完成</div>
}

ReactDom.render(
<Provider globalModel={ globalModel }>
<LocaleProvider locale={zh_CN}>
<HashRouter>
<App />
</HashRouter>
</LocaleProvider>
</Provider>,
document.querySelector('#root')
);

运行 npm run start 命令,在浏览器打开 http://localhost:8000/,就能够看到 开发环境配置完成 正常显示。

此时说明我们各种插件、库都已经引入完成,可以正常使用了。

使用 React Route 进行页面间路由跳转

在 src 目录下新建 App.js 文件:

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React from 'react';
import { Switch, Route } from 'react-router-dom';

import Home from 'pages/home';
import Settings from 'pages/settings';
import Display from 'pages/display';
import NotFound from 'pages/exception'

import styles from './App.less';


export default (props) => {
return (
<div className={ styles.app }>
<Switch>
<Route path='/settings' component={ Settings } />
<Route path='/display' component={ Display } />
<Route exact path='/' component={ Home } />
<Route component={ NotFound } />
</Switch>
</div>
)
}

src 目录下创建 App.less 文件,编写 App 组件样式

App.less

1
2
3
.app {
padding: 60px;
}

在 pages 目录下编写 Home、Settings、Display、NotFound 组件

  • Home 组件是根路由组件,用来跳转到 Setting 界面和 Display 界面
  • Settings 组件演示了如何获取和修改 mobx 的全局 Model
  • Display 组件演示了如何使用 mobx 进行同步和异步的数据处理
  • NotFound 组件在匹配不到正确路由时展示

Home、Settings、Display 相关的代码我就不贴了,占的篇幅较长,大家需要的话可以去我的 Github 上看一下或者下载下来也可以。比较方便。地址:Github

再修改一下 index.js 文件

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React from 'react';
import ReactDom from 'react-dom';
import { Provider } from 'mobx-react'
import { LocaleProvider } from 'antd';
import { HashRouter } from 'react-router-dom';
import zh_CN from 'antd/lib/locale-provider/zh_CN';
import 'moment/locale/zh-cn';

import GlobalModel from './GlobalModel';
import App from './App';

const globalModel = new GlobalModel();

ReactDom.render(
<Provider globalModel={ globalModel }>
<LocaleProvider locale={zh_CN}>
<HashRouter>
<App />
</HashRouter>
</LocaleProvider>
</Provider>,
document.querySelector('#root')
);

可以看到这里有一个 GlobalModel 存放全局通用数据的 Model,里面的逻辑比较简单,我们稍微看一下。

GlobalModel.js

1
2
3
4
5
6
7
8
9
10
11
import { observable, action } from 'mobx';

export default class GlobalModel {
@observable username = '小明';

@action
changeUserName = (name) => {
this.username = name;
}

}

添加 fetch 工具类进行网络请求

由于我们在 Display 组件中需要进行网络请求的异步操作,所以我们这里引入 fetch 进行网络请求。

安装 fetch 相关插件:

1
npm install whatwg-fetch qs --save

编写网络请求工具类

utils 目录下创建 request.js 文件。

utils/request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import 'whatwg-fetch';
import { stringify } from 'qs';

/**
* 使用 Get 方式进行网络请求
* @param {*} url
* @param {*} data
*/
export const get = (url, data) => {
const newUrl = url + '?' + stringify(data) + (stringify(data) === '' ? '' : '&') +'_random=' + Date.now();
return fetch(newUrl, {
cache: 'no-cache',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8'
},
method: 'GET',
})
.then(response => response.json());
}

/**
* 进行 Post 方式进行网络请求
* @param {*} url
* @param {*} data
*/
export const post = (url, data) => {
return fetch(url, {
body: JSON.stringify(data),
cache: 'no-cache',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8'
},
method: 'POST',
})
.then(response => response.json()) // parses response to JSON
}

至此,简单的 React 框架搭建完成了

执行 npm run start,编译成功后,可以看到界面长这样。

Home 界面:

Home 界面
Home 界面

Settings 界面:

Settings 界面
Settings 界面

Display 界面:

Display 界面
Display 界面

NotFound 界面:

NotFound 界面
NotFound 界面


五、进行 React 项目打包配置

先在 package.json 文件中新增一个执行脚本:

1
2
3
4
5
6
7
8
{
...,
"scripts": {
"start": "webpack-dev-server --config webpack/webpack.config.dev.js",
"build": "webpack --config webpack/webpack.config.prod.js"
},
...
}

配置 webpack.config.prod.js 文件

其中大部分的 moduleplugin 还有 resolve 都与开发环境的一致。所以我们就以 webpack.config.dev.js 文件中的配置为基础进行说明。

  • 将 mode 属性值修改为:’production’
  • 将 devtool 属性值修改为:’hidden-source-map’
  • 删除 devServer 属性所有的配置。
  • 删除使用的热加载插件:·webpack.HotModuleReplacementPlugin`,

添加 optimization 属性进行代码压缩

安装相关插件:

1
npm install uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin --save-dev

添加代码压缩配置:

webpack.config.prod.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
...;
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
...;

module.exports = {
mode: 'production',
devtool: 'hidden-source-map',
entry: ...,
output: {...},
plugins: [...],
module: {...},
optimization: {
// 打包压缩js/css文件
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
// 在UglifyJs删除没有用到的代码时不输出警告
warnings: false,
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
},
output: {
// 最紧凑的输出
beautify: false,
// 删除所有的注释
comments: false,
}
}
}),
new OptimizeCSSAssetsPlugin({})
],
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.(css|less)/,
chunks: 'all',
enforce: true,
reuseExistingChunk: true // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
},
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2,
reuseExistingChunk: true
},
vendors: {
name: 'vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true
}
}
},
runtimeChunk: true
},
resolve: {...}
}

使用 mini-css-extract-plugin 插件提取 CSS 代码

安装相关插件:

1
npm install mini-css-extract-plugin --save-dev

配置 mini-css-extract-plugin 插件:

  • plugins 属性中引入
  • modulerules 中使用的 style-loader 替换为 MiniCssExtractPlugin.loader

webpack.config.prod.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
...,
plugins: [
...,
new MiniCssExtractPlugin({
filename: 'public/styles/[name].[contenthash:8].css',
chunkFilename: 'public/styles/[name].[contenthash:8].chunk.css'
})
],
modules: {
rules: [
...,
{
test: /\.(css|less)$/,
exclude: /node_modules/,
use: [
{
- loader: 'style-loader'
+ loader: MiniCssExtractPlugin.loader
},
...
]
},
{
test: /\.(css|less)$/,
exclude: /node_modules/,
use: [
{
- loader: 'style-loader'
+ loader: MiniCssExtractPlugin.loader
},
...
]
},
...
]
},
...
}

使用 DefinePlugin 插件定义当前为生产环境

webpack.config.prod.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
module.exports = {
...,

plugins: [
...,
new webpack.DefinePlugin({
// 定义 NODE_ENV 环境变量为 production
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
...
}

使用 clean-webpack-plugin 清理 dist 目录

打包的过程中,由于部分文件名使用的是 hash 值,会导致每次文件不同,因而在 dist 中生成一些多余的文件。所以我们可以在每次打包之前清理一下 dist 目录。

安装插件:

1
npm install clean-webpack-plugin --save-dev

使用插件:

webpack.config.prod.js

1
2
3
4
5
6
7
8
9
10
...,

module.exports = {
...,
plugins: [
...,
new CleanWebpackPlugin()
],
...
}

添加其他优化配置

  • 添加 stats 配置过滤打包时出现的一些统计信息。
  • 添加 performance 配置关闭性能提示

webpack.config.prod.js

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
...,
stats: {
modules: false,
children: false,
chunks: false,
chunkModules: false
},
performance: {
hints: false
}
}

进行项目的打包发布

打包项目

运行 npm run build 指令,控制台打包完成之后,根目录下多出了 dist 文件夹。

使用 Nginx 发布项目

我这里是用的是 Nginx 作为服务器,发布在本地。

Nginx 下载地址:http://nginx.org/en/download.html

下载完成之后,解压完成。打开 Nginx 目录,可以找到一个 conf 文件夹,找到其中的 nginx.conf 文件,修改器中的配置:

Nginx 配置
Nginx 配置

将图中标注的 html 更换为 dist

然后我们就可以放心的将打包生成的 dist 文件夹直接放到 Nginx 的安装目录下了。(此时 dist 目录与刚才的 conf 目录应该是同级的)。

启动 Nginx 服务:

1
start nginx

打开浏览器,输入 http://127.0.0.1 或者 http://localhost 即可看到我们的项目已经正常的跑起来了。

Nginx 其他命令:

1
2
3
4
5
6
7
8
# 停止 Nginx 服务
nginx -s stop

# 重启 Nginx 服务
nginx -s reload

# 退出 nginx
nginx -s quit

更多 Ngnix 相关请参考:Nginx官方文档

注意:需要在 Nginx 安装目下执行 nginx 相关命令!


六、使用 webpack-merge 引入webpack 公共配置

观察 webpack.config.dev.jswebpack.config.prod.js 文件,可以发现有大量的代码和配置出现了重复。所以我们可以编写一个 webpack.common.js 文件,将共有的配置放入其中,然后使用 webpack-merge 插件分别引入到 webpack.config.dev.jswebpack.config.prod.js 文件中使用。

插件安装:

1
npm install webpack-merge --save-dev

使用:

1
2
3
4
5
6
7
+ const merge = require('webpack-merge');
+ const common = require('./webpack.common.js');

+ module.exports = merge(common, {
+ mode: 'production',
+ ...
+ });

这里就展示了一下用法,由于篇幅太长,三个文件中具体的配置代码我就不贴了,
大家可以到 我的 GitHub 上查看一下使用 webpack-merge 后的配置文件。


七、本文源码地址

react-mobx-starter

欢迎Star,谢谢各位!

文章及代码中如有问题,欢迎指正,谢谢!


八、参考文档:

如何在react项目中配置ESlint

webpack 官网

从零开始搭建一个 Webpack 开发环境配置(附 Demo)

使用 Webpack 进行生产环境配置(附 Demo)