[Full-Stack]React加Express前后端分离构建简单页面

React作为FaceBook家的前端框架,熟悉了它的语法之后,会觉得它真的挺好用的,Components的写法让构建页面变得更加简单方便。但是前端终究是要和后端通信的,本篇将采用Express作为后端,数据库选用Mysql,打通前后端连接,构建一个简单的页面,学习一下React和Express的基础语法。

技术栈

前端:

  • React 18
  • BootStrap 5
  • axios
  • yup(本文暂未使用)
  • webpack

中间件:

  • cors
  • jwt(本文暂未使用)

后端:

  • Express
  • Sequelize
  • Bcrypt(本文暂未使用)
  • Mysql
  • mysql2

后端

初始化后端环境

后端采用的是Express+Mysql+cors+Sequelize,在后端项目目录内(以server为例)使用npm包管理器安装它们。

srever>_ npm init

srever>_ npm install express mysql2 cors sequelize sequelize-cli nodemon

在服务器目录新建index.js:

const express = require('express');
const app = express();

    //服务器监听端口3001
    app.listen(3001,()=>{
        console.log("server is live in 3001");
    })

 

去package.json文件script中加入(逗号不可省略)"start": "nodemon index.js",
"scripts": {
  "start": "nodemon index.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},

server>_ npm start

此时,控制台应该会正常打印server is live in 3001,代表后端服务已正常开启。

初始化Sequelize驱动

Sequelize是用来驱动Mysql的(mysql2才是用来驱动数据库的,Sequelize是ORM),内置了crud的基本函数,使操作数据库变得更加简单,不必再手写sql语句。

在终端使用 npx sequelize-cli init 初始化驱动环境,它会给你的项目新增几个目录,其中configmodels是我们需要关注的。

server/config-config.json保存的是数据库连接信息,server目录为你的后端项目目录。修改连接信息:

{
  
  "development": {
    "username": "root",
    "password": "12345678",
    "database": "FullStack-React-Express",
    "host": "localhost",
    "dialect": "mysql"
  }

在server/models中新建Post.js文件(文件名随意),以json格式映射数据表字段:

module.exports = (sequelize, DataTypes) => {
  //使用sequelize驱动数据库如果Post表不存在则创建Posts表
  const Posts = sequelize.define("Posts", {
    title: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    postText: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    username: {
      type: DataTypes.STRING,
      allowNull: false,
    },
  });
  return Posts;
};

修改server/index.js:

const express = require('express');
const app = express();
// 导入数据库支持驱动sequelize
const db = require('./models');

db.sequelize.sync().then(()=>{

    //服务器监听端口3001
    app.listen(3001,()=>{
        console.log("server is live in 3001");
    })

})

此时在终端ctrl+C关闭服务器,并npm start重新开启服务器,控制台会显示关于sequelize的信息,在后台运行了sql命令帮我们成功创建了Post表。

且表内包含:

title,postText,username,createdAt,updateAt

前面三个是我们在server/models/Post.js中写的字段,后面两个则是sequelize自动创建的。

添加路由

路由可以简单理解为一个网络入口,如:http://localhost/posts

在服务器新建路由目录且新建路由文件server/routes/Posts.js:

const express = require('express');
const router = express.Router();

// 路由实现
router.get('/',(req,res)=>{
    res.send("Hello World!")
});

router.post('/');

module.exports = router;

并修改server/index.js文件:

const app = express();
// 导入数据库支持驱动sequelize
const db = require('./models')

//Post路由
const postRouter = require('./routes/Posts')
//Post路由入口 http://localhost/posts
app.use("/posts",postRouter)
db.sequelize.sync().then(()=>{

    //服务器监听端口3001
    app.listen(3001,()=>{
        console.log("server is live in 3001")
    })
})

此时,通过访问http://localhost/posts应该能看到显示Hello World!

在此推荐一个网络调试软件Insomnia

使用create函数向数据库插入数据

前面提到Sequelize内置了一些操作sql的函数,create就是其中一个,用于创建数据(即sql的insert)。

修改server/index.js添加json解析:

const express = require('express');
const app = express();
//解析json数据
app.use(express.json());

修改 server/routes/Posts.js

const express = require("express");
const router = express.Router();
const { Posts } = require("../models");

//      get路由实现
router.get("/", (req, res) => {
  res.json("Hello World!");
});
//      post路由实现
router.post("/", async (req, res) => {
  const post = req.body;
  //sequelize的语法插入数据
  await Posts.create(post);
  res.json(post);
});

module.exports = router;

主要是新增了router.post函数以及将Posts表从models中导入。

由于前端页面暂时还没创建好,我们使用Insomnia从后端模拟Post请求创建数据。

下载好Insomnia后,创建Post请求,地址为http://localhost:3001/posts,使用json格式数据向服务器发送请求信息。点我下载Insomnia

可以看到,状态码为200,json数据已经成功发送到服务器,在数据库的Post表中已经有了新数据。

使用findAll函数从数据库查找数据

同理,从数据库查找数据我们需要使用get方法,修改server/routes/Posts.js中的get函数:

router.get("/", async (req, res) => {
  const listAllPost = await Posts.findAll();
  res.json(listAllPost);
});

依然使用Insomnia工具查看数据回显,创建get请求,地址为http://localhost:3001/posts,发起get请求,可以看到,已经成功从数据库中获取到之前创建的数据:

客户端

在客户端目录(以client为例)使用react-create-app脚手架初始化React项目:

client>_ npm install react-create-app

client>_ npx react-create-app .

删除以下文件:

App.test.js,index.css,logo.svg

reportWEbVitals.js,setupTest.js(非必需步骤)

将client/src/App.js内容修改为:

import "./App.css";
function App() {
  return <div className="App">Hello React!</div>;
}

export default App;

client/src/index.js内容修改为:

import React from "react";
import  {createRoot}  from "react-dom/client";
import App from "./App";

// for React 17 : ReactDOM.render(<App />, document.getElementById('root'));
createRoot(document.getElementById("root")).render(<App />);

client/src/App.css内仅保留第一个样式:

.App{
    text-align: center;
}
注意:createRoot为React18的语法,React17的语法为ReactDOM.render

现在在客户端npm start,应该可以看到Hello React!字样。

在客户端安装axios实现跨域连接express

client>_ npm install axios

修改client/src/App.js,添加axios支持:

import "./App.css";
import axios from "axios";
import { useEffect } from "react";

function App() {
  useEffect(()=>{
    axios.get("")
  },[])  
  return <div className="App"></div>;
}

export default App;

在服务器添加cors中间件连接axios(在本文最开始已经安装了cors,安装步骤可省略)

server>_ npm install cors

修改server/index.js添加cors支持:

const express = require('express');
const app = express();
const cors = require('cors');
//解析json数据
app.use(express.json());
//add cors support
app.use(cors());

修改client/src/App.js

function App() {
  useEffect(()=>{
    axios.get("http://localhost:3001/posts").then((response) =>{
        console.log(response);
    })
  },[])  
  return <div className="App"></div>;
}
export default App;

添加useEffect和useState钩子函数前端显示数据库内容

修改client/src/App.js文件:

import "./App.css";
import axios from "axios";
//引入useEffect和useState钩子函数管理状态和副作用
import { useEffect, useState } from "react";

function App() {
  const [listAllPost, setlistAllPost] = useState([]);
  useEffect(() => {
    axios.get("http://localhost:3001/posts").then((response) => {
      setlistAllPost(response.data);
    });
  }, []);
  //使用map映射取值
  return (
    <div className="App">
      {listAllPost.map((value, key) => {
        return (
        <div className="container">
                    <div class="title">{value.title}</div>
                    <div class="text">{value.postText}</div>
                    <div class="username">{value.username}</div>
        </div>
        );
      })}
    </div>
  );
}

export default App;

现在虽然前端能够显示数据库的内容,但是布局不够美观。

添加BootStrap美化页面布局

client>_ npm install bootstrp

修改client/src/index.js导入bootstrap库:

import 'bootstrap/dist/css/bootstrap.min.css';

修改client/src/App.js使用卡片布局:

import "./App.css";
import axios from "axios";
//引入useEffect和useState钩子函数管理状态和副作用
import { useEffect, useState } from "react";

function App() {
  const [listAllPost, setlistAllPost] = useState([]);
  useEffect(() => {
    axios.get("http://localhost:3001/posts").then((response) => {
      setlistAllPost(response.data);
    });
  }, []);
  //使用map映射取值
  return (
    <div className="App">
      {listAllPost.map((value, key) => {
        return (
        <div className="container">
            <div class="card mx-auto" style={{width:'500px'}}>
                <div class="card-body">
                    <h5 class="card-title">{value.title}</h5>
                    <p class="card-text">{value.postText}</p>
                    <h6 class="username">{value.username}</h6>
                </div>
            </div>
        </div>
        );
      })}
    </div>
  );
}

export default App;

至此,你可以得到一个页面布局相对美观的前端页面,并且从后端数据库成功拿到了内容显示在页面上。

版权声明:
作者:小鱼
链接:https://afish.org/index.php/2023/10/27/full-stack_react_express/
来源:小鱼的blog
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录