- 我们是怎么进行前端工程化的
随着业务的不断发展,企鹅电竞的前端规模在不断的疯狂膨胀。越来越多的项目被建立起来,越来越多的体系被架构出来,越来越多的代码变的越来越难维护。我们在不断创造新代码的时候,不断在思考,为啥我们的项目看起来这么的野?我们不禁反复思考,我们干了那么久的前端工程师,我们他喵的到底在干个啥?很自然,有人就开始说,我们要推动前端工程化,那么我们在讨论前端工程化的时候,我们到底在讨论啥? 文章将阐述我们在前端工程化的一些思想和做的一些工具,文章并不主要介绍和推广我们的工具,我们更多的介绍的是我们业务发展过程中,遇到各种问题的时候,我们的思考和想法,以及我们采用的对策和措施。当然,如果大家有更好的想法,希望大家能贴在讨论区,我们一起讨论一波。 什么是前端工程化 我们在思考这个问题之前,我们先思考一个更大的问题,什么叫软件工程化?我依稀记得,在我还有头发的时候,在梧桐树旁的十五栋楼上,上课(睡觉)的日子里,那满脸褶皱的软件工程老师是这么说的: 将系统化的、严格约束的、可量化的方法应用于软件的开发、运行和维护,即将工程化应用于软件。 没记错的话是应该是清华大学出版社出版的《软件工程》这本书里的定义,回去如果我翻到了是哪本书,我再贴出来。不管,这里先套用一下,前端的工程化是不是应该是指: 将系统化的、严格约束的、可量化的方法应用于前端页面的开发、运行和维护,即将工程化应用于前端开发。 所以,当我们要讨论前端工程化的时候,是不是讨论的这么几个基本问题: 如何进行系统化的前端开发、运行和维护? 如何对前端的开发、运行和维护进行严格约束? 如何对前端的开发、运行和维护进行量化? 为了方便的进行行文,在此约定,下文的开发将指代开发、运行和维护。所以,在后面我们将就这三个问题,来谈谈我们对于前端工程化的思考和实践。 大部分情况下,我们在讨论前端工程化的时候,我们其实只讨论CI/CD和脚手架,但是软件开发并不是只有CI/CD,在不同的开发阶段,甚至在不同的项目阶段,面对不同的问题,我们都要采用不用的方案方法。里面有共性,也有个性。共性的问题,我们可以参考社区的解决方案,但是个性的问题,则需要我们自己去探索和思考。 为什么要进行前端工程化 ok,按照三段论的基本套路,我们应该要来讨论一下,为啥要进行前端的工程化? 如果你非要我吹逼的话,我会这样吹:世界的本质是熵增的,而生命的本质是熵减的。为了体现我们作为生命的伟大价值,我们将坚定不移的将负熵进行到各个角落。 说一个比较通俗的答案:当前的开发环境已经复杂到影响我们开心的进行下一步的开发,我们需要对当前的开发环境进行一次整理,而这个整理的过程,就是前端工程化的过程。 那我们现在的开发环境到底有多复杂? 截止到我写这篇文章的这一刻,我们一共要负责9大系统。各系统由于立项时间,定位,运行环境,产品要求等各种乱七八糟的原因,使用了不同的技术架构,不同的技术框架,不同的开发方式,不同的发布方式。更恐怖的是,由于技术的不断发展,框架的推陈出新,我们的项目还在不断的复杂中。。。 分别为:电竞官网(可以看直播的那种),电竞ipad版本(可以看直播的那种),电竞移动端(android 和 ios两端各70+场景页面,还有一堆的分享页面),电竞移动助手端(android和ios双端),电竞pc助手端(主播管理端),电竞运营管理端(内部数据管理,各种个性化需求和数据分析工具),电竞开放平台(提供给外部合作伙伴进行数据管理),公会管理系统(用于辅助公会进行主播管理),无以计数的活动页面。 所以,每次当有新同学加入我们的时候,我们都必须要提供多位资深员工,手把手的说明一下,我们项目到底要怎么跑起来,有的项目因为实在太复杂,已经快要跑不起来了:D 如何进行前端工程化 面对日益复杂的业务场景,日益庞复的代码逻辑我们开始思考我们到底要如何解决我们的前端工程化问题。根据前面我们的思考,我们将在以下三个方面来谈谈我们对于前端工程化的思考: 如何进行系统化的前端开发、运行和维护? 对于这个问题,我们首先思考的是什么是系统化?我们要怎么做才能称的上是系统化? 抛开那些高大上的术语,我们的解是,将日常开发的各个步骤整化为固定的流程,当我们在日常开发中,面对各种情况都会有固定的一二三四五的时候,就说明我们完成了系统化。 那么,我们每天都要做哪些工作呢? 收集一下大家的日常工作,都有这么一堆: [图片] 这些庞杂的工作有没有规律? 我们开始对我们的工作流程进行了一次收归。其实如果按照需求开发的基本流程,我们可以简单的把各种毛细的工作收归为以下四个阶段: [图片] 在不同的阶段,我们需要做不同的事情,而且,我们发现这些事情是重复的,或者说,是每次做需求的时候在相同的阶段都需要重复的。 在相同阶段里,做重复的事情,我们是不是能够自动化起来? 当然可以,我们开发一个流程管理工具是不是就可以了!在开发之前,我们看了一下公司内和业内在这个方向上的实践。这里列几个公司内宣传的比较好的方案:weflow、feflow、leah-cli 当然社区也有很多很不错的方案比如vue-cli,还有ng-cli等等,完成度相当高,非常的靠谱和好用。 但是,我们不难发现,这些cli也好,flow也好,都是基于工具的思路去实现的,和我们的思路并不一样。我们希望是能基于流程去实现的,而且能贯穿整个开发流程,形成一个闭环。让我们的同学能够系统化的去一二三就好了,不要思考太多有的没的。所以,我们基于上述的开源实现和思路,自己实现了一套新的方案:pgg-flow。 pgg-flow是以开发周期为核心,辅以各种工具库。举个栗子: 在需求的准备阶段,不管三七二十一,我们先来个pgg-flow start,然后开发分支就自动从主干拉出,并将本地环境切入到该分支。 在需求的开发阶段,来个pgg-flow dev,代码就跑起来了,dev-server,本地构建就跑起来了。 在需求的测试阶段,来个pgg-flow test,代码就提交远端,触发服务端ci/cd,测试环境立即生效就绪。 在需求的发布阶段,来个pgg-flow release,直接就触发ci/cd ,发起merge request(用于code review),生成ars单,触发发布系统流程,触发codecc的自动代码审查,并回收占用的各种资源。 我们希望开发同学,能够不用关心不同模块间的框架和依附系统差异。只要知道以上四个命令就能够完成所有的开发流程。 我们在CI/CD的思考和实践详情参考我们电竞小哥思名的这篇文章《weex/vue 应用的服务器端构建之路》,利用内部基于docker的orange-ci和发布系统,实现git工作流到外网发布的一条龙打通。 当然,在这个基础上,我们可以继续去支持,new page, new api等等更具体的业务辅助工具。通过如此,我们磨平了不同框架体系间的差异,大家可以很开心的进行需求开发了。但是,很快大家又有新问题来了,不同的框架中,不同的开发体系在相同的事情上,使用了不同的技术选型。比如,pc的同学自己基于fetch进行了封装,但是在h5上使用了axios来进行请求的。 这个主要是很多历史遗留,开发pc的和开发h5的在很长一段时间内都不是同一拨人,虽然在开发相同或者说相近的业务,但是,技术选型并不一致,这其实是之前遗留的一个很大的坑。 不管问题之前如何产生的,但是,就事论事,如何让大家的技术选型和技术实践相贴近? 如何让大家的技术选型和技术实践相贴近? 对于这个问题,我们的思考是这样的: 大家选型和实践有较大差异的原因是没有现成的方案,或者不知道有现成的方案。所以我们是从三个角度来解决这个问题的: 提供横跨框架场景的公共库,包含完整的基础方法 提供自动化更新的文档,帮助大家更好的了解基础方法有哪些 提供完善的脚手架,减小使用代价 公共库的管理是基于tnpm(公司内部的npm镜像)。 文档自动化,可以参考我们电竞一姐的这篇文章《企鹅电竞前端公共库文档自动化建设》。文章详细的介绍了,我们是如何使用蓝盾来进行文档自动化管理的。 脚手架的提供,为的是进一步减少重复工作量。 当我们完成以上的工作的时候,我们发现,我们已经基本实现了,系统化的进行前端的页面需求开发。当同学们开始一个需求的时候,可以很容易的借助既成的体系,进行快速的开发迭代。当然,现在这个工作模式还比较稚嫩,在很多细节上的地方还需要继续打磨。 为啥要采用相同或者相近的的技术实践?有个很大的原因是为了能够将优化的工作更容易的扩散出去。 比如,通过webpck4+babel7 的升级,一下就减少了移动端几乎所有页面的40%体积,具体可以参考思名的《weex/vue 应用的webpack4 升级优化之路》,《真·一行代码优化30%的JS bundle体积》。我们觉得优化应该倾向于底层,脱离于业务,只有如此,才能实现更高层级的复用。 如何对前端的开发、运行和维护进行严格约束? 在实现了系统化的进行开发和迭代之后,我们不得不开始面对开发质量的问题。如何对开发质量进行有效的约束和评估,这并不是一个简单的问题。我们最早的方案使用了业内比较通用的方案: 使用本地的eslint进行代码管控 通过eslint + husky + lint-staged来搭建一套基于本地的代码及风格管控,这个一个很棒的方式。业内对于这个方案也有很多很好的实践,大家自行百度一下就好。但是,这个方案对于我们而言,并不能解决所有的问题。 我们希望能对代码质量有一个整体的认识,我们希望能够对于代码质量的监控有一个比较好的客观衡量。 我们希望这个监控体系能够不阻塞大家正常的业务交付。 我们希望这个体系能够从各个维度对我们的代码进行合理的分析。 很明显,本地化的eslint根本不能满足我们对于代码质量监控的要求,就算,你在这个基础上增加prettier来做你的代码格式化的管理工具。于是我们开始寻找,是否有能够有一个平台来帮助我们完成这个工作。 如何优化的监控的前端代码质量 这个问题,我们最后使用了codecc+蓝盾的解决方案。将质量监控体系建设在远端,能够有效的对代码进行监控,同时,也能很容易的让更多的同学0成本的使用起来。有兴趣的话,大家可以参考我们电竞一姐的《如何优雅的监控前端代码质量》。 codecc是公司内部的在线代码自动审查平台,可以实现在线的代码eslint,coverity,重复性,复杂度等各种纬度的代码审查,能够将审查结果进行很好的分析和数据图形化。蓝盾是公司内部的流程集成系统,可以流水线化的实现代码的自动化构建和二三级子服务的连接。 通过蓝盾和codecc的体系,我们发现并解决了很多的问题,但是,很快,我们又发现了,单纯的依靠系统自动化的检查和检测,并不能解决所有的问题。有些设计思路上的问题,我们没办法简单的通过自动化的代码审查来解决。那么是否可以在必要的时候,引入人工的代码审查来进一步的提高代码质量。 在合适的时候进行code review 不能否认,cr是一个很重要,但是很烦人的工作。如何让大家对代码在合适的时候,进行cr,这个问题让我们思考了很久。我们尝试过很多的方案: 在发布流程中通过人工要求增加cr环境 以代码小课堂的形式,进行cr 在开发前后以技术评审的形式来进行cr 但是,这些方式有好有坏,但是,都不能够像水一样的融入到我们的开发生活中来。我们觉得不自然。我们最后的思考是: 将code review和git 工作流和ci相互融合,通过ci工具来自动触发。利用cli来封装各种常用操作,将原本复杂的命令操作,化繁为简。 简单的介绍一下我们的方案: [图片] 为了能够使这一套工作流变的更智能,我们利用node-git构建了一个轻便的git工具。具体的实现方式可以参考我们电竞小哥锦亮的《基于NodeGit的Git工作流封装》。 我们之前讨论了很久的代码质量的管控,但是除了代码质量,还有一个很大的部分是我们的行为。我们在开发过程中,有很多的行为会改变远端的代码状态和共用资源(测试服务器)的状态。我们怎么才能对这个状态进行一个很好的管理? 如何监控代码的过程? 在电竞小哥的《weex/vue 应用的服务器端构建之路》一文中," 通过工蜂的webhook监控提交" 这段内容详细的介绍了我们的实践。除了这部分介绍的实践,我们还搭建了一个node sever用来记录和查询,我们的测试服务器的使用情况。 简单的说,我们实现了各大系统的过程内容收归,各个不同平台的流程数据能够统一的以企业微信机器人的形式主动推送给你。足不出户,可知天下事。 通过完善的自动化代码监控 + 人工cr的双检查,我们简单的构建起了我们的代码质量监控体系。 对于前端而言,轻薄短小快是基本要求,但是做到轻薄短小快并不是一件简单的事情,更复杂的事情,我们有这么多的工作要做,居然还有一群人在后面用排期表抽我们的屁股,这个就让我们很难受了。 如何对前端的开发、运行和维护进行量化 对工作进行量化是一件很复杂,很难把控的事情。我们在这方面并没有很好的实践。对于如何正确的评估工作量, 我们有一个不怎么成熟的想法: 所有工作量应该包含以下几个部分: 技术方案预研所花费的时间 技术方案落地所耗费的时间 自测的时间 联调的时间 产品修改需求的时间 调试解决问题的时间 在具体的时间分配上,根据各个不同的人自我确定和分配。在具体的实践过程中,我们建议保留一定的buffer,你总会在你的代码上线前,发现这样或那样的问题的。 除了上面的建议,我们还有一些比较通用的建议: 比如实现从设计到结果交付的组件化管理,组件化一定要从设计语言开始,这样才能从根本上减少各个流程的耗时,和实现标准化。 技术预研要早于产品开发。预研一代,落地一代,维护一代。永远不要在业务实际开发中使用未经预研的框架和技术体系,会引入极大的项目风险。 使用graphql这种前端为核心的接口配置体系,能够极大的减少因为接口联调带来的不稳定耗时。 以上,简单的介绍了一下,我们现在的前端工程化的体系进程,和我们的一些思考。很多观点还比较的幼稚,很多实现还特别的落后,希望各位大佬,能够给我们提供更多好的思路和方式方法,大家一起讨论和思考如何更优雅的进行完善的前端工程化。 最后打一波广告: 企鹅电竞——腾讯游戏官方直播平台,欢迎UI开发和前端开发同学加入 https://hr.tencent.com/position_detail.php?id=48298
2019-03-11 - 客服消息转发客服工具踩过
在开发环境进行客服消息的接入测试,需要用到内网穿透工具,如ngrok或者花生壳,推荐前者。在开发页面设置好回调服务器地址,选择好数据格式,这里特别说明一下,客服消息转发到客服工具(注:微信网页客服端)和加不加密无关,和那种数据格式无关。 推荐文章:微信小程序客服消息回复开发 这是社区里面的一篇接入文章,写的通俗易懂,不知道如何接入的话,强烈建议先看这篇文章。我遇到的问题是,原样返回接收到的客服消息之后并没有在客服助手处收到待接入的信息,所以这篇文章的目的在于说清楚理解上的奇异点。 1.服务器签名验证,以下是正确的排序代码,文档中写的是将token,nonce,timestamp按字典序进行排序,指的是按照值得字典序进行排序拼接,而不是对应得参数字段名[token,timestamp,nonce]。当然在某些服务中,也有采用后面这种这是我在接入中遇到得第一个问题。 [代码] List<String> strings = Lists.newArrayList(token, nonce, timestamp); strings.sort(Comparator.naturalOrder()); String txt = String.join("", strings); if(signature.equals(DigestUtils.sha1Hex(txt)){ return true; } return false; [代码] DigesUtils是apache commons-codec包下得工具类。 2.转发到客服工具,在这里的奇异点是,将MsgType改为transfortransfer_customer_service然后原样返回就好,是的我仅仅将接收到的json数据中的MsgType改成transfortransfer_customer_service然后返回,由于这是在同一个http请求中完成的,所以一旦完成之后,得不到反馈,就没法根据错误码进行差错。这里需要注意的两点 ①需要将ToUserName指定为接收数据中的FromUserName,FromUserName 指定为接受到数据中的ToUserName,然后CreateTime为接受到数据的CreateTime,MsgType为transfortransfer_customer_service。 ②在接收到该客服消息的HTTP请求的response应答体、只能包含上述的4个字段。格式的你接受的是什么数据格式,返回的就是什么数据格式。 [代码] Map<String, Object> rs = Maps.newHashMap(); rs.put("MsgType","transfer_customer_service"); rs.put("ToUserName", app.getFromUserName()); rs.put("FromUserName", app.getToUserName()); rs.put("CreateTime", app.getCreateTime()); return JSON.toJSONString(rs); [代码] 总结:思维的固式让我在接入的时候,会按照自己的习惯去思考微信的接口。 3.将信息转发给某个指定的客服,方式有两种,可以去接入三方客服工具如回复中的所提到的。或者参官方文档中的 转发指定接入客服以下截图一段官方文档内容 在响应包中返回MsgType为transfer_customer_service的消息,微信服务器收到响应后会把当次发送的消息转发至客服系统。您也可以在返回transfer_customer_service消息时,在XML中附上TransInfo信息指定分配给某个客服帐号。 [代码] <xml> <ToUserName><![CDATA[touser]]></ToUserName> <FromUserName><![CDATA[fromuser]]></FromUserName> <CreateTime>1399197672</CreateTime> <MsgType><![CDATA[transfer_customer_service]]></MsgType> <TransInfo> <KfAccount><![CDATA[test1@test]]></KfAccount> </TransInfo> </xml> [代码] 有趣的是,这段文档是我在公众号开发的文档中找到的,在小程序客服消息中并没有指定客服消息的描述,不过看字段和配置方式一致,再加上三方工具也说明支持,那么我们可以大胆的说yes。有个疑问是这里的KfAccount 看上去像是账号,是微信号吗?这里的@又让人觉得是邮箱。如果有知道的小伙伴,欢迎补充以下。
2019-08-15 - app.onError : "Yn.env.USER_DATA_PATH"
最近在小程序app.js里添加了onError报错上报,发现 [代码] [代码] [代码]appServiceSDKScriptError undefined is not an object (evaluating [代码][代码]'Yn.env.USER_DATA_PATH'[代码][代码]) emit C global code[代码] [代码] [代码] 和 [代码] [代码] [代码]appServiceSDKScriptError undefined is not an object (evaluating [代码][代码]'Yn.env.USER_DATA_PATH'[代码][代码]) T i onError [native code] emit emit _onNativeTimer global code[代码] [代码] [代码] 而且在短时间里不断触发onError ps : 忘记上报systemInfo了
2018-09-10 - api wx.previewImage() 图片预览无法显示
华为荣耀9、小米部分手机在微信6.6.7下 api wx.previewImage() 图片预览无法显示 还测了 苹果6s可以正常显示 其他机型暂未测试, 问下 这个问解决吗?
2018-08-15