移动端项目

一 明确案例的需求

需要理解的概念有:

  • 手机验证码的登录
  • 手机验证码的获取
  • 第三方平台的授权登录
  • 第三方平台用户信息的获取

二 antd的基本使用

需要理解的概念有:

  • React的UI框架有哪些

    • https://ant.design/docs/react/introduce-cn
    • https://material-ui.com/zh/
    • https://zijieke.com/semantic-ui/
    • https://react-bootstrap.github.io/
    • https://apiblueprint.org/
  • Antd的完整安装与使用

    • yarn add antd
    import React, { Component } from 'react';
    import { DatePicker } from 'antd';
    import 'antd/dist/antd.css';
    // 需要引入样式内容,否则界面无样式效果export default class App extends Component {render() {return (<div><DatePicker /></div>);}
    }
    
  • 常用组件的使用

    • 需要先将组件引入
    • 再进行组件的使用
    • Button、Input、DatePicker、Icon、Modal…

三 antd的按需引入样式

需要理解的概念有: npm run build 压缩打包

  • JS按需加载:antd 的 JS 代码默认支持基于 ES modules 的 tree shaking
  • CSS按需加载:
    • 模块安装:npm add @craco/craco craco-less craco-antd
    • 配置文件:package.json(二次环境配置)
{"name": "hello-react",..."scripts": {"start": "craco start","build": "craco build","test": "craco test","eject": "react-scripts eject"},...
}
  • 配置文件:craco.config.js

react是基于webpack配置运行的,但是我们看不到任何的webpack配置,只能用eject进行弹射时才能查看。

为社么使用require? 因为webpack是基于commonjs的

webpack基于五大核心概念:入口 出口 加载器 模式 插件

const CracoAntDesignPlugin = require('craco-antd');
//设置了一个插件
module.exports = {plugins: [{ plugin: CracoAntDesignPlugin }],
};

App.js,去除css的引入

import React, { Component } from 'react';
import { DatePicker } from 'antd';
export default class App extends Component {render() {return (<div><DatePicker /></div>);}
}

四 antd自定义主题

需要理解的概念有:

  • 默认主题的使用
  • 自定义主题的修改

App.js,引入less

import React, { Component } from 'react';
import { DatePicker } from 'antd';
import './App.less';
export default class App extends Component {render() {return (<div><DatePicker /></div>);}
}

App.less,引入less

@import '~antd/dist/antd.less';
  • 配置文件:craco.config.js
