干货屋是用来放置网校视频和课件的一个H5,主要开发人员有一个前端一个后端。项目分为前后台,整体逻辑不太复杂(但是有一些细节的逻辑还是挺有意思),但是是第一次做后台,也是第一次真正的和后端的同学有交流,收获还是挺大的,因此进行项目复盘,记录一些细节。
拿到项目的第一天干劲十足,但是有了上次拿着视觉图就冲导致后面逻辑大改的教训,这一次没有拿着原型图就从上往下开始堆代码,而是粗略地思考了一下实现方案之后再去做,比如首页的内容有一个tabBar、一个导航栏,和一个展示区,我就决定将他们分为三个组件,然后配置路由,点进去之后的视频详情通过路由传参进行数据匹配。
收藏、历史记录和登录功能因为之前没和后端交流过,以为这是前端要做的,没有实现方案,接项目之后心里其实还是挺紧张的,但是后面实际只用掉后端的接口就解决了。这就是当时大致的构思,现在回想起来感觉做得还是蛮正确的,不这样的话后面处理一些逻辑可能会花费大量时间。虽然随着项目的深入,经验的不断积累,现在回想起来还是有不少地方可以优化。
构思完之后,就开始搭项目了,因为前台是一个移动端H5,所以一定要适配,有了上次做红岩迎新H5适配出大问题的惨痛教训,这一次就决定先把一些配置弄好,其中最关键的就是postcss-pxtorem,这一次原本想选这个适配方案,但是奈何这个配置花费了将近一整个晚自习的时间(8:00-10:30),当时还是挺沮丧的,原本信心满满,给自己第一天布置的任务是完成配置并把tabbar和导航写好并把路由注册了,但是这下连第一步都没迈出,后面我去找学长远程帮我配环境,结果学长直接叫我clone他配好的模板,一拿来npm i,调了下参数,果然马上就好起来了,虽然这样成功了,但是还是挺失落的,没有成就感,因为啥也没学到,下次叫继续开发移动端H5还是得clone学长的模板,因此我便暗下决心,一定要写一个我自己的模板(项目收尾之后就写!),后面这个模板其实还帮了我很多,直到我做后台(没用模板)的时候部署项目的时候才知道。后来11点半回到宿舍洗完澡,仍然雄心壮志,打算完成今日目标,结果不料室友买了烧烤回来吃,东吃一家西吃一家就十二点多了,匆匆坐下开搞,最后当天睡觉前完成了导航栏的制作,导航栏是一个可以左右拖动的组件,点击能切换状态并跳转路由。下面总结一下导航栏上遇到的坑。
因为是项目第一个做的组件,我最初的想法是用hooks+tsx来写,因为hooks学得不精,边查边写(“使用hooks模拟生命周期”是我用hooks时搜索得最多的问题,其实我还是没有摆脱类式组件思想的束缚)。可谓是一路碰壁(写完之后感觉hooks其实也没有大家说得那么厉害,虽然确实很轻,小组件用真的合适,但是功能多的组件用hooks就感觉不太好使了),最后总结出不是所有的组件都要用hooks来做,习惯了类式组件就用类式组件,没必要跟风。先开始又用的是tsx,也是一路爆红加警告,一想到后面要用gitlab触发自动部署,有警告都部署不上去,因此直接丢掉了用tsx的念头,马上改文件名后缀,回到了最原始的模样(下次做项目一定一定要用到tsx了啊,不管怎么样)。
写导航栏时还遇到了一个超大坑,就是在路由注册和跳转时,我发现引入withRouter等一些组件居然会爆红,提示称withRouter不存在,我以为是选的库错了,就查资料翻笔记,发现没错,束手无策了,就直接复制报错信息在overflowStack里,发现竟是新版本的react-router-dom中删去了他们,解决方案是下载旧版本的react-router-dom,后面版本一退,果然就好了,因此下次开发的时候,导入的react-router-dom一定要带版本号@5.0.0,不然用老版本的方案会报错。
导航栏还有一个可以说的点,就是我最开始的构想是用redux来存储状态,解决跳转路由时无法使用pubsub传参数做标识的问题,现在回想起来这完全是多余的,导航栏和展示区同时共存,pubsub是可以使用,只有组件没有挂载时,使用pubsub传参是不可取的,就要用到redux了。这里用redux就略有点多余,直接点击之后pubsub传索引给展示区就好,展示区直接请求数据,拿着索引进行部门筛选就可以了(因为对应部门要展示对应部门的信息,而我的逻辑是把所有数据放在一个接口里)。
这里的一级筛选实在太冗杂了。下次一定不到万不得已的时候不用redux。
接着就记录一下导航栏的不太容易的实现逻辑吧,一个长条可拖动的能跳转路由的导航栏。
导航栏两种状态,选中和未选中,选中走第一个,未选中走第二个。
点击事件,点击时将所有状态改为false,接着让选中的状态变为true(通过传索引辨识所点的按钮),这里有个点就是修改状态的方法是拿着整个原始状态的副本在改,最后用改过的副本重新去更新状态,感觉有点冗杂了,但是没有别的方案修改单独的其中一项。
css的拖动是这样实现的,设置一个百分百宽度的条在最外层,然后使用overflow:scroll,最后内部设置一个长条,长条就可以拖动了
接着后买你就做了展示区的逻辑,不一部分不太难,先写固定数据,然后替换为真实的模拟数据,然后把模拟数据交给后端,他就可以给出和模拟数据一模一样的数据,直接套上去就可以。
这里是先拿到数据再做的一级筛选。
然后就做了底部导航栏,底部导航栏就两个状态切换,但是要切换的图片有四张(一个状态两张),文字样式也要对应修改,因此用了5个状态,两个用来存图片(数组)、两个用来存样式(行内样式)、一个用来存索引(对应图片数组)
点击之后的改变为:
接着就写我的界面,静态界面和之前的逻辑大体一致。区别就在于我的中有几个状态的划分,一个是未登录/游客模式,一个是已登录模式,已登录模式在我的界面中会展示历史记录和视频,且收藏功能能够正常使用,游客/未登录界面就会显示空空如也。这里区分登没登陆看的就是有无token。
登录、观看记录,和我的收藏这几个功能就不太好实现,这里详细说明一下。
登录使用的是微信登陆,原理就是点击绑定账号后,计入登录流程,登录流程为用户授权=>跳转第三方微信界面=>拿到token,重定向回页面。
// 登录流程
const token = document.location.href.split('token=')[1];
let today = Date.parse(new Date())
if (today - localStorage.getItem('time') > 2*60*60*1000) {
let date = Date.parse(new Date())
console.log(date)
localStorage.setItem('time', date)
window.location.href =
'https://be-prod.redrock.cqupt.edu.cn/magicloop-wx/auth/enter/yorozuya?origin=' +
encodeURI('https://fe-prod.redrock.cqupt.edu.cn/redrock-house') +
'&scope=student';
}
if (token && token.length) {
document.location.hash = '#/';
localStorage.setItem('id_token', token.replace(/%20/g, '+').replace(/#[\w/]*/g, ''));
}
if (
(!localStorage.getItem('id_token') ||
parseInt(localStorage.getItem('exp')) <
Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 2) &&
!token
) {
let date = Date.parse(new Date())
console.log(date)
localStorage.setItem('time', date)
window.location.href =
'https://be-prod.redrock.cqupt.edu.cn/magicloop-wx/auth/enter/yorozuya?origin=' +
encodeURI('https://fe-prod.redrock.cqupt.edu.cn/redrock-house') +
'&scope=student';
}
值得注意的是,需要到微信开放平台提前进行注册,让网页得到权限。
之后带着token发送请求就可以了,有一个token鉴权的过程。
我的收藏和历史记录要实现,历史记录就需要在点击视频的时候发送一次post请求,收藏记录就需要在点击收藏按钮之后发送post请求,后端就会做一个数据处理。然后请求历史记录和收藏对应的接口就可以拿到数据了。
历史记录接口,点击之后会更新历史记录的get接口
收藏接口,每发送一次后端会对收藏状态取反,直接就可以用
接着就是视频详情界面,是通过路由传参实现数据的一二级过滤(部门为一级过滤,id为二级过滤)。
还有值得一提的就是收藏按钮,收藏按钮在收藏成功后会弹窗收藏成功,收藏失败也会提示去登录。这里的弹窗也重新创了一个Modal组件,通过辨识collectState的状态来展示。
前台最后还有值得一提的就是封装了一个请求的方法,就是之前的api,其实实际效果就是封装了一个baseUrl在请求路径中,同时能让思路更加清晰。
归根结底也还是axios在请求。
前台的逻辑大致就是如此,并未遇到太多卡点。
接着记录后台的逻辑。
后台没有视觉图(可以自由发挥了),就直接引入的antd,连css都没咋用,这次后台开发让我了解了更多的antd知识(确实好用!),同时还让我明白了前后台是怎么交流的。说实在的,后台的基础页面搭建很顺利,直接导入antd的各种组件就好,卡点在于使用后端同学给的接口,因为是第一次和后端的同学交接,人家后端的同学又是开发经验非常丰富,所以在沟通的过程当中唯唯诺诺,用不来接口连多问人家几次的勇气也没有,还好人家比较耐心,回答了我很多次仍然还在解答。后台的卡点最主要的卡在了上传表单那里,使用antd的Upload组件上传图片时,出现了跨域的情况,后端的同学给的接口就是处理那个的,但是当时我并不知道,后端的同学就说是先get一次,post一次,拿到链接,最后投稿的时候带着链接和数据一起上传就可以了,但是我当时并没有明白那个get和post接口的作用,自己反复摸索了好久,后来才知道那是图床接口,get之后会拿到一个链接,post之后图片/视频文件就变为了那串链接,接着明白了之后又继续摸索,投稿时也反复测了好几次,都是报的参数错误,后面问了后端才知道要带着那串链接并用raw格式上传(学到了很多!)。
图床接口的使用
投稿接口
还有一个值得记录的地方就是在做修改课程接口时,出现了表单初始值不显示的问题,排查之后才发现是传过来的initValue是异步请求过来的,在组件渲染时,异步请求返回的数据并未到达,因此为undefined,所以拿不到结果,因此采用了异步渲染组件的方法。
异步渲染组件