React + Koa2打造『官方管理后台』10 总结
一.项目架构
(1).前端
(2).后端
项目架构没有变化增加了如下:
1.err_config 负责返回各个请求成功的状态码及失败的状态码
2.控制器中分别多出了admin.js和index.js用于调用接收传递的参数且调用各个业务接口,返回结果至前端
3.routes 多了admin.js与index.js两个路由
二.关键技术点
(1).React中是怎么保存数据的
react通过constructor来保存数据的,
获取数据通过this.state.xx进行访问
修改数据通过this.setState{}进行修改
第一种 传入新的 state 对象
this.setState({msg: "我被改变了"});
第二种 传入回调函数,并在回调函数里面返回新的 state 对象
handleClick(index) {this.setState(state => {state.list[index].isLike = !state.list[index].isLike;return {list: state.list};});}this.setState(state => ({ msg: "hello world" }));
两种的区别
当通过对象传入的时候如果一个函数中有两个对象传入则出于性能方面考虑,将多次setState调用合并为一次,所以并不会直接对每次调用都更新。如果传入的对象都有相同的key,那么最后一次传入的对象的那个key值会是最终值。
当通过函数传递时的时候如果一个函数中有两个函数传入React则会按照各个setState的调用顺序,将他们放入到一个队列中,然后在更新的时候时会将上一个调用结束时产生的state传入到下一个setState中。
也就是说如果在一个函数中想要连续的通过setState来操作同一个key的属性就必须要传入参数
此外
setState 方法还提供一个可选的参数 callback ,即一个回调函数,会在当前调用 setState 方法更新状态后进行调用
Component.prototype.setState = function (partialState, callback) {// ...
};
(2).React中是怎么做到事件传递的
react的事件传递为在父组件中引入子组件,在子组件的标签上 事件名={事件名}
子组件通过props接收即可
这其中牵扯到this指向的问题
- 第一种是通过bind强行绑定
- 第二种箭头函数拿的是父级的作用域
- 第三种直接绑定箭头函数执行的方式,是在点击的时候执行的,点击谁this就指向谁
丢失问题:是因为函数作为参数时一定会造成this丢失,会打印undefined
<TableBody collectionData = {collectionData} onStatusClick = {this.onStatusClick.bind(this)}></TableBody>
(3).React是怎么做到父子组件引用且最后渲染到页面上的
子页面上通过抛出
export default class xxx extends Componet{render(){return(xxxx)}
}
父页面上通过引入
import xxfrom 'xx'
render(){return(<xxx></xx>)
}
(4).脚手架工具
安装脚手架工具
npm i create-react-app -g
建立文件,在文件中新建项目
create-react-app todo
启动方式
npm start
(5).Redis是怎么在React中使用的
安装好redis依赖的包后通过redis.createClient来创建链接,再封装redis的get set方法抛出使用即可
(6).ejs有什么用
1.用JavaScript创建HTML字符串 ,在JavaScript中拼字符串的缺点是可维护性不好。而使用模板可以让你通过代码的空行和缩进来清楚地展现出你的HTML。
2.基于WebService的AJAX网站开发 EJS可以接收WebService异步传送过来的JSON格式的数据,将这种数据直接传入你的模板里,然后将结果插入到你的页面中。你所需要做的只是通过以下代码:
new EJS({url: 'comments.ejs'}).update('element_id', '/comments.json')
(7).如何区分生产环境与开发环境的配置
通过process.env.NODE_ENV来判断true为开发环境
- Dev:开发环境,该环境下的配置项只影响开发人员本地代码配置,在项目初期代码本地编写时调试使用
- Test:测试环境,该环境配置影响整个团队的测试环境
- Production:正式生产环境,程序最终发布后所需要的参数配置
const ENV = process.env.NODE_ENV;
module.exports={isDev:ENV === 'dev',isPrd:ENV === 'production'
}
(8).是怎么实现密码加密的
通过crypto进行加密node自带的
crypto = require('crypto')
function makeCrypto(str){const _md5 = crypto.createHash('md5'),content = `str=${str}&secret=${cryptoSecret}`;return _md5.update(content).digest('hex')
}
const {adminInfo} = require('../config/config'),{addAdmin} = require('../service/admin'),{makeCrypto} = require('../lib/util');
class Admin {async createAdmin(){adminInfo.password = makeCrypto(adminInfo.password)const result = await addAdmin(adminInfo);if(result){console.log(0);}else{console.log(1);}}
}
(9).如何在react中配置别名
node_modules\react-scripts\config\webpack.config.js搜索alias
path.appSrc为根路径
alias: {// Support React Native Web// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/'react-native': 'react-native-web',// Allows for better profiling with ReactDevTools...(isEnvProductionProfile && {'react-dom$': 'react-dom/profiling','scheduler/tracing': 'scheduler/tracing-profiling',}),...(modules.webpackAliases || {}),'@':path.appSrc,'pages':path.appSrc+'/pages','components':path.appSrc+'/components','assets':path.appSrc+'/assets','services':path.appSrc+'/services','utils':path.appSrc+'/utils'},
(10).如何创建react路由
创建普通路由
react路由需要先安装react-router
在appjs中引入 BrowserRouter和Route,Wwitch
在return()中通过固定格式编写 component来指定页面 path来指定路径
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom'
import { Route, Switch, NavLink, Link } from 'react-router-dom'
import IndexPage from './pages/index'
import LoginPage from './pages/login'function App() {return (<Router><Switch><Route component={LoginPage} path="/login"></Route><Route component={IndexPage} path="/index"></Route></Switch></Router>);
}export default App;
创建子路由
通过render={props =(<父组件>....)}
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom'
import { Route, Switch, NavLink, Link } from 'react-router-dom'
import IndexPage from './pages/index'
import LoginPage from './pages/login'import DetailPage from './pages/sub/detail'
import ListPage from './pages/sub/list'
function App() {return (<Router><Switch><Route component={LoginPage} path="/login"></Route><Route path="/index" render={ props =>(<IndexPage><Switch><Route component={DetailPage} path="/sub/detail"></Route><Route component={ListPage} path="/sub/list"></Route></Switch></IndexPage>)}/></Switch></Router>);
}export default App;
(11).项目的各个功能点页面是怎么编写的
各个功能的页面通过组件拆分的方式编写,以首页为例,分为三个组件分别为头部组件 左侧导航组件 和列表内容页组件,其中内容页组件又拆分为主组件 内容组件
(12).axios如何请求数据
首先需要下载axios包
引入后封装方法如下
import axios from 'axios';
import qs from 'qs';
export default class HTTP{axiosPost(options){axios({url:options.url,method:'post',withCredentials:true,header:{'Content-Type':'application/x-www-form-urlencoded'},data: qs.stringify(options.data)}).then((res)=>{options.success(res.data)}).catch((err)=>{options.error(err)})}axiosGet(options){axios({url:options.url,withCredentials:true,method:'get',}).then((res)=>{options.success(res.data)}).catch((err)=>{options.error(err)})}
}
(13).koa2如何实现跨域请求
后端安装跨域
npm i koa2-cors -S
在服务端的app.js中引入
const cors = require('koa2-cors');
app.use(cors({origin:function(ctx){return 'http://localhost:3001'}
}))
(14).qs是做什么的
安装序列化data qs
将URL解析为对象
const Qs = require('qs'); let url = 'method=one&projectId=85&appToken=abc';Qs.parse(url);//输出结果{method:'one',projectId:'85',appToken:'abc'}
将对象解析序列化为url
npm i qs -Sconst Qs = require('qs'); let obj= { method: "one", projectId: "85", appToken: "abc"};Qs.stringify(obj);//结果method=one&projectId=85&appToken=abc
JSON.string ify与 qs.stringify 的区别
json.stringify{"uid":"cs11","pwd":"000000als","username":"cs11"}qs.stringifyuid=cs11&pwd=abc&username=cs11
(15).React如何做页面跳转
主动跳转
需要先引入Link 再通过<Link to={}>进行 跳转
import React,{Component} from 'react';
import {Link} from 'react-router-dom'
import './index.scss';
export default class NavItem extends Component{constructor(props){super(props)}render(){const {curIdx,index,dataItem,onNavItemClick} = this.props;return(<div className={['nav-item',index===curIdx?'nav-current':''].join(' ')}><Linkto={`/${dataItem.field}`}onClick={()=>onNavItemClick(dataItem,index)} >{dataItem.title}<i className="iconfont iconright"></i></Link></div>)}
}
被动跳转
通过history.push进行跳转
async loginCheck(){const result = await loginservice.loginCheck();const errorCode = result.error_code;const {history} = this.props;if(errorCode === 10006){history.push('/login')return}history.push('/course')}
这个history需要从APP.js向下传递
render={ props =>(<IndexPage history={props.history}>
children
代表获取组件的所有子节点可通过它和router结合进行子路由的跳转
(16).如何验证登录
通过ctx.request.body解析ajax请求传递过来的账号密码经过一些列的合法验证后调用登录接口
其中密码再通过makeCrypto进行进行加密
同时也通过logincheck方法来验证是否当前session已经存储
async loginCheck(ctx,next){if(ctx.session && ctx.session.userInfo){ctx.body = returnInfo(LOGIN.LOGIN_STATUS)return}ctx.body = returnInfo(LOGIN.NOT_LOGIN_STATUS)}
在index.js的componentDidMount钩子函数中调用
(17).如何跨域设置cookie
前端的ajax请求中增加 withCredentials:true设置为使跨域请求提供凭据信息
后端设置app.usecros的地方也要设置withCredentials:true
(18).接口权限验证怎么做的
通过异步函数的ctx.session 和next即可实现,当检测到ctx.session存在时 则执行next 函数。将此方法封装起来,在node的router中的第二个参数中加入此方法,即可拦截执行控制器
const {returnInfo} = require('../lib/util'),{LOGIN} = require('../config/err_config');
module.exports = async(ctx,next)=>{if(ctx.session.userInfo){await next();return}ctx.body = returnInfo(LOGIN.NOT_LOGIN_STATUS)
}
router.get('/get_courses',loginCheck,indexController.getCourseData)
(19).如何进行组件化/如何抽离公共组件
组件化:
1.根据页面进行分割
2.再通过不同功能点进行分割
公共组件:
将头部和下滑栏等组件抽离再在各个功能点的index.js中引入即可
(20).组件间如何完成数据联动
父组件中有该数据 以及修改该数据的方法
将这两个数据传递到子组件,子组件通过数据进行渲染,事件也进行绑定
当子组件事件触发将index传入父组件方法,父组件进行数据修改重新setState
(21).如何解决爬取第二页的问题
如果传递过来的爬虫文件中参数有等于course的则通过
waitForSelector
click
waitFor
await pg.evaluate(options.callback);
再循环把之前爬取的参数进行循环push即可,如下
let result = await pg.evaluate(options.callback);if(result && options.field === 'course'){await pg.waitForSelector('.page-btn.page-last')[0];await pg.click('.page-btn.page-last');await pg.waitFor(2000);const res = await pg.evaluate(options.callback);await pg.waitFor(2000);for(var i = 0; i < res.length; i++){await result.push(res[i]);}}
三..主要功能点实现
(1).你是怎么实现整个登录/退出功能的
首先用户输入用户名密码,提交至后台,通过crypto进行密码加密在后台进行验证,通过后通过ctx.session进行检测,由于在app.js中的配置,会将传递过来的cookie进行redis存储,那么你在二次登录的时候由于在app.js中配置了store是reidis所以通过ctx.session直接去判断redis中是否有这个存储的session信息
redis存储用户信息非常的好的原因是可以支持多个服务器的负载均衡,而且性能好。
session 毕竟是直接存储在服务器上的,redis实际上是用于缓存,所以redis,更适合存储用户信息这一类数据
app.keys = sessionInfo.keys;
app.use(session({key:sessionInfo.name,prefix:sessionInfo.prefix,cookie:cookieInfo,stroe:koaRedis(redisInfo) //session上所有需要挂载的信息的存储工具是redis
}))
退出的时候删除ctx.session即可
async logoutAction(ctx,next){delete ctx.session.userInfo;ctx.body = returnInfo(LOGIN.LOGOUT_SUCCESS)}
(2).你是怎么样发出ajax请求,处理响应的
通过封装好的axios通过sq序列化参数,通过Koa-crose在app.js中设置跨域发出
所有的返回值全部封装起来进行返回,前端再判断返回的code是多少从而进行响应
(3).整个页面的固定文本你是怎么处理的
这个页面的提示框,表头等等信息全部封装为数组,在每一个应用页面进行遍历输出
(4).整个页面是怎么实现只切换下边的tab页面的
通过children参数和route标签进行实现,将所有子节点路由配置在indexpage下,这时如果点击某一个标签则会跳转至index.js页面将dom通过children进行传递 这事再通过提取的Continar组件进行渲染显示即可
四.编写原理
各个子组件在pages下编写,如果需要用到公共组件则调用components文件下的公共组件即可,这时各个子组件再进行详细的拆分书写,最终返回给pages下根目录的组件中而在appjs中再调用根目录下的组件
五.项目心得
此项目主要难点为组件之间的拆分,axios跨域的配置请求,登录的信息存储,以及route的路由配置,api的拦截
React + Koa2打造『官方管理后台』10 总结相关推荐
- 【工程师综合项目二】React + Koa2打造『JS++官网管理后台』
Redis认知.安装与操作 MongoDB:动态数据库,如游戏中需要频繁地保存人物的坐标 Oracle:收费,企业级 mac要安装homebrew(包管理工具) window安装Redis程序运行教程 ...
- 『墨菲安全』10 分钟对心爱的 GitLab 代码仓库来一次全量体检
文章目录 方案调研 方案一(放弃) 方案二(成功) 脚本流程 增量检测 GitLab 配置 项目地址 去年 log4j 漏洞爆发时候就已经很痛苦了,当时把所有的线上服务排查了一遍.没想到这都已经过去3 ...
- 猿创征文|『编程与创作』10款颜值颇高的宝藏工具
- Voyager,最方便的lavavel admin管理后台
今天飞哥推荐1个最简最省心的管理后台,10分钟搭建1个功能齐全的管理后台,开箱即用,如果你在寻找方便的管理数据的管理后台,Voyager在github有11.2K star,值得推荐. 需求背景 开源 ...
- [日推荐]『健康管理』为你的健康保驾护航
2019独角兽企业重金招聘Python工程师标准>>> 健康一直以来是大家最为关注的话题之一,毕竟"身体是革命的本钱",下面小编要推荐的就是一款很实用的身体健康管 ...
- 七夕特辑|ShardingSphere 官方『真爱指南』大公开!
ShardingSphere Lovers' Day 浪漫钟声响起 七夕佳节来袭 特此隆重推出 ShardingSphere 粉丝专属 『真爱指南』 自测下你中了几条 来解锁甜蜜超能力! 一起感受来自 ...
- 基于VUE+DJANGO开发的前后端分离的官方网站系统带管理后台
前言 每个企业都有开发一个官方网站的需求,用于展示企业的产品和服务,企业的文化宗旨和品牌形象等,并要求网站有比较强的自由定制的功能.为此,我开发了这款官方网站系统,自带轻便的管理后台,在后台简单修改下 ...
- 如何三分钟为小程序打造管理后台
云开发扩展能力介绍 云开发扩展能力是云开发团队为开发者提供的一站式云端服务,旨在降低开发者使用云服务的门槛,助力开发者快速开发应用.目前已经对外支持的有图像处理.图像安全审核.图像盲水印.图像标签等. ...
- [日推荐]『讯飞快读』人工智能高效管理时间
科大讯飞大家都不陌生,去年锤子发布会上着实的火了一把,科大讯飞专业从事智能语音及语言技术等方面的研究,语音技术实现了人机语音交互,使人与机器之间沟通变得像人与人沟通一样简单.语音技术主要包括语音合成和 ...
最新文章
- Windows 7中200M神秘隐藏分区
- 【深度学习笔记】分类指标accuracy,recall,precision等的区别
- weboffice 应用
- linux中怎么安装ded包_快速提示:如何在Linux中安装.deb和.tar文件 - push博客
- 「分块」数列分块入门1 – 9
- codeforces problem 768B
- Kafka 慌了!这个中间件,要火了?
- python中import文件_Python导入其他文件中的.py文件 即模块
- 有向图的强连通分量--Tarjan算法---代码分析
- 【SCIRLab】ACL20 基于图注意力网络的多粒度机器阅读理解文档建模
- 手机操作系统学习总结
- arcgis两点之间连线_three3D地图设置两点之间的连线
- Mac制作Windows 10 U盘启动盘
- Lookup函数的使用方法介绍(含VLookup和HLookup)
- Default changeset implementation allows only one operation
- MaNGOS工程概介
- execl 如何同时冻结一行与一列
- html 下拉框搜索,下拉框搜索功能的实现
- Perl之单行命令特技
- windows更新错误0x8024401c
热门文章
- 光流传感器不是一个到手就能用的PIX外设
- Nexus私服(三)
- PPT插件(islide)
- navicat执行sql文件报错:1840-@@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.
- php wps导入数据库,wps excel表格怎么导入数据库-如何把excel表格导入wps
- T检验 ANOVA
- Parallel Data Augmentation for Formality Style Transfer翻译
- 在OPPO应用市场内如何做ASO优化
- 帧率(FPS)计算的几种方法总结
- 2022年创新药行业研究报告