//安装插件
const CracoLessPlugin = require('craco-less');
const CracoAntDesignPlugin = require('craco-antd');module.exports = {plugins: [{ plugin: CracoAntDesignPlugin },{plugin: CracoLessPlugin,options: {lessLoaderOptions: {lessOptions: {modifyVars: { '@primary-color': '#ffcc00' },// 设置自定义主题样式// 配置参数:https://ant.design/docs/react/customize-theme-cnjavascriptEnabled: true,},},},},],
};

五 antd-mobile的配置

需要理解的概念有:

  • react项目的创建

  • UI框架的安装与使用

  • antd-mobile的安装使用与配置

    https://mobile.ant.design/docs/react/use-with-create-react-app-cn

1.项目的创建、模块的安装以及package.json文件的修改

npm i antd-mobile --save
# less与less-loader需要指定版本,如果按最新版本会出现兼容性错误问题以及less语法等后续问题    按需加载
npm i babel-plugin-import customize-cra react-app-rewired less@3.13.1 less-loader@7.3.0 --sav-dev

package.json

{"name": "react-mobile","version": "0.1.0","private": true,"dependencies": {"@testing-library/jest-dom": "^5.11.4","@testing-library/react": "^11.1.0","@testing-library/user-event": "^12.1.10","antd-mobile": "^2.3.4","react": "^17.0.2","react-dom": "^17.0.2","react-scripts": "4.0.3","web-vitals": "^1.0.1"},"scripts": {"start": "react-app-rewired start","build": "react-app-rewired build","test": "react-app-rewired test --env=jsdom","eject": "react-scripts eject"},"eslintConfig": {"extends": ["react-app","react-app/jest"]},"browserslist": {"production": [">0.2%","not dead","not op_mini all"],"development": ["last 1 chrome version","last 1 firefox version","last 1 safari version"]},"devDependencies": {"babel-plugin-import": "^1.13.3","customize-cra": "^1.0.0","less": "^3.13.1","less-loader": "^7.3.0","react-app-rewired": "^2.1.8"}
}

2.config-overrides.js 的创建-按需加载

const { override, fixBabelImports } = require('customize-cra');module.exports = function override(config, env) {// do stuff with the webpack config...return config;
};
module.exports = override(fixBabelImports('import', {libraryName: 'antd-mobile',style: 'css',})
);

3.html文件的修改,加入移动端特性内容

public/index.html

https://mobile.ant.design/docs/react/introduce-cn#3.-%E4%BD%BF%E7%94%A8

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><link rel="icon" href="%PUBLIC_URL%/favicon.ico" /><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /><script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script><script>if ('addEventListener' in document) {document.addEventListener('DOMContentLoaded', function () {FastClick.attach(document.body);}, false);}if (!window.Promise) {document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"' + '>' + '<' + '/' + 'script>');}</script><meta name="theme-color" content="#000000" /><meta name="description" content="Web site created using create-react-app" /><link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /><link rel="manifest" href="%PUBLIC_URL%/manifest.json" /><title>React App</title>
</head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

4.引入antd-mobile组件

App.js

import logo from './logo.svg';
import './App.css';
import { Button } from 'antd-mobile';function App() {return (<div className='App'><Button type='primary'>按钮</Button></div>);
}export default App;

5.实现自定义主题

config-overrides.js

const { override, addLessLoader, fixBabelImports } = require('customize-cra');
module.exports = override(addLessLoader({lessOptions: {modifyVars: { '@brand-primary': '#3BBE64', '@brand-primary-tap': '#B22222', '@hd': '1px' },// 设置自定义主题样式// 配置参数:https://github.com/ant-design/ant-design-mobile/blob/master/components/style/themes/default.lessjavascriptEnabled: true, // 允许js更改antd-mobile中的less变量},}),fixBabelImports('import', {libraryName: 'antd-mobile', // 对哪个库进行按需引入libraryDirectory: 'es', // 样式模块作为es6模块引入style: true, // 处理原样式文件})
);

六 react脚手架中的rem适配

需要理解的概念有:

  • rem+Less的适配模式
  • 脚手架插件模式的适配

1.PC端REM+Less根节点计算模式

public/index.html

计算根节点字体大小

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><link rel="icon" href="%PUBLIC_URL%/favicon.ico" /><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /><script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script><script>if ('addEventListener' in document) {document.addEventListener('DOMContentLoaded', function () {FastClick.attach(document.body);}, false);}if (!window.Promise) {document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"' + '>' + '<' + '/' + 'script>');}function adapter() {//获取手机横向的设备独立像素const dip = document.documentElement.clientWidth//计算根字体大小(100是我们自己指定的,375是设计稿宽度)const rootFontSize = dip / 10//设置根字体document.documentElement.style.fontSize = rootFontSize + 'px'}adapter()window.onresize = adapter</script><meta name="theme-color" content="#000000" /><meta name="description" content="Web site created using create-react-app" /><title>React App</title>
</head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div>
</body></html>

App.js

import './App.less';function App() {return <div className='demo'></div>;
}export default App;

App.less

@font: 375/10rem;
/* 将单位设置到变量中,结果不会有空格*/* {margin: 0;padding: 0;
}.demo {width: 345/@font;height: 150/@font;background-color: skyblue;margin: 0 auto;margin-top: 15/@font;
}

不足之处:样式编写代码繁琐

2.利用postcss-px2rem插件实现字体单位的转换

#安装单位转换插件
npm i postcss-px2rem --save-dev

config-overrides.js

① override:重载,覆盖原来的配置

② addLessLoader:添加less的 loader加载器功能

③ fix修补(Babel)Imports引入

④ addPostcssPlugins处理px转rem

/*config-overrides.js*/
const { override, addLessLoader, fixBabelImports, addPostcssPlugins } = require('customize-cra');
module.exports = override(addLessLoader({lessOptions: {modifyVars: { '@brand-primary': '#3BBE64', '@brand-primary-tap': '#B22222', '@hd': '1px' },// 设置自定义主题样式// 配置参数:https://github.com/ant-design/ant-design-mobile/blob/master/components/style/themes/default.lessjavascriptEnabled: true, // 允许js更改antd-mobile中的less变量},}),fixBabelImports('import', {libraryName: 'antd-mobile', // 对哪个库进行按需引入libraryDirectory: 'es', // 样式模块作为es6模块引入style: true, // 处理原样式文件}),//注释中括号   rem适配的基础字体大小按37.5为标准addPostcssPlugins([require('postcss-px2rem')({ remUnit: 37.5 })])// 按照设计稿计算出根节点的字体大小// 使用插件时注意[]数组形式
);

七 搭建路由

需要理解的概念有:

  • 路由概念:静态路由表、分配地址、统一入口、寻址、过滤
  • 模块化开发
  • 数组遍历操作
# 路由模块安装
npm i react-router-dom --save

1.传统模式的路由配置

pages/login/index.js

import React, { Component } from 'react';export default class Login extends Component {render() {return <div>Login</div>;}
}

pages/userCenter/index.js

import React, { Component } from 'react';export default class UserCenter extends Component {render() {return <div>UserCenter</div>;}
}

index.js–统一入口

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';// 设置统一入口BrowserRouter
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>,document.getElementById('root')
);

App.js

import './App.less';
import UserCenter from './pages/userCenter';
import Login from './pages/login';
import { Redirect, Route, Switch } from 'react-router-dom';function App() {return (<div>{/*静态路由表、分配地址、寻址、过滤*/}<Switch><Route path='/login' component={Login} /><Route path='/usercenter' component={UserCenter} /><Redirect to='/login' /></Switch></div>);
}export default App;

2.单独配置表模式的路由配置

src/config/routes.js

import UserCenter from '../pages/userCenter';
import Login from '../pages/login';
// 静态路由表、分配地址、寻址、过滤
export default [{path: '/login',component: Login,},{path: '/usercenter',component: UserCenter,},
];

App.js

import './App.less';
import routes from './config/routes';
import { Redirect, Route, Switch } from 'react-router-dom';function App() {return (<div><Switch>{routes.map((routeObj) => (<Route key={routeObj.path} path={routeObj.path} component={routeObj.component} />))}<Redirect to='/login' /></Switch></div>);
}
export default App;

八 登录组件静态组件

需要理解的概念有:

  • UI框架组件的应用:NavBar、Button、InputItem等
  • 组件代码的解读与属性的设置

1.登录界面上半部分的布局

1)界面布局结构的设置

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem } from 'antd-mobile';
import './index.less';export default class Login extends Component {render() {return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' /><button className='verify-btn'>获取验证码</button></div><Button type='primary'>登录</Button></div>);}
}

2)样式布局的控制

pages/login/index.less

body {background-color: white;
}.login {padding: 10px;
}.verify-input {display: flex;justify-content: space-between;margin-bottom: 30px;.verify-btn {font-size: 15px;width: 120px;background-color: transparent;border: none;color: #F40700;}
}

3)全局主题内容的操控

/*config-overrides.js*/
const { override, addLessLoader, fixBabelImports, addPostcssPlugins } = require('customize-cra');
module.exports = override(addLessLoader({lessOptions: {modifyVars: { '@brand-primary': '#F40700', '@brand-primary-tap': '#bd0a04', '@hd': '1px' },// 设置自定义主题样式// 配置参数:https://github.com/ant-design/ant-design-mobile/blob/master/components/style/themes/default.lessjavascriptEnabled: true, // 允许js更改antd-mobile中的less变量},}),fixBabelImports('import', {libraryName: 'antd-mobile', // 对哪个库进行按需引入libraryDirectory: 'es', // 样式模块作为es6模块引入style: true, // 处理原样式文件}),addPostcssPlugins([require('postcss-px2rem')({ remUnit: 37.5 })])
);

2.登录界面下半部分的布局

1)界面布局结构的设置

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem } from 'antd-mobile';
import './index.less';
import github from './imgs/github.png';//把图片当作模块引入
import qq from './imgs/qq.png';
import wechat from './imgs/wechat.png';export default class Login extends Component {render() {return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' /><button className='verify-btn'>获取验证码</button></div><Button type='primary'>登录</Button>{/* 底部其它的登录方式 */}{/* 线条可以用图片或者伪元素 */}<footer className='footer'><span className='other'>其他登录方式</span><div className='login-type'><img src={github} alt='' /><img src={qq} alt='' /><img src={wechat} alt='' /></div><span className='footer-text'>未注册的手机号,验证后会自动创建账号,登录即代表您同意:<a href='http://www.baidu.com.com'>《隐私政策》</a></span></footer></div>);}
}

2)样式布局的控制

pages/login/index.less

.footer {margin-top: 80px;text-align: center;.other {/* 父元素相对位置,子元素绝对位置 */position: relative;padding-left: 10px;padding-right: 10px;color: gray;/* 子元素绝对位置 */&::after {display: block;content: '';/*自身宽度100%,左边设置-100%与父元素同宽度*/width: 100%;left: -100%;height: 1px;background-color: gray;position: absolute;top: 50%;/*会存在自身高度一半的问题*/transform: translateY(-50%);}&::before {display: block;content: '';width: 100%;/* 一前一后,一左一右 */right: -100%;height: 1px;background-color: gray;position: absolute;top: 50%;transform: translateY(-50%);}}.login-type {width: 120px;margin: 0 auto;margin-top: 30px;display: flex;justify-content: space-around;margin-bottom: 30px;img {width: 30px;height: 30px;}}.footer-text {color: gray;}
}

九 收集表单数据

需要理解的概念有:

  • 接口与接口文档的查看
  • postman的接口调用
  • http请求的状态码有哪些(问题) 12345
  • 项目自定义接口返回的code与http状态码区别(问题)
  • http请求形式有哪些(问题)
  • http的请求参数设置有哪些(问题)
  • 表单数据的收集
  • React组件调试工具的使用

1.接口准备工作

1)mongodb非关系型数据库的启动

program/mongoDB/server/bin mongod --dbpath

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbABDxMt-1624176080961)(C:\Users\angi\AppData\Roaming\Typora\typora-user-images\image-20210610135041087.png)]

2)server项目的启动

服务项目代码:http://qn.chinavanes.com/react-mobile-server.zip

npm install安装依赖模块,运行npm start启动项目

2.表单数据的收集

1)Antd-mobile的Input事件接收与普通的Input的差异

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem } from 'antd-mobile';
import './index.less';
import github from './imgs/github.png';
import qq from './imgs/qq.png';
import wechat from './imgs/wechat.png';export default class Login extends Component {saveData = (type) => {return (event) => {// antd-mobile的Input没有返回event事件对象// 直接返回的用户输入值,所以可以将event修改成valueconsole.log(type, event);};};render() {return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' onChange={this.saveData('phone')} /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' /><button className='verify-btn'>获取验证码</button></div><Button type='primary'>登录</Button>{/* 底部其它的登录方式 */}{/* 线条可以用图片或者伪元素 */}<footer className='footer'><span className='other'>其他登录方式</span><div className='login-type'><img 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>);}
}

2)通过动态赋值方式设置state数据

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem } from 'antd-mobile';
import './index.less';
import github from './imgs/github.png';
import qq from './imgs/qq.png';
import wechat from './imgs/wechat.png';export default class Login extends Component {state = {phone: '',verifyCode: '',};saveData = (type) => {return (value) => {
// 普通的input事实上返回的是event,但现在用的是inputitem组件,所以返回valuethis.setState({[type]: value,});};};render() {return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' onChange={this.saveData('phone')} /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' onChange={this.saveData('verifyCode')} /><button className='verify-btn'>获取验证码</button></div><Button type='primary'>登录</Button>{/* 底部其它的登录方式 */}{/* 线条可以用图片或者伪元素 */}<footer className='footer'><span className='other'>其他登录方式</span><div className='login-type'><img 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>);}
}

3)有效性数据的验证处理

utils/reg.js

正则表达式验证手机号码

export const phoneReg = /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/;
export const verifyCodeReg = /^\d{6}$/;

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem } from 'antd-mobile';
import './index.less';
import github from './imgs/github.png';
import qq from './imgs/qq.png';
import wechat from './imgs/wechat.png';
import { phoneReg, verifyCodeReg } from '../../utils/reg';export default class Login extends Component {state = {phone: '',verifyCode: '',};saveData = (type) => {return (value) => {// 动态赋值进行条件判断,确认操作的是phone还是验证码//如果类型是手机号码,并且正则验证成功,则进行动态赋值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]: '' });};};render() {return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' onChange={this.saveData('phone')} /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' onChange={this.saveData('verifyCode')} /><button className='verify-btn'>获取验证码</button></div><Button type='primary'>登录</Button><footer className='footer'><span className='other'>其他登录方式</span><div className='login-type'><img 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>);}
}

4.获取验证码的点击收集到表单数据

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem } from 'antd-mobile';
import './index.less';
import github from './imgs/github.png';
import qq from './imgs/qq.png';
import wechat from './imgs/wechat.png';
import { phoneReg, verifyCodeReg } from '../../utils/reg';export default class Login extends Component {state = {phone: '',verifyCode: '',};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 = () => {console.log(this.state);};render() {return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' onChange={this.saveData('phone')} /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' onChange={this.saveData('verifyCode')} /><button className='verify-btn' onTouchStart={this.getVerifyCode}>获取验证码</button></div><Button type='primary'>登录</Button><footer className='footer'><span className='other'>其他登录方式</span><div className='login-type'><img 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>);}
}

十 获取验证码

需要理解的概念有:

  • 什么是跨域
  • 跨域的解决方案有哪些
  • 安装axios,并进行引入

1.通过axios进行验证码请求(出现跨域)

pages/login/index.js

 // 获取验证码getVerifyCode = () => {const { phone } = this.state;// 请求时出现跨域axios.post('http://localhost:5000/login/digits', { phone }).then((res) => {console.log(res.data);},(error) => {console.log(error);});};

2.跨域问题的解决

package.json

  "proxy": "http://localhost:5000"

修改请求端口

pages/login/index.js

// 获取验证码getVerifyCode = () => {const { phone } = this.state;// 请求时出现跨域axios.post('http://localhost:3000/login/digits', { phone }).then((res) => {console.log(res.data); // 验证码不会返回到data中,因为实际应该发到手机上},(error) => {console.log(error);});};

3.改善用户体验,增加错误提示

pages/login/index.js

import { NavBar, Button, InputItem, Toast } from 'antd-mobile';// 获取验证码getVerifyCode = () => {const { phone } = this.state;// alert提示用户体验性不好if (!phone) return Toast.fail('手机号格式不合法', 1);axios.post('http://localhost:3000/login/digits', { phone }).then((res) => {console.log(res.data);},(error) => {console.log(error);});};

4.倒计时获取验证码的操作处理

button按钮中不要用disabled属性进行禁用,设置disabled以后按钮仍旧可以触发,可以利用35行代码进行条件判断与中断操作。

pages/login/index.js

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 = () => {// 获取手机号、按钮状态const { phone, canClick } = this.state;// 如果按钮不可点击,终止获取验证码逻辑操作if (!canClick) return;// 校验手机号// alert提示用户体验性不好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);axios.post('http://localhost:3000/login/digits', { phone }).then((res) => {console.log(res.data);},(error) => {console.log(error);});};render() {const { canClick, time } = this.state;return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' onChange={this.saveData('phone')} /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' onChange={this.saveData('verifyCode')} /><buttonclassName='verify-btn'style={{ color: canClick ? '#F40700' : 'gray' }}onTouchEnd={this.getVerifyCode}>      //三元运算  点击时显示time获取验证码{canClick ? '' : `(${time})`}</button></div><Button type='primary'>登录</Button><footer className='footer'><span className='other'>其他登录方式</span><div className='login-type'><img 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>);}
}

十一 封装axios

需要理解的概念有:

  • axios的封装,统一进行拦截器处理、统一进行提示操作、统一进行进度条显示
  • 统一接口的封装,可以考虑按模块进行接口封装处理
  • 请求接口的引入与调用
  • 异步操作的实现async、await

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1XLicUyE-1624176080963)(C:\Users\angi\AppData\Roaming\Typora\typora-user-images\image-20210610145827345.png)]

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 });

pages/login/index.js

import { reqVerifyCode } from '../../api';// 获取验证码getVerifyCode = async () => {// 获取手机号、按钮状态const { phone, canClick } = this.state;// 如果按钮不可点击,终止逻辑if (!canClick) return;// 校验手机号// alert提示用户体验性不好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);const result = await reqVerifyCode(phone);console.log(result);Toast.success('验证码发送成功');};

十二 发送登录请求

需要理解的概念有:

  • 统一接口的封装
  • 请求接口的调用
  • 按钮disabled状态的判断
  • 错误提示的查看

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 });

