一、日常工作
1)开会
由于公司规模并不大,因此一有事情就会拉个会议,例如需要大会、技术评审、汇报周会、突发会议等。一周中大概有20%~30%的时间会花在大大小小的会议上。
公司会议难免会出现接锅的情况,虽然自己不想做锅靖,但是有些边界定义比较模糊的锅(既可以给后端,也可以给前端),在时间允许的时候,都会自己接下,因为服务端资源很有限,日常杂活太多,大家打工人都不容易。
虽然比较费时,但会让自己有更多的参与感。公司办公都是用mac pro最新款,携带也比较方便,开会的时候带个笔记本,也可以在会议中处理一些工作的事情。
在团队成员来齐后,每周五的10点,自己还会组织一个团队短会,一般控制在30分钟内。不是工作汇报,而是:
- 回顾一下本周遇到的难点或费时的事情,若还未解决,可在会议上当场讨论思路。避免下次碰到又要花费太多精力。
- 发表一下自己这周遇到的困惑,无论是技术还是公司,都可以随意发表。互相了解一下团队成员的想法。
- 我也会传达一些公司信息,例如本周或下周的重点任务、公司的新政策等,及时与团队成员同步。
- 若有时间还会做个简短的 Code Review,或者做技术分享,无论是当前前沿技术,或者是经典案例,都行。
2)任务分配
目前公司两条产品线,加上我是3个人,他们各自负责一条线。若一些需求没时间做的时候,我也会帮忙写。
并且与组内成员要保持任务信息的同步,大家都得清楚现在做什么,未来还有哪些活需要处理。
新同学刚来没多久,还在适应期,给他的任务都不是紧急的。当抽不出人手时,也会让他搞,不过我和另一个同学也会协助他。
当有紧急需求时,我会将优先级不高的任务延后,并安排给合适的人处理。
自己除了开会之外,也要写代码,一些难弄麻烦费时的功能都留给了自己,例如那些跨团队的、或是测试环境不容易部署的功能。
每天也会关注一下组员的进度,遇到问题可以帮忙出解决方案或应对策略,以免卡在一个地方,既影响心情,也影响开发进度。
3)错误调试
我们Web小组不仅维护着一个庞大的后台管理系统(前后台都做),还参与过许多后端服务(例如定时任务、金额结算)、H5活动页面、微信公众号、小程序等。
因此,很多时候线上出问题,都会与我们组有关。经常是某个人找到我,向我反馈问题,严重的话我就得停下手中工作,立刻去拍错。
我花了很多时间去做各种错误调试,并且经常会出现跨团队的场景,而我排错的过程一般是几步:
- 页面样式出错,那毫无疑问是我们组的问题,除非那人看错了,将客户端的页面报到了我们这边。
- 若是调用的接口问题(所有的前端接口都会通过Node服务做一层中转),那么会将接口路径拿到VS Code中做一次项目搜索,查看代码逻辑。
- 若内部核心逻辑是调用服务端内部接口,那么就需要将问题反馈给他们,再做联调和定位是谁的问题。
- 若接口中的逻辑没有调用内部接口,都是直接操作数据库的,那么就要查找数据库写入的逻辑。若没有,那么就去找谁在维护这张表。
之前就遇到过一个问题,说是后台的一张重要的统计报表没数据了。查了半天,发现是一个定时任务在做写入,而在这个定时任务中又会涉及MongoDB的写入、客户端的上报、第三方平台和服务端的读取。在一一排除后,最终发现是服务端在写入其中一个数据源时,日期写的不对(由于时区的问题)。断断续续花了4天的时间,期间与运维、客户端、服务器、测试还有第三方平台的技术人员先后做过沟通,这种排错很费成本。
后面又遇到一个问题,也是与后台有关,说是一张统计表看不到了。这次也是先查写入的代码,发现没有,马上与服务端联系,他们那边也没有。于是就找到测试组,因为他们组目前是公司内最了解整个管理系统的人,果然找对了人,他就说是AI算法组在维护的。找到他们之后,才查出是他们的一个定时任务挂了,怪不得没有数据。之后在周会上,我就提出让他们统计出自己维护的数据库表,并且是那些与我们组相关的数据库中的表。
4)完善文档
文档有两大关键的意义:
- 第一个是帮助梳理思路,消除个体对于事物的不确定性。
- 第二个是传递信息,消除团队中对于事物的不确定性。
先前的 WIKI 已有部分文档,但内容非常分散,不成体系,我花了点时间重新梳理,并整理到合适的位置。
书写之前没有的文档,尤其是现在比较重要的业务或功能,完善业务流程,甚至将相关的 SQL 查询语句都整理了出来,方便调试。
邀请其他组的成员帮忙书写相关的文档,例如让客户端组完善 JSBridge 文档,而测试组是目前掌握最多业务细节的人,让他们帮忙写些发生突发情况或意外故障的处理过程。
二、工作优化
日常的工作除了做业务需求(俗称写BUG)之外,其实就是发现问题,再解决问题,有时间的话,找出合理的优化手段,避免重复踩坑。
1)MongoDB可视化界面
若要查询MySQL,可以借助DMS可视化界面。但要查询MongoDB,那么只能先连上跳板机,然后再通过跳板机进入MongoDB服务器,这一来一回经常要耗上点时间。
因此,后面就着手自己写了一个简易的可视化界面,只提供查询功能。Node代码中的ORM系统采用的是Mongoose库,那么就从其find()方法着手。
界面中需要传入一个Model名称,多行文本中只要传入 where 条件即可,语法参考的是 db.collection.find(),下图灰色使用了 <pre> 标签,为了能在页面中显示空行和换行。
在后台管理系统中,原先有个查询redis的界面,但只查了一个库,数据类型也只列了4种,后面将库修改为可配,并且新增了一个数据类型。
从而就能避免通过跳板机来读取redis中的缓存,可直接在页面中查询。
脚本文件的执行,原先除了上传代码之外,还得进入服务器中,手动输入命令执行。
这么操作略微繁琐,自己做了点加工,做成半自动,代码还是要发,不过在后台管理系统中有个按钮,点击后就能执行指定的脚本,下面是执行接口,调用了 fork() 方法。
router.get('/scripts/exec', async (ctx) => { const path = require('path'); const { fork } = require('child_process'); const exactPath = path.resolve(__dirname, 'scripts/index.js'); fork(exactPath); ctx.body = { code: 0 }; });
2)微信公众号配置封装
调用微信公众号的JS-SDK之前,都是需要先引入配置文件的。先前的做法是先在前端脚请求一个接口,响应得到timestamp、noncestr和signature等参数。
然后调用wx.config()进行配置,最后在wx.ready()调用微信的onMenuShareTimeline()、onMenuShareAppMessage()等方法。
const wx = require("weixin-js-sdk"); const wxUrl = encodeURIComponent(location.href.split("#")[0]); getWeixin(wxUrl).then((e) => { wx.config({ // debug: true, appId: "wx....", // 必填,公众号的唯一标识 timestamp: e.timestamp, // 必填,生成签名的时间戳 nonceStr: e.noncestr, // 必填,生成签名的随机串 signature: e.signature, // 必填,签名 jsApiList: [ "onMenuShareTimeline", "onMenuShareAppMessage", ], }); }); wx.ready(function () { wx.onMenuShareTimeline({ title: "", desc: "", link: "", imgUrl: "", success: function () {}, }); wx.onMenuShareAppMessage({ title: "", desc: "", link: "", imgUrl: "", success: function () {}, }); });
一旦需要用到微信JS-SDK就要调用这么一坨,但其实这些可以都放到服务端去执行。
后面我将上述配置封装到后端的一个router方法中,路由成一个JS文件(例如bridge.js),前端只要调用它,然后传递一些参数做一些简单的变量声明即可,完全不需要配置appid、signature等参数。
3)后台管理系统界面优化
后台管理系统都会包含左边的菜单栏,刚接手的时候,每次页面刷新,菜单栏默认就会闭合,不会展开当前URL路径所处的菜单。
在调试的时候,改一下脚本就会自动刷新窗口,一刷新菜单就会闭合,导致我要换旁边的菜单时,又要一步步的点击。
于是就着手开始优化,在查看代码时发现,菜单数据被抽象成了多叉树结构,而选中某个页面的菜单,其实就是用深度优先算法来匹配指定路径,下面用代码简单的实现了这个功能。
//深度优先 搜索当前菜单 let defaultSelectedKeys = [], //默认选中的菜单 defaultOpenKeys = [], //默认需要展开的菜单路径 isFind = false; const dfs = (node, keys) => { keys.push(node.id); if(node.routers == props.path) { isFind = true; defaultSelectedKeys.push(node.id); defaultOpenKeys = keys; return; } node.children && node.children.forEach(value => { if(isFind) return; dfs(value, [...keys]); }); }; dfs(menuTree[0], []);
其实算法离我们平时的业务并不远,只要用心发现就能挖掘出各类算法。
4)发布系统问题
公司买了第三方平台的一套发布系统服务,每次发布时,都会比较耗时,卡在某一步,少则7、8分钟,多则10几、20多分钟。
向运维也反馈过很多次,但每次都没有找到针对性的解决办法。
最后无奈,只得和运维商量,在测试环境,自己写个文件传输工具,直接把build后的代码传到指定目录,跳过发布系统。
毕竟测试环境要经常发代码,老这么延迟,实在是会让人困扰。运维的意思是他们提供脚本,我们在跳板机上运行脚本,实现文件传输。
虽然没有一键传输方便,但聊胜于无,总比干等好,同意了这个方案,后续跟进。
期间还碰到了另一个棘手的问题,那就是测试和正式环境的Node版本过低,比当前稳定版本低了6个版本。
这就导致很多库不能直接安装,得另想办法或者用该库比较老的版本。
现在也不能立马更新,一更新就出错,测试也没有额外的资源帮忙一起调试。时间与资源的矛盾暂时还不能妥善解决。
5)性能分析日志
公司购买了阿里云的日志服务,之前几个纯前端的项目虽然开通了日志服务,但并没有妥善使用起来。
这次正好想到可以开通阿里云的WebTracking的采集功能,将性能参数推送到阿里云日志服务器中,参数搜集的库采用了之前自己封装的pineapple.js。
<head> <title>性能参数demo</title> <meta charset="utf-8"> <script> window.pineapple || (pineapple = {}); pineapple.param = { "token": "a0b923820dcc509a" }; </script> <script src="pineapple.js"></script> </head>
在参数搜集好后,就能借助阿里云强大的查询分析功能,分析页面性能了,目前还没想好分析维度。