案例代码:React/Login · 云牧/exampleCode - 码云 - 开源中国 (gitee.com)
案例图示:
1.前置准备
1.antd的基本使用:
(1).yarn add antd
(2).引入样式:import 'antd/dist/antd.css';
(3).根据文档引入组件
2.antd按需引入样式
(1).yarn add react-app-rewired customize-cra babel-plugin-import
(2).修改package.json,内容如下:
.....
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
.....
(3).在根目录下创建:config-overrides.js,内容如下:
const { override, fixBabelImports } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd', //对哪个库进行按需引入
libraryDirectory: 'es', //样式模块作为ES6模块处理
style: 'css',//处理的css样式
}),
);
3.antd自定义主题
(1).yarn add less less-loader
(2).修改config-overrides.js,内容如下:
const { override, fixBabelImports,addLessLoader } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd', //对哪个库进行按需引入
libraryDirectory: 'es', //样式模块作为ES6模块处理
style: true,//处理原文件样式
}),
addLessLoader({
lessOptions:{
javascriptEnabled: true, //允许js更改修改antd的less文件中的变量
modifyVars: { '@primary-color': 'green' },
}
}),
);
4.antd-mobile配置:
修改config-overrides:
const { override, fixBabelImports,addLessLoader,addPostcssPlugins } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd-mobile', //对哪个库进行按需引入
libraryDirectory: 'es', //样式模块作为ES6模块处理
style: true,//处理原文件样式
}),
addLessLoader({
lessOptions:{
javascriptEnabled: true, //允许js更改修改antd的less文件中的变量
// modifyVars: { '@primary-color': 'green' }, //antd要修改的是@primary-color
modifyVars: {
"@brand-primary": "green",
"@brand-primary-tap":"rgb(1, 99, 1);"
},
}
}),
addPostcssPlugins([
require("postcss-px2rem")({
remUnit: 37.5 //按照设计师的设计稿计算出来的根字体大小
})
])
);
5.react脚手架中的rem适配
1.初始化react脚手架
create-react-app he11lo_react
2.安装依赖
yarn add postcss-px2rem customize-cra react-app-rewired
3.根目录下建立 config.overrides.js 内容如下:
module.exports = override( addPostcssPlugins([
addPostcssPlugins([
require("postcss-px2rem")({
remUnit: 37.5, //按照设计师的设计稿计算出来的根字体大小
}),
])
);
4.更改 package.json 的启动命令:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
5.src 目录下建立 utils/rem.js,内容如下(给不同设备设置根字体大小):
/*
该文件是移动端适配器
*/
function adapter() {
//获取布局视口宽度,因为开启了理想视口,布局视口=设备横向独立像素值
const dpWidth = document.documentElement.clientWidth;
//计算根字体大小
const rootFonstSize = dpWidth / 10;
//设置根字体大小
document.documentElement.style.fontSize = rootFonstSize + "px";
}
adapter();
window.onresize = adapter;
2.项目编码
app.jsx
import React, { Component } from 'react'
import routes from './config/routes'
import {Route,Switch} from 'react-router-dom'
import './utils/rem'
export default class App extends Component {
render() {
return (
<div>
<Switch>
{routes.map( routeObj => <Route key={routeObj.path} {...routeObj}/> )}
</Switch>
</div>
)
}
}
config/routes
// 路由配置文件,项目中所有路由都在此配置
import Login from "../pages/Login";
import UserCenter from "../pages/UserCenter";
const routes = [
//登录路由
{
path: "/login",
component: Login,
},
//个人中心路由
{
path: "/usercenter",
component: UserCenter,
},
];
export default routes;
pages/Login/index.jsx
// 登录组件
import React, { Component } from "react";
import { NavBar, InputItem, Button, Toast } from "antd-mobile";
import { reqVerifyCode, reqLogin } from "../../api";
import github from "./imgs/github.png";
import qq from "./imgs/qq.png";
import wechat from "./imgs/wechat.png";
import { phoneReg, verifyCodeReg } from "../../utils/reg";
import "./index.less";
export default class Login extends Component {
state = {
phone: "",
verifyCode: "",
time: 60,
canClick: true,
};
//保存数据
saveData = (type) => {
return (value) => {
//如果用户输入的数据,符合要求,那么维护进状态
if (type === "phone" && phoneReg.test(value)) return this.setState({ [type]: value });
if (type === "verifyCode" && verifyCodeReg.test(value))
return this.setState({ [type]: value });
this.setState({ [type]: "" });
};
};
//获取验证码的回调
getVerifyCode = async () => {
//获取手机号、按钮状态
const { phone, canClick } = this.state;
//如果按钮不可点击,终止逻辑
if (!canClick) return;
//校验手机号
if (!phone) return Toast.fail("手机号格式不合法", 1);
//更新状态让获取验证码按钮不可点击
this.setState({ canClick: false });
//开启定时器更新倒计时
this.timer = setInterval(() => {
let { time } = this.state;
time--;
//若倒计时结束
if (time <= 0) {
clearInterval(this.timer);
return this.setState({ canClick: true, time: 60 });
}
this.setState({ time });
}, 1000);
//发送请求
await reqVerifyCode(phone);
Toast.success("验证码发送成功");
};
//登录的回调
login = async () => {
//获取手机号,验证码
const { phone, verifyCode } = this.state;
if (!(phone && verifyCode)) return Toast.fail("请检查手机号或验证码格式", 2);
const result = await reqLogin(phone, verifyCode);
const { code, message } = result;
if (code === 20000) {
Toast.success("登录成功!");
this.props.history.push("/usercenter");
} else Toast.fail(message);
};
componentWillUnmount() {
clearInterval(this.timer);
}
githubLogin = () => {
window.location.href =
"https://github.com/login/oauth/authorize?client_id=908ba7f76c6df2f6ac33";
};
render() {
const { time, canClick, phone, verifyCode } = this.state;
return (
<div className="login">
{/* 顶部导航区 */}
<NavBar mode="light">手机验证码登录</NavBar>
{/* 手机验证码输入框 */}
<InputItem onChange={this.saveData("phone")} clear placeholder="请输入手机号" />
{/* 验证码输入框 */}
<div className="verify-input">
<InputItem onChange={this.saveData("verifyCode")} clear placeholder="请输入验证码" />
<button
className="verify-btn"
style={{ color: canClick ? "#F40700" : "gray" }}
onTouchEnd={this.getVerifyCode}
>
获取验证码{canClick ? "" : `(${time})`}
</button>
</div>
{/* 登录按钮 */}
<Button
onTouchEnd={this.login}
type="primary"
disabled={phone && verifyCode ? false : true}
>
登录
</Button>
{/* 底部其他登录方式区 */}
<footer className="footer">
<span className="other">其他登录方式</span>
<div className="login-type">
<img onTouchEnd={this.githubLogin} src={github} alt="" />
<img src={qq} alt="" />
<img src={wechat} alt="" />
</div>
<span className="footer-text">
未注册的手机号,验证后会自动创建硅谷账号,登录即代表您同意:
<a href="http://www.atguigu.com">《硅谷隐私政策》</a>
</span>
</footer>
</div>
);
}
}
api/ajax.js
//该文件是对axios的二次封装,目的是:统一处理请求的错误,返回服务器的纯数据
import axios from "axios";
import { Toast } from "antd-mobile";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
//使用axios的请求拦截器
axios.interceptors.request.use((config) => {
NProgress.start(); //进度条开始
return config;
});
//使用axios的响应拦截器
axios.interceptors.response.use(
(response) => {
NProgress.done(); //进度条结束
return response.data;
},
(error) => {
//如果状态码不是2开头,就会进入该回调
NProgress.done(); //进度条结束
Toast.fail(error.message);
return new Promise(() => {});
}
);
export default axios;
api/index.js
//统一管理项目中所有的ajax请求
import ajax from "./ajax";
//请求验证码
export const reqVerifyCode = (phone) => ajax.post("/login/digits", { phone });
//请求登录
export const reqLogin = (phone, code) => ajax.post("/login/phone", { phone, code });
//请求校验用户身份
export const reqVerifyToken = () => ajax.post("/login/verify");
//请求校验用户身份
export const reqLogout = (_id) => ajax.post("/logout", { _id });
pages/UseCenter/index.jsx
//个人中心组件
import React, { Component } from "react";
import { Toast, NavBar, Button } from "antd-mobile";
import { reqVerifyToken, reqLogout } from "../../api";
import "./index.less";
export default class UserCenter extends Component {
state = {
nickName: "",
phone: "",
avatar: "",
_id: "",
};
logout = async () => {
await reqLogout(this.state._id);
this.props.history.replace("/login");
};
async componentDidMount() {
const result = await reqVerifyToken();
const { code, message, data, _id } = result;
if (code !== 20000) {
Toast.fail(message);
this.props.history.replace("/login");
} else {
const { nickName, phone, avatar } = data;
this.setState({ nickName, phone, avatar });
}
}
render() {
const { nickName, phone, avatar } = this.state;
return (
<div className="user-info">
<NavBar mode="light">个人中心</NavBar>
<img className="avatar" src={avatar} alt="" />
<div className="nick-name">昵称:{nickName}</div>
<Button onTouchEnd={this.logout} type="primary">
退出登录
</Button>
</div>
);
}
}
3.token原理图
4.OAuth 2.0
1.OAuth 2.0
- OAuth 2.0 是目前最流行的授权机制,用来授权第三方应用,获取用户数据。
- 简单说,OAuth 就是一种授权机制。数据的所有者同意其他应用使用自己存储的用户信息。
2. 授权流程(以GitHub为例)
-
开发流程介绍
- 从A 网站跳转到 GitHub授权页面。
- GitHub 要求校验用户信息,引导用户登录。
- GitHub 询问"A 网站要求获得你的xx数据,你是否同意?"
- 用户同意,GitHub 就会重定向到A网站对应的服务器,同时发回一个授权码。
- A网站服务器使用授权码,向 GitHub 请求令牌。
- GitHub 返回令牌token. A网站服务器使用令牌,向 GitHub 请求用户数据。
-
应用登记
- 一个应用要 OAuth 授权,必须先到对方网站登记,让对方知道是谁在请求。
-
Github OAuth 2.0 文档
3.使用GitHub授权
1.GitHub登记应用
登记地址:github.com/settings/ap…
2.获得client_id
3.配置
3.1 前台项目准备好个人中心组件,供授权成功后查看
3.2 将得到的 client_id 、clinet_secret配置到服务器config\index.js中,随后重启服务器。
// github oauth
const CLIENT_ID = "xxxxxxxxxxxxxxx";
const CLIENT_SECRET = "xxxxxxxxxxxxxxx";
3.3 将得到的 client_id 存到前端项目 config\oauth.js 中
// github申请成功后得到的client_id
const client_id = "xxxx";
// oauth验证网址
const auth_url = "https://github.com/login/oauth/authorize";
export { client_id, auth_url };
3.4 项目中携带网站标识跳转到授权页
loginGithub = ()=>{
window.location.href = AUTH_BASE_URL+'?client_id='+CLIENT_ID
}