pages/login/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem, Toast } from 'antd-mobile';
import axios from 'axios';
import './index.less';
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 { reqVerifyCode, reqLogin } from '../../api';export default class Login extends Component {state = {phone: '',verifyCode: '',time: 60,canClick: true,};saveData = (type) => {return (value) => {// react-mobile的Input没有返回event事件对象// 直接返回的用户输入值,所以可以将event修改成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;// 校验手机号// alert提示用户体验性不好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);const result = await reqVerifyCode(phone);console.log(result);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); }render() {const { canClick, time, phone, verifyCode } = this.state;return (<div className='login'><NavBar mode='light'>手机验证码登录</NavBar>{/* 手机号码输入框 */}<InputItem clear placeholder='请输入手机号' onChange={this.saveData('phone')} /><div className='verify-input'>{/* 验证码输入框 */}<InputItem clear placeholder='请输入验证码' onChange={this.saveData('verifyCode')} /><buttonclassName='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 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>);}
}

十三 token的工作原理

需要理解的概念有:

  • token令牌的作用

    • 进城->检查->不想每次盘问->给一个令牌
    • 进城->有令牌->直接进
  • token存储的位置:cookie、header、data等
  • chrome插件的应用:EditThisCookie(https://chrome.google.com/webstore/search/cookies)

十四 完成个人中心

需要理解的概念有:

  • 统一接口的封装
  • 请求接口的调用

1.获取Token认证信息

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');

pages/userCenter/index.js

import React, { Component } from 'react';
import { reqVerifyToken } from '../../api';export default class UserCenter extends Component {async componentDidMount() {const result = await reqVerifyToken();console.log(result);const { code } = result;if (code !== 20000) {this.props.history.replace('/login');}}render() {return <div>UserCenter</div>;}
}

2.完善界面与信息

import React, { Component } from 'react';
import { Toast, NavBar, Button } from 'antd-mobile';
import { reqVerifyToken } from '../../api';
import './index.less';export default class UserCenter extends Component {state = {username: '',phone: '',avatar: '',};async componentDidMount() {const result = await reqVerifyToken();const { code, message, data } = result;if (code !== 20000) {Toast.fail(message);this.props.history.replace('/login');} else {const { username, phone, avatar } = data;this.setState({ username, phone, avatar });}}render() {const { username, avatar } = this.state;return (<div className='user-info'><NavBar mode='light'>个人中心</NavBar><img className='avatar' src={avatar} alt='' /><div className='nick-name'>昵称:{username}</div><Button type='primary'>退出登录</Button></div>);}
}

十五 退出登录

需要理解的概念有:

  • 统一接口的封装
  • 请求接口的调用
  • 编程路由导航
//统一管理项目中所有的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 });
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 = {username: '',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 } = result;if (code !== 20000) {Toast.fail(message);this.props.history.replace('/login');} else {const { username, phone, avatar } = data;this.setState({ username, phone, avatar });}}render() {const { username, phone, avatar } = this.state;return (<div className='user-info'><NavBar mode='light'>个人中心</NavBar><img className='avatar' src={avatar} alt='' /><div className='nick-name'>昵称:{username}</div><Button onTouchEnd={this.logout} type='primary'>退出登录</Button></div>);}
}

十六 OAuth2.0授权原理

需要理解的概念有:

  • OAuth2.0授权原理

准备工作1:需要与第三方授权登录平台“签订协议”;

https://github.com/settings/developers

准备工作2:第三方授权登录平台给你

1.网站标识:6333111045394ed42a3e

2.网站的机密码:7f61efde4245785b0a6484c5bb1d2480a113e4e7

3.查看文档:https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps

4.请求地址测试:https://github.com/login/oauth/authorize?client_id=6333111045394ed42a3e(只需要传递client_id)

5.授权操作

6.获取查询授权码

7.利用postman进行token获取请求:access_token=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&scope=&token_type=bearer

8.获取授权平台用户信息

准备工作3:网站系统有几个

  1. xxx系统,http://localhost:3000,是前台网站系统,可以由jquery、react、vue等实现
  2. xxx系统后台服务器,http://localhost:5000,是网站后台接口系统,可以由node、java、php等实现
  3. 在xxx系统中,需要想法设法让用户点击第三方授权平台,并且携带网站的标识去点击(否则github会知道你是哪个网站吗?)
  4. 在第三方平台中,需要进行用户的登录授权操作,验证用户的信息,返回查询授权码(查询授权码只限于查询当前操作用户的信息),这一信息内容很重要,不能直接返回前台,所以需要将该信息内容返回到后台服务器中
  5. 后台服务器需要利用查询授权码+网站机密码+网站标识三样内容去与第三方授权登录平台进行对接
  6. 第三方授权登录平台将返回一个token口令给后台服务器,但该token与我们前台网站平台的token没有任何的关联关系,这个token是你找第三方授权平台的凭证
  7. 后台服务器利用token与第三方授权平台进行数据交互
  8. 最终第三方授权平台将用户信息返回到后台服务器
  9. 后台管理系统则可以将用户信息存储于自己的数据库当中,并且将改用户信息进行后台服务器系统的token口令的生成
  10. 携带上xxx系统token重新定向到前端的页面
  11. 前台网站最终确认是否登录成功

十七 完成第三方授权

需要理解的概念有:

  • 修改第三方授权的回调地址
  • 网站标识,机密码
  • 服务器返回前台网站的重定向地址

前端项目需要完成的工作很少,只需要进行授权地址定向就可以,其它都是后台开发实现。

pages/userCenter/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem, Toast } from 'antd-mobile';
import axios from 'axios';
import './index.less';
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 { reqVerifyCode, reqLogin } from '../../api';export default class Login extends Component {//github  授权地址githubLogin = () => {window.location.href = 'https://github.com/login/oauth/authorize?client_id=6333111045394ed42a3e';};render() {return(<div><img onTouchEnd={this.githubLogin} src={github} alt='' /><img src={qq} alt='' /><img src={wechat} alt='' /></div>)}
}

