起因背景:
先做一个全栈的项目,实现一个自己喜欢的个人主页
技术选型:
前端:
- react 技术栈
- react-dom
- hooks
- react-router-dom
- babel(也可归到后端,主要使用babel进行es6到es5的转换)
- axios
后端:
- node.js 技术栈
- express 用于服务器搭建
- babel(转译es6的语法)
- cros 跨域问题
- mongodb (数据库)
- mongoose 操作 mongodb数据库
服务器与操作系统
- tx云centos7操作系统(具有公网ip)
- nginx (静态资源管理,接入外网请求反向代理)
本期需求:
实现用户登录验证测试并进行页面跳转。
前端:
- 用户登录基本模态框
- 用户名输入
- 密码输入
- 点击提交按钮
- 用户跳转路由配置
- 验证成功后跳转页面
后端:
- express 服务器搭建
- 服务器及端口配置
- 基本接口配置
- 数据库
- 后端测试数据库搭建插入测试数据
- 数据库模型建立
服务器:
- 搭建后台服务器
- nginx 配置暴露外网IP和端口
- 配置nodejs的反向代理
版本管理
- 码云(git)
前端页面
1.编写思路:
类似于creat react-app 这样的操作就不说了,主要思路就是遵循react的思想,组件化。
目录构建如下:
- Login组件就是一个登录模态框,它的子组件有:
- FormInput 输入框
- Button 按钮组件
实现效果如下:
这样分解组件的主要考虑的是,可以多编写一个可复用的公共样式,另外也提供了一些可选择的属性:
// FormInput.js
export default function FormInput(props) {
// input type 属性:
const [inputMust,setInputMust] = useState('');
// 控制必填属性
function handleInputMust() {
// console.log(props.inputMust);
if (props.inputMust) {
setInputMust('(必填)');
}
};
// 进行渲染
useEffect(()=>{
handleInputMust();
})
return (
<div className="com-input">
<span>{props.inputName}</span>
<input type={props.inputType} onChange={props.getInputValue}></input>
<span>{inputMust}</span>
</div>
)
}
对于 Button组件:
export default function Button(props) {
const [buttonName,setButtonName] = useState('');
// const [buttonEvent,setButtonEvent] = useState(()=>{});
function handleButtonProps() {
setButtonName(props.buttonName);
// setButtonEvent(props.buttonEvent);
}
return (
<button onClick={props.buttonClick}>{props.buttonName}</button>
)
}
在Login.js中可以定制一些自己需要的属性:
export default function Login(props) {
// const [buttonEvent,setButtonEvent] = useState(buttonEventHandle);
const [inputNameVal,setInputNameVal] = useState('');
const [inputPassVal,setInputPassVal] = useState('');
function buttonEventHandle() {
let that = this;
const url = 'http://82.156.***.***/db'; // ip打码
axios.post(url,{
'id':inputNameVal !== ''?inputNameVal:'NONE',
'pass':inputPassVal !== ''?inputPassVal:'NONE'
}).then(res=>{
// 登录成功后进行跳转:
if (res.data['res']) {
props.history.push('/home');
}
});
}
function getInputName(val) {
console.log(val.target.value);
setInputNameVal(val.target.value);
}
function getInputPass(val) {
console.log(val.target.value);
setInputPassVal(val.target.value);
}
return (
<div>
{/* login模态框 */}
<div className="login">
<FormInput getInputValue={getInputName} inputType="text" inputName="工号/用户名:" inputMust="true"></FormInput>
<FormInput getInputValue={getInputPass} inputType="password" inputName="密码:" inputMust="true"></FormInput>
<Button buttonName="登录" buttonClick={buttonEventHandle}></Button>
</div>
</div>
)
}
即可实现上面图片的效果。
2.父子组件通信
2.1 父传子:
这个模态框的实现中,存在很多父传子的通信,比如:
Login父组件为了设定一些自己想要的属性,向子组件FormInput 和 Button 传递了一些数据,而在两个子组件中,分别进行接受使用:
Button:
FormInput:
2.2 子传父:
在react中,子传父数据通信的思路一般是:
在子组件中调用父组件的方法,并用此方法影响父组件中的数据。
个人理解,还是建立在父传子技术的基础之上,毕竟还是需要向子组件传递一个函数的。
目前这个项目中涉及到的子传父主要是 FormInput和Login之间的来往:
Login
FormInput
3.使用REACT HOOK
3.1 初识HOOK:
真正接触HOOK之后,才发现HOOK的便捷之处。
在目前的项目中,我主要是为了替代类组件中的state 和 setState (在接触react之前接触了微信小程序,发现这个写法和小程序的data 和 setData一样)。
3.2 管理函数组件中数据的状态:
Button中控制是否为必填选项
FormInput中控制input输入值
4. 使用react-router-dom:
4.1 仅用react对路由进行管理:
不再利用后端的express路由跳转html静态页面的方式进行跳转,而是通过加载不同的组件达到加载不同页面的目的。
4.2 在index.js中开启路由:
4.3 在APP.js中进行全局配置:
目前配置了两个路由:
/:入口页面,跳转到登录页面
/home:登录验证成功后跳转页面,目前只是给一个跳转显示验证是否成功。
5.使用axios对后端接口发起请求:
点击了登录模态框中的登录按钮后,会向对应接口发起post请求,验证登录的信息。
5.1 添加按钮点击事件:
Login.js
Button.js
5.2 点击事件函数:
- 利用axios的post接口对后端接口发起请求;
- 异步接受返回数据;
- 根据返回结果判断是否进行跳转;
- 存在跨域问题,在后端中利用cors方法进行解决;
后端服务:
1. 语言及技术栈:
- 使用nodejs作为后端开发语言;
- express搭建服务;
- mongodb作为后端;
- cors 解决跨域问题;
2. 使用express搭建服务:
记录几个坑:
2.1 托管静态库:
第一次用express从0搭建后端服务,没有注意这个细节,导致一直报404:
2.2 express中路径需要写绝对路径:
2.3 express中配置静态页面入口页应该与nginx的配置保持一致:
即:在根路径下进行访问即可.
这个问题对于新手真的很折磨很难发现。
2.4 nginx 配置express反向代理:
如上图所示。
3.mongoose踩坑记录:
一个model不能重复定义,应该封装成一个模块引入: