写在前面

我发现网上的很多新手教程都并不是完全针对新手的,新手在使用起来也是丈二的和尚摸不到头脑,最近想用node做后端,react做前端搭建一个小系统,想把过程记录下来,从头开始搭建脚手架,绝对适合新手(本人也就是个前端小白)。既然是针对新手,那么就除去那些复杂的内容,什么服务端渲染之类的全部不考虑,使用的也绝对都是主流:Node+Express做后端,数据库采用MongoDB,前端用React+React-Router,然后使用Redux做状态管理,再搭配一个UI框架antd。其他的想用直接在这基础之上添加就可以了,新手参照下面步骤完全可以自己搭建出来项目骨架以及通过文章掌握一些知识点。 下面是脚手架截图:

首页:

用户列表页:我还丧心病狂的为你们配置了404页(情不自禁给自己点赞):虽然只有三个页面,但是麻雀虽小五脏俱全哦:包括前后端路由的配置、数据库的链接,数据的获取、react和redux的使用等等,老铁们,说它是react全家桶不过分吧。

项目地址请点此处,喜欢的小伙伴可以star哦!

第一步 create-react-app

Facebook官方出的脚手架,基本配置完全够用,初始化创建项目就不多BB了,你可以自己去看官网。这里只讲一句,因为要配置antd按需加载,可以按照antd官网一步步安装,不过我在按照官网安装的时候遇到了一些问题,最后还是按照自己的安装来吧。
首先,安装依赖项:

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的
复制代码