onetab 暴力猴 gofulpage jsonview snap 图片(批量下载)woppaly

VsRes6Lip1pIdER5LtfEtnsR186dd5&scope=&token_type=bearer

8.获取授权平台用户信息

准备工作3:网站系统有几个

  1. xxx系统,http://localhost:3000,是前台网站系统,可以由jquery、react、vue等实现
  2. xxx系统后台服务器,http://localhost:5000,是网站后台接口系统,可以由node、java、php等实现
  3. 在xxx系统中,需要想法设法让用户点击第三方授权平台,并且携带网站的标识去点击(否则github会知道你是哪个网站吗?)
  4. 在第三方平台中,需要进行用户的登录授权操作,验证用户的信息,返回查询授权码(查询授权码只限于查询当前操作用户的信息),这一信息内容很重要,不能直接返回前台,所以需要将该信息内容返回到后台服务器中
  5. 后台服务器需要利用查询授权码+网站机密码+网站标识三样内容去与第三方授权登录平台进行对接
  6. 第三方授权登录平台将返回一个token口令给后台服务器,但该token与我们前台网站平台的token没有任何的关联关系,这个token是你找第三方授权平台的凭证
  7. 后台服务器利用token与第三方授权平台进行数据交互
  8. 最终第三方授权平台将用户信息返回到后台服务器
  9. 后台管理系统则可以将用户信息存储于自己的数据库当中,并且将改用户信息进行后台服务器系统的token口令的生成
  10. 携带上xxx系统token重新定向到前端的页面
  11. 前台网站最终确认是否登录成功

