1.strapi
首先strapi是一个api管理系统,可以让我们直接用网页的形式去定义自己的api,包括设置模型和权限等功能。
首先直接在项目目录里面安装库。npx create-strapi@latest server --quickstart这样就可以直接在项目目录创建一个连接数据库的服务器了。不用我们去书写代码,直接在网页版数据库添加表以及表中的数据就可以自动生成api让我们去访问请求了。
编辑
就是这种,我们网页形式创建表,添加权限,自动生成api。这样我们就可以在前端发送请求去获取数据了。值得一提的是记得npm的版本以及node.js版本会有限制。
可以装一个nvm管理自己开发项目用的node。
我是mac系统所以用brew安装nvm brem install nvm,然后创建nvm工作目录 mkdir ~/.nvm
然后配置终端环境变量,nano ~/.zshrc 然后文件内容
bash
Maestro CLI
export PATH="$HOME/.maestro/bin:$PATH"
OpenJDK 17
export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"
nvm 配置(Homebrew 路径,Apple M 芯片)
export NVM_DIR="$HOME/.nvm"
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && . "/opt/homebrew/opt/nvm/nvm.sh"
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && . "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"
然后control x退出 按y 再按回车保存退出成功。在执行立即生效命令 source ~/.zshrc这样nvm就配置好了。我们用nvm install 22装上 22版本的node.js然后nvm use 22就可以创建strapi项目了。
2.fetch
上面网页可以看到我们路由是/api/students,那么我们就可以尝试去用fetch(浏览器自带的发送请求的api,是浏览器提供的全局函数,返回的一个promise)所以可以用链式调用.then.catch因为promise有状态,请求成功自动执行.then失败会自动调用.catch而且参数就是返回的promise实例对象。
javascript
import React, { useEffect } from 'react'
export default function App() {
const [person, setPerson] = React.useState([])
const [show, setShow] = React.useState(false)
/*
从接口http://localhost:1337/api/students中加载数据
组件一渲染就向服务器发送请求加载数据
组件初始化时候发送请求来加载数据
*/
useEffect(() => {
//说明数据开始加载了
setShow(true)
//发送请求
//fetch()用来向服务器发送请求加载数据,Ajax的升级版
//需要两个参数 1.请求地址2.请求信息
//promise两个特点 成功和失败两个回调函数.then成功时候调用 .catch失败时调用
fetch('http://localhost:1337/api/students/')
.then((res) => {
//response表示响应信息
// console.log(res);
return res.json();//该方法可以将响应的json直接转换为js对象
}).then((data) => {
console.log(data.data);
// 将加载到的数据设置进state中
setPerson(data.data)
setShow(false)
})
.catch(() => { })
}, [])
return (
<div>
{show ? (
<p>正在加载中...</p>
) : (
person.map((item) => (
<div key={item.id}>
{item.name} --- {item.gender} --- {item.age}
</div>
))
)}
</div>
)
}
这里我们没有写fetch发送请求的类型,所以是get。这里展示数据成功,说明访问成功之后把数据返回来了,但是第一次是json类型
编辑
我们需要去转化成对象。比如这里再次使用.then是因为前面的.then把json转化为js对象。然后return返回的也是一个promise实例对象。所以可以.then,报错.catch捕捉。
编辑
这里转化的js对象有两个属性一个是data,一个是meta(不认识),但是我们展开确定我们需要的是data里面的对象编辑
所以用setState去存住返回来的数据就可以去展示到页面上面了。就实现了第一次用fetch请求api响应数据展示的过程了。
但是我们要知道还有一个.catch我们没有去写,比如我们去掉路由students中的s,
编辑
编辑
返回404但是问题是map也就是map方法错了所以请求整个过程还是执行了?这很明显不对。但是我们看到ok变为false,那么我们就可以去截断这个请求过程。而且statusText都变了。
编辑
对比我们就知道如果错了,ok和statueText会改变,那就对这些进行判断。
编辑
比如这里,这样如果错误就不会再执行请求后面的代码了。因为只有成功才会返回promise实例对象,才可以调用后面的.then,而且.catch没有内容所以什么都没有。
javascript
import React, { useEffect } from 'react'
export default function App() {
const [person, setPerson] = React.useState([])
const [show, setShow] = React.useState(false)
const [err, setError] = React.useState(null)
/*
从接口http://localhost:1337/api/students中加载数据
组件一渲染就向服务器发送请求加载数据
组件初始化时候发送请求来加载数据
*/
useEffect(() => {
//说明数据开始加载了
setShow(true)
//发送请求
//fetch()用来向服务器发送请求加载数据,Ajax的升级版
//需要两个参数 1.请求地址2.请求信息
//promise两个特点 成功和失败两个回调函数.then成功时候调用 .satch失败时调用
fetch('http://localhost:1337/api/students/')
.then((res) => {
//判断是否正常返回响应信息
if (res.ok) {
console.log(res);
return res.json()
}
//代码运行到这里 说明没有加载到数据
// setShow(false)
//抛出一个错误
throw new Error('数据加载失败')
// response表示响应信息
// console.log(res);
// return res.json();//该方法可以将响应的json直接转换为js对象
}).then((data) => {
// console.log(data);
// 将加载到的数据设置进state中
setPerson(data.data)
setShow(false)
})
.catch((e) => {
//catch中的回调函数用来统一处理错误
//catch执行说明上面代码出错
setShow(false)
// console.log(e);
console.log('e', e)
setError(e)
})
}, [])
return (
<div>
{show && <p>加载中</p>}
{err && <p>出错了:{err.message}</p>}
{!show && !err && person.map((item) => (
<div key={item.id}>
{item.name} --- {item.gender} --- {item.age}
</div>
))}
</div>
)
}
当报错的时候用throw new Error('')扔出一个err,然后.catch就可以捕捉到。这样就完成了整个请求的逻辑,首先要有一个state接收响应数据,还要有一个state去控制请求发送后如果没有即使响应展示的内容,还需要一个state去控制页面如果报错了展示错误信息,
3.async await语法糖
javascript
import React, { useEffect } from 'react'
export default function App() {
const [person, setPerson] = React.useState([])
const [show, setShow] = React.useState(false)
const [err, setError] = React.useState(null)
useEffect(() => {
const fetData = async () => {
try {
setShow(true)
setError(null)
const res = await fetch('http://localhost:1337/api/students')
if (res.ok) {
const data = await res.json()
setPerson(data.data)
} else {
throw new Error('数据加载失败!')
}
} catch (e) {
setError(e)
} finally {
setShow(false)
}
}
fetData()
}, [])
return (
<div>
{show && <p>加载中</p>}
{err && <p>出错了:{err.message}</p>}
{!show && !err && person.map((item) => (
<div key={item.id}>
{item.name} --- {item.gender} --- {item.age}
</div>
))}
</div>
)
}
最开始我们使用 fetch 是通过连续调用 .then 来一步步获取 response 和 json 数据,因为 fetch 返回的是一个 Promise 实例对象,不能直接取到里面的数据。而使用 await 关键字后,就相当于自动帮我们调用了 .then,把 Promise 解析后的数据直接赋值给变量。比如第一次用 await fetch... 获取响应对象 response,第二次再用 await response.json() 把 JSON 数据解析成 JS 对象。
由于 await 是同步语法风格,我们就不能再用 .catch 来捕获错误,而需要使用 try catch finally 来捕获异常,比如 throw 抛出的错误才能在 catch 中处理。需要注意的是:只有在 async 声明的函数中才能使用 await。
4.删除数据
javascript
import React, { } from 'react';
import StuContext from './store/StuContext';
import StudentForm from './StudentForm';
const Student = (props) => {
const Stx = React.useContext(StuContext)
const [loading, setLoading] = React.useState(false)
const [error, setError] = React.useState(null)
const [isEdit, setIsEdit] = React.useState(null)
const deleteData = React.useCallback(async () => {
try {
setLoading(true)
setError(null)
const res = await fetch(http://localhost:1337/api/students/${props.stu.documentId}, {
method: 'delete'
})
if (!res.ok) {
throw new Error('删除失败')
}
//修改成功后触发列表重新刷新
Stx.fetchData()
// const data = await res.json()
// console.log('data', data)
// 被删除的学生
} catch (e) {
console.log(e);
setError(e)
} finally {
setLoading(false)
}
}, [])
const Delete = () => {
//删除学生
deleteData()
}
const cancel = () => {
setIsEdit(false)
}
return (
<>
{!isEdit &&
<tr>
<td>{props.stu.name}</td>
<td>{props.stu.gender}</td>
<td>{props.stu.age}</td>
<td>{props.stu.address}</td>
<td>
<button onClick={Delete} >删除</button>
</td>
<td>
<button onClick={() => { setIsEdit(true) }} >修改</button>
</td>
</tr>
}
{
isEdit && <StudentForm stu={props.stu} onCancel={cancel} />
}
{
loading && <tr><td colSpan={5}>正在删除数据</td></tr>
}
{
error && <tr><td colSpan={5}>删除失败</td></tr>
}
</>
);
};
export default Student;
这是我们的删除请求,我们只需要在后面的对象中指名请求头为delete就可以去删除了。当然需要一个参数documentId去指名删除的哪一个。id不行在说一边我用的strapi不支持id去指定删除或者更新的表中的列。卡了一天了.............只不过一个点是Context我们用到了,因为我们
编辑
编辑
每次在删除之后需要重新加载一遍数据,保证最新的数据在 页面上。
xml
<StuContext.Provider value={{ fetchData }}>
<div className="app">
<button onClick={() => { loadDataHandler() }}>加载数据</button>
{(!loading && !error) && <StudentList stus={stuData} />}
{loading && <p>数据正在加载中...</p>}
{error && <p>数据加载异常!</p>}
</div>
</StuContext.Provider>
就是这样然后我们在删除请求成功之后,执行一下重新加载的方法就好了。
5.创建以及更新请求
javascript
import React from 'react'
import './StudentForm.css'
import StuContext from './store/StuContext'
export default function StudentForm(props) {
const Stx = React.useContext(StuContext)
const [input, setInput] = React.useState({
name: props.stu ? props.stu.name : '', age: props.stu ? props.stu.age : '',
address: props.stu ? props.stu.address : '', gender: props.stu ? props.stu.gender : '男'
})
const [loading, setLoading] = React.useState(false)
const [err, setErr] = React.useState(null)
const addData = (async (input) => {
try {
setLoading(true)
setErr(null)
const res = await fetch('http://localhost:1337/api/students', {
method: 'post',
body: JSON.stringify({
data: input
}),
headers: {
'Content-Type': 'application/json'
}
})
console.log('res.ok', res.ok)
if (!res.ok) {
throw new Error('添加失败')
}
//添加成功刷新列表
Stx.fetchData()
} catch (e) {
console.log(e);
setErr(e)
} finally {
setLoading(false)
}
})
const updataData = (async (newStu, id) => {
try {
setErr(null)
setLoading(true)
const res = await fetch(http://localhost:1337/api/students/${id}, {
method: 'PUT',
body: JSON.stringify({ data: newStu }),
headers: {
'Content-Type': 'application/json'
}
})
if (!res.ok) {
throw new Error('修改出错')
}
Stx.fetchData()
} catch (e) {
setErr(e)
} finally {
setLoading(false)
}
})
const nameChange = (e) => {
setInput((preState) => ({ ...preState, name: e.target.value }))
}
const ageChange = (e) => {
setInput((preState) => ({ ...preState, age: +e.target.value }))
}
const genderChange = (e) => {
setInput((preState) => ({ ...preState, gender: e.target.value }))
}
const addressChange = (e) => {
setInput((preState) => ({ ...preState, address: e.target.value }))
}
const handle = () => {
addData(input)
}
const updataHandle = () => {
updataData(input, props.stu.documentId)
}
return (
<>
<tr className='student-form'>
<td><input type="text" onChange={nameChange} value={input.name} /></td>
<td><select name="" id="" onChange={genderChange} value={input.gender}>
<option value="男">男</option>
<option value="女">女</option>
</select></td>
<td><input type="text" onChange={ageChange} value={input.age} /></td>
<td><input type="text" onChange={addressChange} value={input.address} /></td>
<td>
{props.stu && <><button onClick={() => { props.onCancel() }}>取消</button><button onClick={updataHandle}>修改</button></>}
{!props.stu && <button onClick={handle}>添加</button>}
</td>
</tr>
{loading && <tr><td colSpan={5}>添加中</td></tr>}
{err && <tr><td colSpan={5}>添加失败</td></tr>}
</>
)
}
唯一不同的是我们在执行创建或者更新请求除了请求头不同以外,创建也就是添加新表元素需要传一个json格式的文本内容,也就是我们需要把js对象转化成json格式。正好对应我们响应体json格式需要转化成js对象我们用。以及指明请求体的格式,headers:{"Content-Type:'application/json'"}说明我们传过去的是json格式。包括更新,毕竟我们是传过去数据而不是get获取数据。这一点要注意。好了现在我们用fetch实现了post 增 delete删 put 改 get查了。虽然不熟练但至少有个思路怎么去数据库拿东西了。至于优化还是慢慢来吧。