我发现网上的不少新手教程都并非彻底针对新手的,新手在使用起来也是丈二的和尚摸不到头脑,最近想用node作后端,react作前端搭建一个小系统,想把过程记录下来,从头开始搭建脚手架,绝对适合新手(本人也就是个前端小白)。既然是针对新手,那么就除去那些复杂的内容,什么服务端渲染之类的所有不考虑,使用的也绝对都是主流:Node+Express作后端,数据库采用MongoDB,前端用React+React-Router,而后使用Redux作状态管理,再搭配一个UI框架antd。其余的想用直接在这基础之上添加就能够了,新手参照下面步骤彻底能够本身搭建出来项目骨架以及经过文章掌握一些知识点。 下面是脚手架截图:html
首页: 前端
项目地址请点此处,喜欢的小伙伴能够star哦!vue
Facebook官方出的脚手架,基本配置彻底够用,初始化建立项目就很少BB了,你能够本身去看官网。这里只讲一句,由于要配置antd按需加载,能够按照antd官网一步步安装,不过我在按照官网安装的时候遇到了一些问题,最后仍是按照本身的安装来吧。
首先,安装依赖项:node
yarn add react-app-rewired react-app-rewire-less antd babel-plugin-import
// react-app-rewired 是用来修改create-react-app的默认配置的
// babel-plugin-import 按需加载antd组件必须的插件
// react-app-wire-less antd是依赖less的
复制代码
其次,进行配置:react
"start": "react-app-rewired start",
"build": "react-app-rewired build",
复制代码
/* config-overrides.js */
const { injectBabelPlugin } = require('react-app-rewired');
const rewireLess = require('react-app-rewire-less');
module.exports = function override(config, env) {
config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config);
config = rewireLess.withLoaderOptions({
modifyVars: { "@primary-color": "#ADFF2F" }, // 能够在这里修改antd的默认配置
})(config, env);
return config;
};
复制代码
至此,就能够在组件里按需引用antd了。ios
// 首先,16以后react-router和react-router-dom安装一个便可
yarn add react-router-dom
// 其次,使用BrowserRouter做为路由,同时须要history配合
yarn add history
// 最后,router的配置
...
import { Router, Switch, Route, Redirect} from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
...
const router = (
<Router history={history}>
<Switch>
<Route exact path="/home" component={App}/> // 首页路由
<Route path="/userList" component={UserList} /> //用户列表页
<Redirect from='' to="/home" />
</Switch>
</Router>
);
ReactDOM.render(router, document.getElementById('root'));
registerServiceWorker();
复制代码
接下来,就是在项目里添加后端,express。git
{
"name": "server",
"version": "1.0.0",
"description": "server config",
"main": "server.js",
"author": "luffy",
"license": "MIT",
"dependencies": {
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"body-parser": "^1.18.2",
"express": "^4.16.3",
"mongoose": "^5.0.16"
},
"scripts": {
"start": "nodemon ./server.js",
"build": "babel ./server.js --out-file server-compiled.js",
"serve": "node server-compiled.js"
}
}
复制代码
这里注意,本来的start命令应该是node,可是为了让后端也达到修改代码自动更新的效果,须要全局安装nodemon,
npm install nodemon -g
。github
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
// 给app配置bodyParser中间件
// 经过以下配置再路由种处理request时,能够直接得到post请求的body部分
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// 注册路由
const router = express.Router();
// 路由中间件
router.use((req, res, next) => {
// 任何路由信息都会执行这里面的语句
console.log('this is a api request!');
// 把它交给下一个中间件,注意中间件的注册顺序是按序执行
next();
})
// 获取用户列表信息的路由
router.get('/user/list', (req, res) => {
const userList = [
{
name: 'luffy',
age: 24,
gender: '男'
},{
name: 'lfy',
age: 23,
gender: '女'
}
];
res.json(userList);
});
// 全部的路由会加上“/api”前缀
app.use('/api', router); //添加router中间件
// express 自动帮咱们建立一个server,封装的node底层http
app.listen(3003, () => {
console.log('node server is listening 3003');
});
复制代码
这里暂时没有抽离路由部分,只是测试。ajax
// 后端运行
cd server && yarn start
// 前端运行
yarn start
复制代码
看到上图说明后端运行正常。mongodb
import React, { Component } from 'react';
import axios from 'axios';
import { Table } from 'antd';
class UserList extends Component {
constructor(props) {
super(props);
this.state = { userList:[] };
}
componentDidMount() {
// 获取用户列表
axios.get('/api/user/list')
.then((res) => {
console.log(res);
this.setState({ userList: res.data })
})
.catch(function (error) {
console.log(error);
});
}
render() {
const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '性别',
dataIndex: 'gender',
key: 'gender',
}];
return (
<div>
<h1 style={{ textAlign: 'center' }}>用户列表页</h1>
<div style={{ width: '50%', margin: '10px auto' }}>
<Table dataSource={this.state.userList} columns={columns} />
</div>
</div>
)
}
}
export default UserList;
复制代码
httpRequest使用的是axios,使用fetch就能够,可是与时俱进,毕竟vue2.0推荐的是axios,并且文档良好,
yarn add axios
.
同时启动,先后端。 上面启动项目须要先启动后端,再启动前端,至少须要开启两个命令行工具,一个工程两个命令行感受很不友好,虽然之前一直这么作,O(∩_∩)O哈哈~。
yarn add concurrently
修改package.json下scripts代码以下:
"scripts": {
"react-start": "react-app-rewired start",
"react-build": "react-app-rewired build",
"start": "concurrently \"react-app-rewired start\" \"cd server && yarn start\"",
"build": "concurrently \"react-app-rewired build\" \"cd server && yarn build\"",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
复制代码
接下来,秩序执行yarn start就能够同时启动前端和后端了。
解决跨域
只需在package.json增长下面这一条代码,便可实现跨域获取数据,本项目前端是3000端口,后端是3003端口。配置以下:
"proxy": "http://127.0.0.1:3003"
//allow custom header and CORS
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200); /让options请求快速返回/
}
else {
next();
}
});
复制代码
最后,须要特别注意的是,项目使用node+express做为后端提供API服务,所以后端并没有任何渲染页面,这跟使用node+express搭建博客系统等有本质区别,因此咱们并无后端的渲染页面,也就是view,所以,全部的路由都须要使用res.json()做为返回而不能使用res.render()做为返回,不然会报错Express Error: No default engine was specified and no extension was provided。
数据库采用MongoDB,所以,node端须要安装mongoose。yarn add mongoose
。
// 下面是关于mongoose的几个概念:
Schema: 一种以文件形式存储的数据库模型骨架,不具有数据库的操做能力
Model: 由Schema发布生成的模型,具备抽象属性和行为的数据库操做对象
Entity: 由Model建立的实体,它的操做也会影响数据库
Schema、Model、Entity的关系请牢记,Schema生成Model,Model创造Entity,Model和Entity均可对数据库操做形成影响,但Model比Entity更具操做性。
复制代码
这里关于MongoDB的安装就很少说了,安装完以后的各类配置直接问度娘就能够了。安装完以后可使用各类可视化工具来查看数据库。这里我安装的是robo,而且建立了一个数据库luffy_blog,没错,后续可能会用这个脚手架搭建一个博客,由于我发现做为一个新手,有些教程确实不是很友好。
如上图,我已经安装好了MongoDB,而且新建了数据库luffy_blog,而且新增了一条用户数据,接下来咱们就使用express配合MongoDB获取数据传递给前端:
server目录下新建db文件夹,用于处理数据库相关
目录结构以下:
- db
- config // MongoDB的配置文件
- models // 数据模型model
- schemas // 模型骨架schema
复制代码
对MongoDB进行配置
config文件夹
// 数据库地址: 'mongodb://用户名:密码@ip地址:端口号/数据库';
// 通常若是没设置用户名和密码直接写IP地址就能够,数据库你能够新建一个
module.exports = {
mongodb : 'mongodb://127.0.0.1:27017/luffy_blog'
};
复制代码
// 用于链接数据库而且定义Schema和Model
const mongoose = require('mongoose');
const config = require('./config');
module.exports = ()=>{
mongoose.connect(config.mongodb);//链接mongodb数据库
// 实例化链接对象
var db = mongoose.connection;
db.on('error', console.error.bind(console, '链接错误:'));
db.once('open', (callback) => {
console.log('MongoDB链接成功!!');
});
return db;
};
复制代码
上面就完成了链接数据库的操做,接下来在server.js添加以下代码便可:// 链接mongodb数据库
const mongoose = require('./db/config/mongoose');
const db = mongoose();
复制代码
接下来就是建立数据骨架和模型,彻底按照mongoose的模板来就能够,接下来就以用户模型user为例。
schemas文件夹
// UserSchema.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//建立Schema
const UserSchema = new Schema({
name: String,
password: String,
email: String
});
module.exports = UserSchema;
复制代码
models文件夹
// UserModel.js
const mongoose = require('mongoose');
const UserSchema = require('../schemas/UserSchema');
//建立model,这个地方的user对应mongodb数据库中users的conllection。
const User = mongoose.model('user',UserSchema);
module.exports = User;
复制代码
万事俱备,只欠东风。数据库咱们有了,数据咱们也有了,express对数据库的链接也已经完成了,接下来只剩下将数据从数据库取出以API形式返给前端。例如:咱们将接口定义为以下形式:
接口名称: /api/user/list
后端路由:
const express = require('express');
const User = require('../db/models/UserModel');// 引入模型
const router = express.Router();
router.get('/list', (req, res) => {
User.find({}, (err, data) => {
if (err)
next(err);
res.json(data);
});
});
浏览器访问:
浏览器能够登陆http://localhost:3003/api/user/list访问数据
前端页面获取:
axios.get('/api/user/list')
.then(res => {
return res.data;
})
.catch((error) => {
console.log(error);
});
复制代码
最后咱们将数据渲染到页面上,效果以下:
以上,数据库部分引入完成,详细代码能够clone项目查看。
重点来了,重点来了,重点来了(重要的事情说三遍)。提到react的项目,怎么可能不使用redux来进行状态管理呢。固然,如今的生态圈可能不少人对你说,用mobx吧,更简单通常项目来讲足够了,可是我以为,既然更简单,那么把redux学会了,再去看mobx吧,大家认为呢?
声明一点,这里不是讲解什么是redux,这里是讲解怎么在项目里使用redux,详细的讲解文章能够去看网上教程,入门的话推荐阮大神的,redux入门系列点此前往。
接下来,我就当你已经了解redux是干什么的了,只不过怎么引入到项目里不是很清楚,由于网上的文章要不就是太深,深到还没读完第一段你就放弃了;要不就是太浅,永远都是计数器实例,看完我也不清楚怎么在项目里进行状态管理。若是是上面那样,恭喜你,总算遇到我了,下面绝对会让你学会如何使用redux。
安装redux相关依赖
yarn add redux react-redux redux-logger redux-thunk
总所周知(我就当你知道),redux依赖各类中间件,咱们这里为了简易起见只使用redux-logger和redux-thunk,一个是输出redux日志,另外一个是让咱们方便进行异步操做。更具体的,去看官方文档和各类教程。
前端增长redux目录
// 目录结构,ok就是redux三剑客
- redux
- actions
- reducers
- store
- middleware
复制代码
完成各类基础配置
接下来,你只需按照下面几步,就能够在项目里引入redux。
// configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { logger } from '../middleware';
import rootReducer from '../reducers';
const nextReducer = require('../reducers');
function configure(initialState) {
const create = window.devToolsExtension
? window.devToolsExtension()(createStore)
: createStore;
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware,
logger,
)(create);
const store = createStoreWithMiddleware(rootReducer, initialState);
if (module.hot) {
module.hot.accept('../reducers', () => {
store.replaceReducer(nextReducer);
});
}
return store;
}
export default configure;
复制代码
配置好上面文件以后,确定是一堆报错,不要紧,咱们一点点来。上面用到了logger中间件以及reducer,接下来就配置这两个。
/middleware/index.js
// 你没有看错,中间件就是人家已经造好的轮子,咱们直接拿来用就行。
import logger from 'redux-logger';
export {
logger,
};
/reducers/index.js
import { combineReducers } from 'redux';
import user from './user/index'; // 通常会配置多个reducer而后使用combineReducers将他们合并起来
const rootReducer = combineReducers({
user
});
export default rootReducer;
复制代码
配置好logger的效果以下:
配置action
说是配置action,其实action并非配置得来的,而是咱们将整个应用的状态都交给了redux来进行管理,因此咱们若是想进行数据的更新,就必须经过redux来进行,redux为咱们提供的更新数据的方式就是dispatch action。下面就以获取用户列表数据为例,真真切切的使用redux。
/actions/User.js
import {
FETCH_ALL_USER_LIST,
FETCH_ALL_USER_LIST_SUCCESS,
FETCH_ALL_USER_LIST_FAIL
} from '../../constants/ActionTypes';
import axios from 'axios';
// 获取用户列表
const getAllUserList = () => ({
type: FETCH_ALL_USER_LIST,
});
const getAllUserListSuccess = (payload) => ({
type: FETCH_ALL_USER_LIST_SUCCESS,
payload
});
const getAllUserListFail = () => ({
type: FETCH_ALL_USER_LIST_FAIL
});
export const fetchAllUserList = () => (dispatch) => {
dispatch(getAllUserList());
// 获取用户列表
// 由于设置了proxy的缘故,因此不须要写http://localhost:3003
// 会自动定向到后端服务器
return axios.get('/api/user/list')
.then(res => {
return dispatch(getAllUserListSuccess(res.data));
})
.catch((error) => {
console.log(error);
dispatch(getAllUserListFail());
});
};
复制代码
上面就是一个完整的触发action获取数据的过程,通常包括请求数据,请求数据成功和请求数据失败三个阶段。
将组件分为容器组件containers和展现组件components
同理,这二者区别仍是去看大牛们的讲解,他们讲的很细致,我这里只讲一点,既然引入了redux,那么数据确定不是在页面里componentDidMount()经过ajax获取到的,上面提到了,是经过action触发的,所以须要状态组件将页面所需的state和数据操做API传给展现组件。
// /containers/UserList.js容器组件
import { connect } from 'react-redux';
import UserList from '../components/UserList';
import {
fetchAllUserList
} from '../redux/actions/User';
const mapStateToProps = state => ({
list: state.user.userList.list,
});
const mapDispatchToProps = dispatch => ({
fetchAllUserList() {
dispatch(fetchAllUserList());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserList);
// /components/UserList.js 展现组件
import React, { Component } from 'react';
import { Table } from 'antd';
class UserList extends Component {
constructor(props) {
super(props);
this.state = { userList: this.props.list };
}
componentDidMount() {
this.props.fetchAllUserList(); //获取数据渲染页面
}
...
}
export default UserList;
复制代码
总算是写完我的的第一篇纯技术性文章了,虽然没什么技术性,可是我以为仍是颇有意义的,至少我以为我写的东西应该会让新手或者在校生理解吧,但愿你们多多指正!最后的最后,放上代码,小伙伴若是喜欢不要吝惜大家的star! 接下来可能会用这个脚手架作一些系统之类的练练手,主要目的也是加强能力。
GitHub:https://github.com/luffyZh/express-react-scaffold.git 快速通道