十七 完成第三方授权

需要理解的概念有:

  • 修改第三方授权的回调地址
  • 网站标识,机密码
  • 服务器返回前台网站的重定向地址

[外链图片转存中…(img-Rm69V5Pk-1624176080970)] [外链图片转存中…(img-ZfEcI7yP-1624176080971)]

前端项目需要完成的工作很少,只需要进行授权地址定向就可以,其它都是后台开发实现。

pages/userCenter/index.js

import React, { Component } from 'react';
import { NavBar, Button, InputItem, Toast } from 'antd-mobile';
import axios from 'axios';
import './index.less';
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 { reqVerifyCode, reqLogin } from '../../api';export default class Login extends Component {//github  授权地址githubLogin = () => {window.location.href = 'https://github.com/login/oauth/authorize?client_id=6333111045394ed42a3e';};render() {return(<div><img onTouchEnd={this.githubLogin} src={github} alt='' /><img src={qq} alt='' /><img src={wechat} alt='' /></div>)}
}

[外链图片转存中…(img-cfoLfM3L-1624176080972)]

onetab 暴力猴 gofulpage jsonview snap 图片(批量下载)woppaly

react-移动端项目相关推荐

  1. 用vite命令搭个react移动端项目,实现canvas碰撞效果(按需导入antd-mobile,pxtorem适配)

    前言 最近看见大家都在卷react源码,突然就心慌了.但是自己的操作水平还有待提高,现在看源码也需要循序渐进的,打算还是从写代码慢慢理解功能再去看源码.所以就尝试使用vite这个构建工具进行尝试构建一 ...

  2. React 移动端项目之pdf预览

    因为项目需要,需要在在项目中实现pdf文件遇见功能,众所周知,安卓老大哥貌似不怎么支持,所以采用的react-pdf插件实现方式,实现方式很简单: 一:引入react-pdf包: yarn add r ...

  3. 7 款最棒的 React 移动端 UI 组件库 - 特别针对国内使用场景推荐

    本文完整版:<7 款最棒的 React 移动端 UI 组件库 - 特别针对国内使用场景推荐> React 移动端 UI 组件库 1. Taro UI for React - 京东出品,多端 ...

  4. 手把手带你用next搭建一个完善的react服务端渲染项目(集成antd、redux、样式解决方案)

    前言 本文参考了慕课网jokcy老师的React16.8+Next.js+Koa2开发Github全栈项目,也算是做个笔记吧. 源码地址 github.com/sl1673495/n- 介绍 Next ...

  5. React服务端渲染(SSR)入门及项目搭建

    代码已经关联到github: 链接地址 文章有更新也会优先在这,觉得不错可以顺手点个star,这里会持续分享自己的开发经验(: 前言 服务端渲染是什么?我们什么情况下需要使用它?想要了解这些,需要简单 ...

  6. 用 TypeScript 编写一个 React 服务端渲染库(1)

    前言 代码都甩在 Github 上面了,欢迎随手 star ? 踩坑的过程大概都在 TypeScript + Webpack + Koa 搭建 React 服务端渲染 这篇文章里面 踩坑的 DEMO ...

  7. React服务端渲染实现(基于Dva)

    React服务端渲染实现 (基于Dva) 功能 基于 Dva 的 SSR 解决方案 (react-router-v4, redux, redux-saga) 支持 Dynamic Import (不再 ...

  8. react 技术栈项目轻量化方案调研

    react 技术栈项目轻量化方案调研 团队的新项目,无论是pc端的还是移动端的,都已全面转移到了 react 的技术栈. 然而,对移动端来说,react 框架脚本的体量还是有些偏大. 在后续项目比较成 ...

  9. ssr Android简书,react服务端渲染ssr

    Next.js 一个轻量级的 React 服务端渲染框架 1 概念 SPA single page application : 单页面应用程序 缺点:首屏加载慢,不利于SEO SSR Server-s ...

最新文章

  1. 关于UnimplementedError: Fused conv implementation does not support grouped convolutions for now.报错
  2. websocket并发性测试
  3. 通过 .gitlab-ci.yml配置任务-官方配置文件翻译
  4. unity 获得所有的tag_Unity3D_06_根据Transform、GameObject和Tag获取子对象集合
  5. Leetcode(20210412-20210418 第一周 每日一题)
  6. 注释和简单用户交互程序
  7. 闲鱼无障碍是怎么在端侧实现的
  8. C#中lock关键字的用法
  9. 2012.4.16总结(二)
  10. Windows10安装Gooey
  11. [JavaScript]20个优秀的Javascript导航技术
  12. Github上十大C#开源项目排行榜
  13. 聊天软件开发_3_数据库表设计
  14. 回旋共振 有效质量的测量
  15. js ajax 401,$ .ajax请求总是401(UNAUTHORIZED)
  16. 银河帝国----基地与帝国
  17. Android下的特殊文件夹
  18. woj 1537 Stones I
  19. ESP8266Wi-Fi数据通讯
  20. java勇者大冒险_文字冒险页面游戏-java用到了类,循环等

热门文章

  1. electron 解压zip_node.js实现简单的压缩/解压缩功能示例
  2. hive币2021年预计涨到多少_星时代云播|IPFS/Filecoin的价值被严重低估, FIL能不能涨到300美金?...
  3. 深度学习框架zf_谈谈深度学习框架的数据排布
  4. ap心理可以用计算机吗,AP考试哪些科目需要使用计算器
  5. DCGAN:生成动漫头像
  6. JS-面向对象-原形对象链(自定义对象实例原形对象链 / 本地对象原形对象链)
  7. Dubbo入门之hello world(zookeeper做注册中心)
  8. 小汤学编程之JAVA基础day01——JAVA基本概念、第一个JAVA程序
  9. Centos7通过yum安装jdk8
  10. Django 第八课 3.【MySQL问题】