前端:React
1.新建项目
2.组件Book.js Home.js User.js
3.路由模块化 router.js
cnpm install react-router-dom --save (安装)
要在根组件里引入这个,才可以用路由。
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
router.js
import User from '../components/User';
import Home from '../components/Home';
import User from '../components/User';
let routers = [
{
path: "/",
component: Home,
exact:true,
},
{
path: "/user",
component: User,
},
{
path: "/book",
component: Book
},
];
export default routers;
在根组件引入router.js,之后根组件里这样写。
<Router>
<div>
<header className='title'>
<Link to='/'>首页组件</Link>
<Link to='/user'>用户首页</Link>
<Link to='/book'>书籍组件</Link>
</header>
{
routers.map((route,key)=>{
if(route.exact){
return <Route key={key} exact path={route.path}
component={route.component} routes={route.routes}
/>
}else{
return <Route key={key} path={route.path}
component={route.component} routes={route.routes}
/>
}
})
}
</div>
</Router>
写到这里,项目应该可以运行,点击根组件的几个链接可以实现跳转。
接下来是引入css以及在Book.js引入html的页面 让页面变得能看。
index.css
.title{
height: 44px;
line-height: 44px;
background: #000;
}
.title a{
color: white;
padding: 6px;
text-decoration: none;
}
/*左右分栏*/
.content{
width: 100%;
height:500px;
display: flex;
}
.content .left{
width: 200px;
height: 500px;
background: #eeee;
border: 1px solid #eeee;
}
.content .right{
flex:1;
height: 500px;
border: 1px solid #eeee;
}
<div className='book'>
<div className='content'>
<div className='left'>
<Link to='/book/'>书籍列表</Link>
<br/> <br/>
<Link to='/book/addBook'> 添加书籍</Link>
</div>
<div className='right'>
{
}
{/* <Route exact path='/user/' component={UserList}/>
<Route path='/user/UserAdd' component={UserAdd}/> */}
</div>
</div>
</div>
接下来就是路由嵌套,
首先新建两个组件 BookList.js 和 AddBook.js
然后就要在router.js 把这两个子路由写进去。记得放进去别放错了,要放在Book的下面。
routes:[ /*嵌套路由*/
{
path: "/book/",
component: BookList
},
{
path: "/book/AddBook",
component: AddBook
},
]
接下来在根组件里写法要注意。因为路由嵌套,要把嵌套的路由的值传给子组件,这样子在组件里点击链接才能实现跳转。
{
// 前面的js跳转还挺简单的
// 然后是路由嵌套,整了个简单的页面,还是不错的
// 实现了路由的模块化,的确挺不错的,整的怪方便的..也挺麻烦的,真的怪麻烦的,父子组件传值也要在这里面吗,要弄清楚逻辑呀
// 这一节比较难,尤其是到了父子组件传值的那一部分
routers.map((route,key)=>{
if(route.exact){
return <Route key={key} exact path={route.path}
render={props=>(
<route.component {...props} routes={route.routes}/>
)}
/>
}else{
return <Route key={key} path={route.path}
// component={route.component} routes={route.routes}
render={props=>(
<route.component {...props} routes={route.routes}/>
)}
/>
}
})
}
什么?看不懂?看不懂就对了。
render={props=>(
<route.component {...props} routes={route.routes}/>
)}
这一部分真的看不懂,反正就cv吧,
!!!!!最后一步了,在子组件里获取父组件的传的值,然后写个循环,把路由弄出来。
<div className='right'>
{
this.props.routes.map((route,key)=>{
return <Route key={key} exact path={route.path} component={route.component}/>
})
}
{/* <Route exact path='/user/' component={UserList}/>
<Route path='/user/UserAdd' component={UserAdd}/> */}
</div>
几个页面都没有问题,拿这个做图书管理系统的demo。
数据库设计(book 表):
后端 :springboot
参考教程:
https://www.bilibili.com/video/BV1PE411i7CV?p=33
https://blog.csdn.net/wogieni/article/details/88778969
搭项目结构,实现查询书籍列表的功能。
注意pom.xml 需要引入整合的依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter:整合-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
这个是整合框架要用到的。
在 pom.xml 还发现一些依赖报红了,可以通过降低它们的版本来避免报红。
BookMapper.xml 中的格式有一定要求
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo05.mapper.BookMapper">
</mapper>
然后在 application.yaml 中也要配置数据库,整合等。
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/bookmanage?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
# 整合mybatis
mybatis:
type-aliases-package: com.example.demo05.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
配置基本完成了,现在来写实体类,Book.java
然后写controllr层,BookController
service层,Bookservice BookserviceImpl
mapper层,BookMapper BookMapper.xml
在运行的时候找不到BookMapper.xml文件,原来是目录的问题。
controller层注解要这样写 @RestController 返回的值才对。
要解决 跨域 在后端项目 BookController.java 文件中 @RestController 后加入
@CrossOrigin(origins = {"http://localhost:3000"},allowCredentials = "true",allowedHeaders = {"X-Custom-Header"}, maxAge = 3600L, methods={RequestMethod.GET,RequestMethod.POST,RequestMethod.HEAD})
接着还是前端:
接下来用前端获取后端的数据,然后渲染出来,
在前端获取数据要一个插件,
https://github.com/axios/axios
因为后台已经允许跨域请求,所以使用axios就可以实现请求。
大概就三步:1.安装 2.引入 3.看文档
cnpm install axios --save
import axios from 'axios';
getData=()=>{
//通过axios获取服务器数据
// Make a request for a user with a given ID
var api='http://localhost:8080/booklist';
axios.get(api)
.then( (response)=> {
// handle success
//用到this,要注意this指向
console.log(response.data);
this.setState({
list:response.data,
})
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
//组件的生命周期函数,在页面刷新的时候触发
componentDidMount(){
this.getData();
}
组件加载完成后就去请求数据,在生命周期函数里调用请求数据的方法。
4.接收数据,把数据渲染出来
定义一个list,在请求数据的函数里获得数据,
this.setState({
list:response.data,
})
把数据渲染出来。设计布局,然后遍历。
<div >
<table className='table'>
<thead>
<tr>
<th>书名</th>
<th>作者</th>
<th>ISBN</th>
<th>价格(元)</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{
this.state.list.map((v,k)=>{
return (
<tr key={k}>
<td>{v.name}</td>
<td>{v.author}</td>
<td>{v.isbn}</td>
<td>{v.price}</td>
<td>
{/* 修改的话需要再写一个页面,然后父子组件传值,传书的id过去,在另一个页面查询书的信息,然后渲染,然后修改。 */}
<Link to={`/book/modifyBook/${v.id}`}>修改</Link>
<button onClick={this.deleteBook.bind(this,v.id)}>删除</button>
</td>
</tr>
)
})
}
</tbody>
</table>
</div>
到这里,展示图书列表暂时没有问题了。
现在写下一个功能,添加图书,
首先点一个链接(路由)跳到添加图书组件。
<Link to='/book/addBook'> 添加书籍</Link>
在AddBook组件里,
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation
} from "react-router-dom";
import axios from 'axios';
class addBook extends React.Component {
constructor(props) {
super(props);
this.state = {
name:'',
author:'',
isbn:'',
price:'',
AddBookFlag:false,
}
}
doAddBook=(e)=>{
// e.preventDefault();
let name = this.state.name;
let author = this.state.author;
let isbn = this.state.isbn;
let price = this.state.price;
console.log(name,author,isbn,price);
this.AddBook(name,author,isbn,price);
//正常情况需要请求接口
if(true)
{
this.setState({
AddBookFlag:true,
})
//登录成功,跳转到首页
}else{
alert('登录失败');
}
}
AddBook=(name,author,isbn,price)=>{
//通过axios获取服务器数据
// Make a request for a user with a given ID
// var api='http://localhost:8080/booklist';
var api='http://localhost:8080/addBook'+'?name='+name+'&author='+author+'&isbn='+isbn+'&price='+price;
axios.get(api)
.then( (response)=> {
// handle success
//用到this,要注意this指向
console.log(response);
this.setState({
AddBookFlag:true,
})
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
componentWillUnmount = () => {
this.setState = (state,callback)=>{
return;
};
}
inputChange1=(event)=>{
this.setState({
name:event.target.value,
})
}
inputChange2=(event)=>{
this.setState({
author:event.target.value,
})
}
inputChange3=(event)=>{
this.setState({
isbn:event.target.value,
})
}
inputChange4=(event)=>{
this.setState({
price:event.target.value,
})
}
render() {
if(this.state.AddBookFlag){
// return <Redirect to={{pathname:'/'}}/>
return <Redirect to='/book'/>
}
return (
<div>
<form onSubmit={this.doAddBook}>
书名:<input type='text' name='name' value={this.state.name} onChange={this.inputChange1}/>
<br/><br/>
作者:<input type='text' name='author' value={this.state.author} onChange={this.inputChange2}/>
<br/><br/>
ISBN:<input type='text' name='isbn' value={this.state.isbn} onChange={this.inputChange3} />
<br/><br/>
价格:<input type='text' name='price' value={this.state.price} onChange={this.inputChange4} />
<br/><br/>
<input type='submit' value='添加' />
</form>
</div>
);
}
}
export default addBook;
大致的逻辑就是获取表单里所有的值,然后调一个接口,把参数全部传过去,最后跳转页面。
axios,前端向后端发送请求,并且带上参数,然后在后端里获取参数。差不多难点就是这些。
发送请求,带上参数。在后端获取。
这个我当初一点头绪都没有,不知道咋带参数,经过尝试后,这样简单的两行代码就可以传递并接收数据,不过我相信还有更好的方法求传参,例如封装等。
var api='http://localhost:8080/addBook'+'?name='+name+'&author='+author+'&isbn='+isbn+'&price='+price;
public int addBook(String name,String author,String isbn,double price)
像修改和删除就是用的不同的方法写的,思路是什么,
删除的话就是拿到书籍的id,然后调用一个方法,方法里调用一个接口,记得要传值(书籍的id)。
这个是写在BookList组件里面的。
<button onClick={this.deleteBook.bind(this,v.id)}>删除</button>
deleteBook(id){
console.log(id);
//通过axios获取服务器数据
// Make a request for a user with a given ID
var api='http://localhost:8080/deleteBook?id='+id;
axios.get(api)
.then( (response)=> {
// handle success
//用到this,要注意this指向
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
this.getData();
}
修改的话有点像添加书籍,都是用的路由,不过他这个是动态路由,要传值(传递书籍的id)
{
path: "/book/modifyBook/:id",
component: ModifyBook
},
<Link to={`/book/modifyBook/${v.id}`}>修改</Link>
跳到ModifyBook组件,在这个组件里拿到书籍的id,再调一个接口,查询到这本书的信息,然后渲染到前端,前端得到数据,就可以在页面上修改,然后再调一个接口,记得传书籍的id,修改书的信息。
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation
} from "react-router-dom";
import axios from 'axios';
class ModifyBook extends React.Component {
constructor(props) {
super(props);
this.state = {
id:'',
name:'',
author:'',
isbn:'',
price:'',
ModifyBookFlag:false,
}
}
getData=(id)=>{
//通过axios获取服务器数据
// Make a request for a user with a given ID
var api='http://localhost:8080/getBook?id='+id;
axios.get(api)
.then( (response)=> {
// handle success
//用到this,要注意this指向
console.log('OKOK');
console.log(response.data);
this.setState({
id:response.data.id,
name:response.data.name,
author:response.data.author,
isbn:response.data.isbn,
price:response.data.price,
})
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
// componentWillUnmount = () => {
// this.setState = (state,callback)=>{
// return;
// };
// }
//组件的生命周期函数,在页面刷新的时候触发
componentDidMount(){
console.log('OK');
console.log(this.props.match.params.id);
let id=this.props.match.params.id;
this.getData(id);
}
doModifyBook=()=>{
console.log('doModifyBook方法');
// 进这个方法,然后提交了表单,很快就被刷新了
let id = this.state.id;
let name = this.state.name;
let author = this.state.author;
let isbn = this.state.isbn;
let price = this.state.price;
console.log(id,name,author,isbn,price);
this.ModifyBook(id,name,author,isbn,price);
this.setState({
ModifyBookFlag:true,
})
}
ModifyBook=(id,name,author,isbn,price)=>{
//通过axios获取服务器数据
// Make a request for a user with a given ID
// var api='http://localhost:8080/booklist';
var api='http://localhost:8080/modifyBook'+'?id='+id+'&name='+name+'&author='+author+'&isbn='+isbn+'&price='+price;
axios.get(api)
.then( (response)=> {
// handle success
//用到this,要注意this指向
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
inputChange1=(event)=>{
this.setState({
name:event.target.value,
})
}
inputChange2=(event)=>{
this.setState({
author:event.target.value,
})
}
inputChange3=(event)=>{
this.setState({
isbn:event.target.value,
})
}
inputChange4=(event)=>{
this.setState({
price:event.target.value,
})
}
render() {
if(this.state.ModifyBookFlag){
// return <Redirect to={{pathname:'/'}}/>
return <Redirect to='/book'/>
}
return (
<div>
<form onSubmit={this.doModifyBook}>
书名:<input type='text' name='name' value={this.state.name} onChange={this.inputChange1}/>
<br/><br/>
作者:<input type='text' name='author' value={this.state.author} onChange={this.inputChange2}/>
<br/><br/>
ISBN:<input type='text' name='isbn' value={this.state.isbn} onChange={this.inputChange3} />
<br/><br/>
价格:<input type='text' name='price' value={this.state.price} onChange={this.inputChange4} />
<br/><br/>
<input type='submit' value='修改' />
</form>
</div>
);
}
}
export default ModifyBook;
目前的主要问题是
前后端没有数据校验,后端没有返回具体的信息,只是调了一个接口,没有返回类似于成功,失败(失败的类型)的信息给前端,然后让前端显示给用户。
第二个问题就是刷新的问题,不管是添加,删除,还是修改,最后都不会刷新书籍列表的数据,这个暂时也不知道咋解决。
代码,在github上。