1、前端注册功能实现
在service/user.js中增加reg注册函数
1 import axios from "axios" 2 import { observable } from "mobx"; 3 import store from 'store' 4 5 // 过期插件 6 store.addPlugin(require('store/plugins/expire')) 7 8 class UserService { 9 // 定义被观察对象 10 @observable loggedin = false 11 12 //登录 13 login (email, password) { 14 //TODO 15 console.log('----------------') 16 console.log(email) 17 console.log(password) 18 console.log('----------------') 19 20 axios.post('/api/user/login', { 21 email:email, 22 password:password 23 })// 成功执行 24 // 普通函数,用箭头处理this问题 25 .then((response) => { 26 console.log(1,response); 27 console.log(2,response.data) 28 const {token, user} = response.data 29 console.log(3,token) 30 store.set('token',token, (new Date()).getTime() + (8 * 3600 * 1000)) 31 console.log(4,user) 32 //成功后,状态改变 33 this.loggedin = true 34 35 })//错误就不执行 36 .catch( (error) => { 37 console.log(7,error); 38 this.loggedin = false; 39 }); 40 } 41 42 // 注册 43 reg (name, email, password) { 44 //TODO 45 console.log('-------***---------') 46 console.log(email) 47 console.log(password) 48 console.log('-------***---------') 49 50 axios.post('/api/user/reg', { 51 email:email, 52 password:password, 53 name:name 54 })// dev server 会代理 55 // 普通函数,用箭头处理this问题 56 .then( response => { 57 console.log(1,response); 58 console.log(2,response.data) 59 console.log(3,response.status) 60 const {token, user} = response.data 61 console.log(4,token) 62 // 存储token 63 store.set('token',token, (new Date()).getTime() + (8 * 3600 * 1000)) 64 console.log(5,user) 65 //成功后,状态改变 66 console.log('==================') 67 this.loggedin = true 68 console.log('==================') 69 70 })//错误就不执行 71 .catch(error => { 72 console.log(6,error); 73 this.loggedin = false; 74 }); 75 } 76 } 77 78 79 80 //全局变量 81 const userService = new UserService() 82 83 export {userService}
《===等价===》
测试:
component/reg.js 组件:
1 import React from 'react' 2 import{Link, Redirect} from 'react-router-dom' 3 import '../css/login.css' 4 import {userService as service} from '../service/user'; 5 import { observer } from 'mobx-react'; 6 7 8 9 export default class Reg extends React.Component{ 10 render(){ 11 return <_Reg service={service} />; 12 } 13 } 14 // 观察者 15 @observer 16 class _Reg extends React.Component{ 17 validatePwd(pwd1, pwd2) { 18 return pwd1.value == pwd2.value //确保两次输入的密码一致,下面没有代码,可以自己添加 19 } 20 21 handleClick(event) { 22 event.preventDefault() 23 console.log(typeof(event.target.form),'=========') 24 const [name, email, password, confirm] = event.target.form; 25 console.log(this.validatePwd(password, confirm),'===========') 26 this.props.service.reg(name.value, email.value, password.value); 27 28 } 29 30 render(){ 31 if (this.props.service.loggedin) { 32 return <Redirect to="/" /> 33 } 34 return( 35 <div className="login-page"> 36 <div className="form"> 37 <form className="register-form"> 38 <input type="text" placeholder="姓名"/> 39 <input type="text" placeholder="邮箱"/> 40 <input type="password" placeholder="密码"/> 41 <input type="password" placeholder="确认密码"/> 42 <button onClick={this.handleClick.bind(this)}>注册</button> 43 <p className="message">已经注册? <Link to="/login">登录</Link></p> 44 </form> 45 </div> 46 </div> 47 ) 48 } 49 }
1 # 注册(业务) insert 2 def reg(request:HttpRequest): 3 try: 4 play = simplejson.loads(request.body) 5 # print(play, type(play),'========================') 6 email = play["email"] 7 a = User.objects.filter(email=email) 8 # print(a.query) # 查看查询语句 9 if a.first():# 懒惰,只要用,就会查 10 return HttpResponseBadRequest('用户名存在') 11 print('--------------------------------------') 12 name = play["name"] 13 # 这两步可以合起来,减少计算 14 password = play["password"] 15 print(password) 16 password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) 17 # ORM 操作 18 user = User() 19 user.email = email 20 user.name = name 21 user.password = password 22 23 try: # 这个try可以去掉,不要把e法到浏览器端, 24 # 这个try里可以做些特殊处理; 25 user.save() # commit提交 26 # 如果正常,返回json数据 27 token = gen_token(user.id) 28 res = JsonResponse({ 29 'user': { 30 "user_id": user.id, 31 "name": user.name, 32 "email": user.email 33 }, 34 'token': token 35 }) 36 res.set_cookie('Jwt', token) # set cookie 37 return res 38 except jwt.ExpiredSignatureError as e: 39 print(e) 40 return HttpResponseBadRequest("jwt 过期了") 41 except Exception as e: 42 print(e) 43 # return HttpResponseBadRequest("参数错误") 44 raise # 直接往外抛,外面的接住。 45 except Exception as e: # 有任何异常返回。 46 print(e,'==') 47 return HttpResponseBadRequest("参数错误") # 这里返回实例,这不是异常类,继承自httpresponse
注意:
所有的业务操作都是对一个service实例的操作,不能说,注册使用一个service实例,登录使用一个service实例,这样两者没有任何关系
所以直接从service/user.js 中导出,为每个业务使用
本项目是:只要注册成功,就不需要登录了,直接登录成功,所以后台直接返回token
2、Ant Design
Ant Design 蚂蚁金服开源的 React UI 库
官网:https://ant.design/index-cn
官方文档:https://ant.design/docs/react/introduce-cn
安装: $npm install antd $ yarn add antd
使用:
3、网页中信息显示
网页开发中,不管操作成功与否,有很多信息提示, 目前信息都是控制台输出的,用户看不到,使用Antd的message组件显示友好信息提示
在service/user.js 中增加一个被观察对象
1 import axios from "axios" 2 import { observable } from "mobx"; 3 import store from 'store' 4 5 6 // 过期插件 7 store.addPlugin(require('store/plugins/expire')) 8 9 class UserService { 10 // 定义被观察对象 11 @observable loggedin = false 12 @observable errMsg = '' // 登录错误信息 13 14 //登录 15 login (email, password) { 16 //TODO 17 console.log('----------------') 18 console.log(email) 19 console.log(password) 20 console.log('----------------') 21 22 axios.post('/api/user/login', { 23 email:email, 24 password:password 25 })// 成功执行 26 // 普通函数,用箭头处理this问题 27 .then((response) => { 28 console.log(1,response); 29 console.log(2,response.data) 30 const {token, user} = response.data 31 console.log(3,token) 32 store.set('token',token, (new Date()).getTime() + (8 * 3600 * 1000)) 33 console.log(4,user) 34 //成功后,状态改变 35 this.loggedin = true 36 this.errMsg = '' 37 38 })//错误就不执行 39 .catch( (error) => { 40 console.log(7,error); 41 this.loggedin = false; 42 this.errMsg = '登录名或密码错误' 43 }); 44 } 45 46 // 注册 47 reg (name, email, password) { 48 //TODO 49 console.log('-------***---------') 50 console.log(email) 51 console.log(password) 52 console.log('-------***---------') 53 54 axios.post('/api/user/reg', { 55 email, 56 password, 57 name 58 })// dev server 会代理 59 // 普通函数,用箭头处理this问题 60 .then( response => { 61 console.log(1,response); 62 console.log(2,response.data) 63 console.log(3,response.status) 64 const {token, user} = response.data 65 console.log(4,token) 66 // 存储token 67 store.set('token',token, (new Date()).getTime() + (8 * 3600 * 1000)) 68 console.log(5,user) 69 //成功后,状态改变 70 console.log('==================') 71 this.loggedin = true 72 this.errMsg = ' ' 73 console.log('==================') 74 75 })//错误就不执行 76 .catch(error => { 77 console.log(6,error); 78 this.loggedin = false; 79 this.errMsg = '注册失败' 80 81 }); 82 } 83 } 84 85 86 87 //全局变量 88 const userService = new UserService() 89 90 export {userService}
component/login.js组件,增加Antd的message组件
1 import React from 'react'; 2 import {Link,Redirect} from 'react-router-dom'; 3 import '../css/login.css'; 4 import {userService as service} from '../service/user'; 5 import { observer } from 'mobx-react'; 6 import { message } from 'antd' 7 import 'antd/lib/message/style' 8 9 10 11 12 export default class Login extends React.Component{ 13 render(){ 14 return <_Login service={service} />; 15 } 16 } 17 // 观察者 18 @observer 19 class _Login extends React.Component { 20 handleClick(event) { 21 event.preventDefault(); 22 const [email, password] = event.target.form 23 this.props.service.login(email.value, password.value)// 获取表单的值 24 } 25 26 27 render(){ 28 console.log('======================') 29 // 观察者发现改变了,在这里只要用一次,即便是打印,就会通知render 30 // 没有改变,则不会被调用loggedin 31 console.log(this.props.service.loggedin) 32 // 登录成功之后,就不会往下走了,直接return,并跳转 33 if (this.props.service.loggedin) return <Redirect to="/" /> 34 //import { message } from 'antd' 35 // 官网提供的函数:显示3秒信息,之后,置空 36 if (this.props.service.errMsg) {// 如果注册失败,才会进来 37 //js中 [] {} 等效true 38 message.info(this.props.service.errMsg, 3, 39 () => this.props.service.errMsg = '') 40 } 41 42 return( 43 <div className="login-page"> 44 <div className="form"> 45 <form className="login-form"> 46 <input type="text" placeholder="邮箱"/> 47 <input type="password" placeholder="密码"/> 48 <button onClick={this.handleClick.bind(this)}>登录</button> 49 <p className="message">未注册? <a href="#">立即注册</a></p> 50 {/* <span>{this.props.service.errMsg}</span> */} 51 </form> 52 </div> 53 </div> 54 ) 55 } 56 }
结果:
上面的代码执行到用户登录失败的时候,浏览器控制台会抛出 一个警告:
解决办法:
将消息的清除代码移动到 componentDidUpdat中(render结束后执行 消息提示)
1 import React from 'react'; 2 import {Link,Redirect} from 'react-router-dom'; 3 import '../css/login.css'; 4 import {userService as service} from '../service/user'; 5 import { observer } from 'mobx-react'; 6 import { message } from 'antd' 7 import 'antd/lib/message/style' 8 9 10 11 12 export default class Login extends React.Component{ 13 render(){ 14 return <_Login service={service} />; 15 } 16 } 17 // 观察者 18 @observer 19 class _Login extends React.Component { 20 handleClick(event) { 21 event.preventDefault(); 22 const [email, password] = event.target.form 23 this.props.service.login(email.value, password.value)// 获取表单的值 24 } 25 26 27 render(){ 28 console.log('======================') 29 // 观察者发现改变了,在这里只要用一次,即便是打印,就会通知render 30 // 没有改变,则不会被调用loggedin 31 console.log(this.props.service.loggedin) 32 // 登录成功之后,就不会往下走了,直接return,并跳转 33 if (this.props.service.loggedin) return <Redirect to="/" /> 34 35 let em = this.props.service.errMsg; // 引用这个值,留作观察,否则不知道时候改了没 36 37 return( 38 <div className="login-page"> 39 <div className="form"> 40 <form className="login-form"> 41 <input type="text" placeholder="邮箱"/> 42 <input type="password" placeholder="密码"/> 43 <button onClick={this.handleClick.bind(this)}>登录</button> 44 <p className="message">未注册? <a href="#">立即注册</a></p> 45 {/* <span>{this.props.service.errMsg}</span> */} 46 </form> 47 </div> 48 </div> 49 ) 50 } 51 componentDidUpdate(prevProps, prevState) { //渲染后,显示消息组件 52 //这里的prevProps和下面的this.props是同一个值,因为service这个属性没有改变 53 //一直是user。js中导出的service 54 //import { message } from 'antd' 55 // 官网提供的函数:显示3秒信息,之后,置空 56 if (prevProps.service.errMsg) {// 如果注册失败,才会进来 57 //js中 [] {} 等效true 58 message.info(prevProps.service.errMsg, 3, 59 () => prevProps.service.errMsg = '') 60 } 61 } 62 }
component/reg.js组件同样增加message组件
1 import React from 'react' 2 import{Link, Redirect} from 'react-router-dom' 3 import '../css/login.css' 4 import {userService as service} from '../service/user'; 5 import { observer } from 'mobx-react'; 6 import { message } from 'antd' 7 import 'antd/lib/message/style' 8 import {inject} from '../utils' 9 10 11 12 // 观察者 13 @inject({service}) 14 @observer 15 class _Reg extends React.Component{ 16 validatePwd(pwd1, pwd2) { 17 return pwd1.value == pwd2.value 18 } 19 validateEma(email) { 20 // email校验 21 } 22 23 handleClick(event) { 24 event.preventDefault() 25 console.log(typeof(event.target.form),'=========') 26 const [name, email, password, confirm] = event.target.form; 27 if (this.validatePwd(password, confirm) ){ 28 this.props.service.reg(name.value, email.value, password.value); 29 30 } 31 else { 32 console.log('两次密码不同') 33 this.props.service.reg(name.value, email.value, password.value,false); 34 } 35 36 37 } 38 39 render(){ 40 if (this.props.service.loggedin) { 41 return <Redirect to="/" /> 42 } 43 let em = this.props.service.errMsg; // 引用这个值,留作观察,否则不知道时候改了没 44 45 return( 46 <div className="login-page"> 47 <div className="form"> 48 <form className="register-form"> 49 <input type="text" placeholder="姓名"/> 50 <input type="text" placeholder="邮箱"/> 51 <input type="password" placeholder="密码"/> 52 <input type="password" placeholder="确认密码"/> 53 <button onClick={this.handleClick.bind(this)}>注册</button> 54 <p className="message">已经注册? <Link to="/login">登录</Link></p> 55 </form> 56 </div> 57 </div> 58 ) 59 } 60 componentDidUpdate(prevProps, prevState) { //渲染后,显示消息组件 61 //这里的prevProps和下面的this.props是同一个值,因为service这个属性没有改变 62 //一直是user。js中导出的service 63 //import { message } from 'antd' 64 // 官网提供的函数:显示3秒信息,之后,置空 65 if (prevProps.service.errMsg) {// 如果注册失败,才会进来 66 //js中 [] {} 等效true 67 message.info(prevProps.service.errMsg, 3, 68 () => prevProps.service.errMsg = '') 69 } 70 } 71 }
4、高阶组件装饰器
装饰器函数的整个过程演变如下:
1 export default class Login extends React.Component{ 2 render(){ 3 return <_Login service={service} />; 4 } 5 } 6 7 class Login extends React.Component{ 8 render(){ 9 return <_Login service={service} />; 10 } 11 } 12 // 匿名 组件 13 const Login = class extends React.Component { 14 render(){ 15 return <_Login service={service} />; 16 } 17 } 18 // 提参数 19 function inject (){ 20 return Login 21 } 22 23 function inject() { 24 return class extends React.Component { 25 render(){ 26 return <_Login service={service} />; 27 } 28 } 29 } 30 31 function inject(x,Comp) { 32 return class extends React.Component { 33 render(){ 34 return <Comp service={x} />; 35 } 36 } 37 } 38 39 //柯里化 40 function inject(x) { 41 function wrapper (Comp) { 42 return class extends React.Component { 43 render(){ 44 return <Comp service={x} />; 45 } 46 } 47 } 48 return wrapper 49 } 50 51 52 53 function inject(obj) { 54 function wrapper (Comp) { 55 return class extends React.Component { 56 render(){ 57 return <Comp {...obj} /> 58 } 59 } 60 } 61 return wrapper 62 } 63 64 function inject(obj) { 65 return wrapper (Comp) { 66 return class extends React.Component { 67 render(){ 68 return <Comp {...obj} /> 69 } 70 } 71 } 72 } 73 74 const inject = obj => Comp => { 75 return class extends React.Component { 76 render(){ 77 return <Comp {...obj} /> 78 } 79 } 80 } 81 //等价为一个无状态组件 82 const inject = obj => Comp => { 83 return function (props) { 84 return <Comp {...obj} /> 85 } 86 } 87 88 89 const inject = obj => Comp => { 90 props => <Comp {...obj} />; 91 } 92 93 const inject = obj => Comp => props => <Comp {...obj} />; 94 const inject = obj => Comp => props => <Comp {...obj} {...props} />;
新建一个工具文件 src/utils.js 放入以下内容:
1 import React from 'react' 2 3 const inject = obj => Comp => props => <Comp {...obj} {...props} />; 4 5 6 export {inject}
将登录,注册组件装饰一下:
具体代码:Mobx的observer 装饰器要求,所以注意装饰的顺序
1 import React from 'react'; 2 import {Link,Redirect} from 'react-router-dom'; 3 import '../css/login.css'; 4 import {userService as service} from '../service/user'; 5 import { observer } from 'mobx-react'; 6 import { message } from 'antd' 7 import 'antd/lib/message/style' 8 import {inject} from '../utils' 9 10 11 @inject({service}) 12 @observer//官方推荐,贴近类 13 export default class Login extends React.Component { 14 handleClick(event) { 15 event.preventDefault(); 16 const [email, password] = event.target.form 17 this.props.service.login(email.value, password.value)// 获取表单的值 18 } 19 20 21 render(){ 22 console.log('======================') 23 // 观察者发现改变了,在这里只要用一次,即便是打印,就会通知render 24 // 没有改变,则不会被调用loggedin 25 console.log(this.props.service.loggedin) 26 // 登录成功之后,就不会往下走了,直接return,并跳转 27 if (this.props.service.loggedin) return <Redirect to="/" /> 28 29 let em = this.props.service.errMsg; // 引用这个值,留作观察,否则不知道时候改了没 30 31 return( 32 <div className="login-page"> 33 <div className="form"> 34 <form className="login-form"> 35 <input type="text" placeholder="邮箱"/> 36 <input type="password" placeholder="密码"/> 37 <button onClick={this.handleClick.bind(this)}>登录</button> 38 <p className="message">未注册? <a href="#">立即注册</a></p> 39 {/* <span>{this.props.service.errMsg}</span> */} 40 </form> 41 </div> 42 </div> 43 ) 44 } 45 componentDidUpdate(prevProps, prevState) { //渲染后,显示消息组件 46 //这里的prevProps和下面的this.props是同一个值,因为service这个属性没有改变 47 //一直是user。js中导出的service 48 //import { message } from 'antd' 49 // 官网提供的函数:显示3秒信息,之后,置空 50 if (prevProps.service.errMsg) {// 如果注册失败,才会进来 51 //js中 [] {} 等效true 52 message.info(prevProps.service.errMsg, 3, 53 () => prevProps.service.errMsg = '') 54 } 55 } 56 }
1 import React from 'react' 2 import{Link, Redirect} from 'react-router-dom' 3 import '../css/login.css' 4 import {userService as service} from '../service/user'; 5 import { observer } from 'mobx-react'; 6 import { message } from 'antd' 7 import 'antd/lib/message/style' 8 import {inject} from '../utils' 9 10 11 12 // 观察者 13 @inject({service}) 14 @observer 15 class _Reg extends React.Component{ 16 validatePwd(pwd1, pwd2) { 17 return pwd1.value == pwd2.value 18 } 19 validateEma(email) { 20 // email校验 21 } 22 23 handleClick(event) { 24 event.preventDefault() 25 console.log(typeof(event.target.form),'=========') 26 const [name, email, password, confirm] = event.target.form; 27 if (this.validatePwd(password, confirm) ){ 28 this.props.service.reg(name.value, email.value, password.value); 29 30 } 31 else { 32 console.log('两次密码不同') 33 this.props.service.reg(name.value, email.value, password.value,false); 34 } 35 36 37 } 38 39 render(){ 40 if (this.props.service.loggedin) { 41 return <Redirect to="/" /> 42 } 43 let em = this.props.service.errMsg; // 引用这个值,留作观察,否则不知道时候改了没 44 45 return( 46 <div className="login-page"> 47 <div className="form"> 48 <form className="register-form"> 49 <input type="text" placeholder="姓名"/> 50 <input type="text" placeholder="邮箱"/> 51 <input type="password" placeholder="密码"/> 52 <input type="password" placeholder="确认密码"/> 53 <button onClick={this.handleClick.bind(this)}>注册</button> 54 <p className="message">已经注册? <Link to="/login">登录</Link></p> 55 </form> 56 </div> 57 </div> 58 ) 59 } 60 componentDidUpdate(prevProps, prevState) { //渲染后,显示消息组件 61 //这里的prevProps和下面的this.props是同一个值,因为service这个属性没有改变 62 //一直是user。js中导出的service 63 //import { message } from 'antd' 64 // 官网提供的函数:显示3秒信息,之后,置空 65 if (prevProps.service.errMsg) {// 如果注册失败,才会进来 66 //js中 [] {} 等效true 67 message.info(prevProps.service.errMsg, 3, 68 () => prevProps.service.errMsg = '') 69 } 70 } 71 }
@inject({service}) // Reg=inject({service})(Reg) props是Login组件自己的 属性props