其次,进行配置:

  • 修改package.json文件,将启动方式变为rewired启动

     "start": "react-app-rewired start","build": "react-app-rewired build",
    复制代码
  • 在根目录添加config-overrides.js文件,配置antd按需加载
    /* 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了。

第二步 配置router

// 首先,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();
复制代码

第三步 node + express

接下来,就是在项目里添加后端,express。

  • 在根目录下新建文件夹server,然后新建package.json,内容如下:

        {"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

  • server文件夹下新建server.js文件,内容如下:
      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底层httpapp.listen(3003, () => {console.log('node server is listening 3003');});
    复制代码

这里暂时没有抽离路由部分,只是测试。

第四步,前后端测试

  • 分别运行后端和前端代码

    // 后端运行
    cd server && yarn start
    // 前端运行
    yarn start
    复制代码
  • 在浏览器访问http://localhost:3003/api/user/list

看到上图说明后端运行正常。

  • 前端增加页面UserList,从后端获取数据渲染在组件里

      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哈哈~。

    这里使用 concurrently来帮我们同时执行两条命令。

    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就可以同时启动前端和后端了。

  • 解决跨域

    第一种 create-react-app proxy属性(推荐)

    只需在package.json增加下面这一条代码,即可实现跨域获取数据,本项目前端是3000端口,后端是3003端口。配置如下:

    "proxy": "http://127.0.0.1:3003"

    第二种 node端解决跨域
    //allow custom header and CORSapp.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文件夹

    • config.js

      // 数据库地址: 'mongodb://用户名:密码@ip地址:端口号/数据库';
      // 一般如果没设置用户名和密码直接写IP地址就可以,数据库你可以新建一个
      module.exports = {mongodb : 'mongodb://127.0.0.1:27017/luffy_blog'
      };
      复制代码
    • mongoose.js
      // 用于连接数据库并且定义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.jsconst mongoose = require('mongoose');const Schema = mongoose.Schema;//创建Schemaconst UserSchema = new Schema({name: String,password: String,email: String});module.exports = UserSchema;
    复制代码

    models文件夹

      // UserModel.jsconst 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项目查看。

第五步 增加redux进行状态管理

重点来了,重点来了,重点来了(重要的事情说三遍)。提到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。

    • 配置store
    // configureStore.jsimport { 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,接下来就配置这两个。

    • 配置reducer和middleware

      /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 快速通道

新手搭建简洁的Express-React-Redux脚手架相关推荐

  1. React+Redux开发实录(一)搭建工程脚手架

    React+Redux开发实录(一)搭建工程脚手架 React+Redux开发实录(二)React技术栈一览 搭建工程脚手架 准备工作 安装node 安装git 安装一款前端IDE 推荐VSCode, ...

  2. react+redux+generation-modation脚手架搭建一个todolist

    TodoList 1. 编写actions.js 2. 分析state 试着拆分成多个reducer 3. 了解store 4. 了解redux数据流生命周期 5. 分析容器组件和展示组件 搞清楚,数 ...

  3. React + Redux + Express + Mongodb 零基础开发完整大型商城网站视频教程(97 个视频)

    React + Redux + Express + Mongodb 零基础开发完整大型商城网站视频教程(97 个视频) mern 架构零基础开发完整电商网站 React + Redux + Expre ...

  4. 利用脚手架工具搭建一个新的react项目

    利用脚手架工具搭建一个新的react项目 一,工程架构 1.使用的是create-react-app脚手架工具搭建的工程架构 npm install create-react-app -g全局安装 c ...

  5. react全家桶从0搭建一个完整的react项目(react-router4、redux、redux-saga)

    react全家桶从0到1(最新) 本文从零开始,逐步讲解如何用react全家桶搭建一个完整的react项目.文中针对react.webpack.babel.react-route.redux.redu ...

  6. 如何使用webpack+react+redux从头搭建Todolist应用

    webpack环境配置 应用整体框架设计 代码实现 Container Components Actions Reducers indexjs 测试 总结 一言不和先上demo: https://ms ...

  7. Vue + TypeScript + Element 搭建简洁时尚的博客网站及踩坑记

    前言 本文讲解如何在 Vue 项目中使用 TypeScript 来搭建并开发项目,并在此过程中踩过的坑 . TypeScript 具有类型系统,且是 JavaScript 的超集,TypeScript ...

  8. React+Redux技术栈核心要点解析(上篇)

    感谢作者郭永峰的授权发布. 作者:郭永峰,前端架构师,现用友网络 FED团队负责人,目前主要负责企业级应用前端技术平台建设工作,在前端工程化实现.Node 应用开发.React技术.移动开发等方向有丰 ...

  9. 【React进阶-1】从0搭建一个完整的React项目(入门篇)

    这篇文章带领大家从零开始手动撸一个React项目的基础框架,集成React全家桶.万字长文,请各位有足够的时间时再来阅读和学习. 概述 平时工作中一直在用React提供的脚手架工具搭建React项目, ...

最新文章

  1. 表迁移工具的选型-复制ibd的方法-传输表空间
  2. 原始样式增加标题_版式设计!10个技巧让你设计出好的标题
  3. kali NETCAT NC的使用
  4. 【算法】剑指 Offer 04. 二维数组中的查找 【重刷】
  5. java两种不同单例模式_关于Java里的两种单例模式
  6. 大数据_MapperReduce_与hive的集成_使用hive数据分析工具_关联操作hbase---Hbase工作笔记0025
  7. [python基础]关于中文编码和解码那点事儿
  8. 区块链项目开发区块链应用场景需满足3个
  9. 二分法02:寻找第一个和最后一个的满足条件的位置
  10. android rs232串口协议,RS232串口协议详解-在路上.PDF
  11. 【非长篇大论】X3D - Web3D标准的发展
  12. BZOJ4198: [Noi2015]荷马史诗(哈夫曼树)
  13. 力扣刷题 DAY_82 贪心
  14. phantomjs自动截图生成图片
  15. 软件测试平台的作用以及会包含哪些功能?
  16. 突破某些网站限制只能由微信打开的尴尬场景
  17. Quartz 2.4.0 源码解析
  18. HTML之网页布局与设计技巧
  19. Tensorflow学习之tf.keras(一) tf.keras.layers.Model(另附compile,fit)
  20. sql Server 创建临时表 嵌套循环 添加数据

热门文章

  1. 今天的工作发现了4年前的“bug一枚”
  2. zabbix snmp trap 监控
  3. WCF 4.0 REST Service JSON跨域调用
  4. 掌握管理Linux磁盘和分区的方法 创建并挂载文件系统以及 创建并管理LVM
  5. 四大中三家已面向客户推出机器人业务解决方案?别逗了,先用机器人自我革命吧! post by 上海嘉冰信息技术...
  6. 概要设计阶段--组装测试计划
  7. jpa多条件查询重写Specification的toPredicate方法(转)
  8. J2ME下漫游(追逐)AI的实现
  9. 20145234黄斐《Java程序设计》第十周
  10. [转] C#中绘制矢量图形