小公司的一年,一齐拜访小商店的前端能够如何

作者:分分快三计划

数据库改造

这里我们做的体系化第一步是对数据库进行改造,将子系统做到一张公共的表,有过相关经验的朋友都会知道,子系统较多的公司,应该将基础用户表设计得足够抽象,只需要包含核心数据:

① 用户id

② 用户昵称

③ 用户手机

④ 用户密码

⑤ 头像&性别&出生年月&身份证&头像......

当然每个子系统的用户角色不尽相同,所以需要每个子系统自己维护一个用户角色表:

 1 //用户总表
 2 //公司用户不一定都是子系统的用户,但子系统用户一定存在与公司用户总表中
 3 var users = [
 4     {
 5         id: '1',
 6         phone: '13579246810',
 7         name: '昵称1',
 8         infos: '其它公共信息'
 9     },
10     {
11         id: '2',
12         phone: '13579246811',
13         name: '昵称2',
14         infos: '其它公共信息'
15     },
16     {
17         id: '3',
18         phone: '13579246812',
19         name: '昵称3',
20         infos: '其它公共信息'
21     }
22 ];
23 //子系统A中的用户表
24 var a_users = [
25     {
26         id: '1',
27         roleId: '1'//游客
28     },
29     {
30         id: '3',
31         roleId: '2'//超级管理员
32     }
33 ];
34 //子系统B中的用户表
35 var b_users = [
36     {
37         id: '1',
38         title: '教授'//游客
39     },
40     {
41         id: '2',
42         roleId: '副教授'//超级管理员
43     }
44 ];
45 //子系统C中的用户表
46 var c_users = [
47     {
48         id: '2',
49         address: 'xxxx',
50         title: '三甲'//游客
51     },
52     {
53         id: '3',
54         address: 'xxxx',
55         roleId: '二甲'//超级管理员
56     }
57 ];

我们在处理用户表的时候,除了抽象基础用户信息时,有可能还需要一层业务公共层,以我们公司为例,多数用户是医生,所以像职称、所属医院这种信息会经常被用到,这个时候就会出现直接使用这种业务公共表的情况:

分分快三计划 1

子系统A与子系统B都是使用的与子系统C一样的用户id,但是他们直接依赖了公共业务关联,他们在公共业务中获取了类似科室、职称等信息,如果这个体系再扩展的话,会是这样的:

分分快三计划 2

结语

希望此文能对准备接触Hybrid技术的朋友提供一些帮助,关于Hybrid的系列这里是最后一篇实战类文章介绍,这里是demo期间的一些效果图,后续git库的代码会再做整理:

 分分快三计划 3

分分快三计划 4

分分快三计划 5

打通H5与Native

体系化第三步是整合H5与Native资源,这里也是所谓的Hybrid体系,只有在前两步完成后,才能很好的完成这一步,否则就只能称为内嵌页,不能叫Hybrid,更不能说是什么移动体系化。

整合Native与H5的第一步仍旧是账号打通,一般来说,我们强制要求native中H5只能使用native提供的统一页面进行登录,不管这个页面是native的还是H5的。

事实上,我们H5端就登录一块做了三套页面一个弹窗,一套账号登录(废弃)、一套手机号登录、一套手机号登录并且带第三方登录,还有在页面里面直接弹出的登录框,一个个APP中是不应该允许存在两个退出账号或者有个人信息的地方。

设想,如果H5具有自己的登录,那么整个情况会变得极其复杂,首先APP具有一套自己的登录,而H5如果与APP登录了不同的账号,那么就会存在用户串了的情况,当然,APP可以监控H5的登录态变化,但这个东西技术实现成本比较高,并且容易出错,所以我们这边要求所有H5的登录全部走Native的一套体系,每次Native打开webview时,如果有cookie就注入webview,这样前端就自己有登录态了 一旦当app注销账户,之前所有的页面也将全部弹出掉,新开一局,如此一来账号体系已经打通。

分分快三计划 6

账号体系

一般来说,一个公司的账号体系健壮灵活程度会很大程度反映出这个研发团队的整体实力:

① 统一的鉴权认证

② 短信服务图形验证码的处理

③ 子系统的权限设计、公共的用户信息导出

④ 第三方接入方案

⑤ 接入文档输出

⑥ ......

这个技术方案,有没有是一回事(说明没思维),有几套是一回事(说明比较乱,技术不统一),对外的一套做到了什么程度又是一回事,当然这个不是我们讨论的重点,而账号体系也是Hybrid设计中不可或缺的一环。

账号体系涉及了接口权限控制、资源访问控制,现在有一种方案是,前端代码不做接口鉴权,账号一块的工作全部放到native端。

团队战斗

我十分喜欢武侠,近期特别喜欢剑雨中的一段戏份:

当时是转轮王手下三大高手,雷彬、彩戏师、细雨等五人戏份(大S可忽略),彩戏师提出你我三人联手格杀转轮王如何,并开出了条件等待交易:

我(彩戏师)只要罗摩遗体

全部财产给雷彬

细雨回去和爱人过小日子

显然,彩戏师的价码是足够让人动心的,他也提出了相当的诚意,主攻转轮王去了,这个时候雷彬开始了观望,然而细雨一声“你们玩,我回家和相公吃饭去了”,直接把整个交易堵死了。

这部戏其实真的非常精彩,如果他们三突然达成一致跑去围杀转轮王,我才会感到奇怪。考虑到电影才过3/4,观众可能会说那么这一切岂不是太轻易了?但是从真实社会阅历来说,这桩交易达成的概率很低,一个核心原因是:

这笔买卖涉及了大多数人的利益,乃至生命,一旦一件事情涉及了多个人的利益,那么这个事情势必会很慢、很难达成一致

我们做一件事情时,但烦需要他人合作共事,就一定比一个人难多了,人和人之间想法差异是及其巨大的,就看剑雨几个主要人物的需求:

① 转轮王,需要罗摩遗体长jj,好xxoo

② 彩戏师,需要罗摩遗体治病

③ 雷彬,需要钱与不受控制

④ 细雨,需要爱,需要家人不受伤害

⑤ 大S,也许是需要关注吧

可以看到,里面最核心的利益冲突是来源于转轮王与彩戏师的,这也是为什么处于弱者的彩戏师要先出手,表达诚意,其他人可以观望,可以退出。

同样,团队之中,人和人的差异是巨大的,这种差异甚至是难以调和的,在这之中就一定会有一个智障或者特别有私心、或者懒惰、或者喜欢单独和上级沟通的,只要一个队友有一项或者几项毛病,整个团队就会扯皮,而处理扯皮是内耗的事情,但这种内耗又往往比正经做事要花费更多的精力。

在团队内,各个小伙伴性格各异,彼此较劲;然后团队之间又有分别,产品与研发彼此对立,就算一个公司,内部也有派系之分,小至一个家族也会兄弟拆墙,讲到底,分的是彼此,争的是权利,除了自己人就不是自己人,不是自己人就算别人,这个分别不知何时才能停止。

因为是人都会有分别,产品变更了需求,就比开发改了我代码更可恶;上海分公司的产品欺负了深圳分公司的研发,又比身边的死UI更加可恶,大大小小的分别,职位的分别,地域的分别,亲疏的分别,人很容易就可以找到和自己不同的族群作为敌人,所以纷争很难停止,解决分别心这种佛学的话题显然我们无能为力,所以选人就变得尤为关键。

综上,要让团队有战斗力:

① 首先就要有好的计划

② 其次要有好的leader

③ 然后找合适的人

⑤ 最后打到共同的敌人(项目)

好的方向才可能出好的结果,任何没有计划的事情都收效甚微,好的leader才能团结团队,技术要过硬,视野要够远,抢业务要凶猛,而慈不掌兵,如果有和团队气质不符,甚至起到反作用的,一定要提前剔除(劝退是最后的手段,更多的应该是影响),否则吃亏的是整个团队。

这里再强调下leader的作用,团队的战斗力需要leader去激发,leader作为公司的执行意志与核心驱动力,需要起到正确的带头作用,要有足够的责任感与危机感,要善于汇报工作,争抢业务,如果你的leader总是把业务往外面推,那么这个leader是不合格的。

因为,我们是来工作,是来赚钱的,我首先是来赚钱的,其次才有团队小伙伴的战斗情谊,如果没有业务就是没有KPI,没有KPI就是没有钱,连最基本的发展都没有的话,再好的私交在公司面前也不可持续,我们是因为这份事业才在一起的,梦想&激情这种属于稀缺的消耗品,不是人人拥有的。

个人与团队的关系,矛盾而又统一,完全追求个人能力成长最大化必定和团队利益冲突,如果能合理运用团队又能打破个人的局限,所以团队合作才是突破一切的关键,一个人的力量是有限的,遇到困难也更容易突破,如果发现一些事情,团队中只有你能做,就要小心了。

推动感悟

从项目调研到项目落地再到最近一些的优化,已经花了三个月时间了,要做好一件事是不容易的,而且我们这个还涉及到持续优化,和配套业务比如:

① passport

② 钱包业务

③ 反馈业务

.....

等同步制作,很多工作的意义,或者作用,是非技术同事看不到的,但是如果我们不坚持做下去,迫于业务压力或者自我松懈放纵,那么就什么也没有了,我们要推动一件事情,不可能一站出来就说,嘿,小样,我们这个不错,你拿去用吧,这样人家会猜疑你的,我们一定是要先做一定demo让人有一定初步印象,再强制或者偷偷再某一个生产业务试用,一方面将技术依赖弄进去,一方面要告诉其他同事,看看嘛,也没有引起多大问题嘛,呵呵。

做事难,推动难,难在坚持,难在携手共进,这里面是需要信念的,在此尤其感谢团队3个伙伴的无私付出(杨杨、文文、文文)。

后续,我们在持续推动hybrid建设的同时,会尝试React Native,找寻更好的更适合自己的解决方案。

微博求粉

最后,我的微博粉丝极其少,如果您觉得这篇博客对您哪怕有一丝丝的帮助,微博求粉博客求赞!!!

分分快三计划 7

前言 接上文:(阅读本文前,建议阅读前两篇文章先) 浅谈Hybrid技术的...

数据可视化

上述工作做完,表现层的东西就做了一大部分了,站在前端的角度的话,可以做的东西好像不多了,其实细细一想,有这种想法真的是图样图森破,就算要把上面的事情做完都要费老大的劲,况且真正的难点可能才真正的开始,如我们第一张图,我们有一个项目外包了,那个外包的用户是游离于我们账户体系外的,我们应该如何处理?而更让我们头疼的可能是数据收集与分析,猛的回头,你会发现,对于前端,还有数据可视化这么一大坨的东西需要你去挖掘!

随着技术的沉淀,公司的发展,公司的业务虽然越来越复杂,但是在我们的体系下,都还能很好的运转,但是业务才是技术的祖宗,我们可能会收到类似这种需求:

① 请给我一下上次迎新活动3个月后的用户留存率

② 请给我XX推广人员的订单推广率

③ 请给我APP由XX二维码推广活动的数据

④ 请告诉我为什么我们转化率低

......

Android webview兼容

Android webview的表现不佳,闪屏等问题比较多,遇到的几个问题有:

① 使用hybrid命令(比如跳转),如果点击快了的话,Android因为响应慢要开两个新页面,需要对连续点击做冻结

② 4.4以下低版本不能捕获js回调,意思是Android拿不到js的返回值,一些特殊的功能就做不了,比如back容错

③ ......

公共H5服务

体系化的第二步是H5整合Server服务,当公共的服务出现后,这里便需要提供公共的H5页面,这里会遇到一个难题需要突破:

① 各个业务有自己的定制,比如注册时候要求的字段都不一样

② UI会成为第一个拦路虎需要你去说服

既然是公共页面,就需要满足一些业务的定制需求,当底层框架完善并且统一后,便可以以规范的力量给予业务开发以指导与限制了,只有将公共业务做好后,才能真正提高我们整体的开发效率

第一个问题是业务受限,那就说明公共服务做的不行,不具有通用性,那就改设计,改到满足就行了

第二个问题,肯定是在我们已经有公共服务的情况下,告诉UI,我们这个是公共服务,是不能乱改的,设计要中性化

要整个公司的人都形成公共服务,以及重用,效率的思维后,大家才会认可这个,在人家不知道或者不认可的情况下,说那么多也没用,知行合一才是王道。

一个较为合理的公共页面可能是这个样子的:

分分快三计划 8

所以,后期我们的系统就变成了这个样子了:

分分快三计划 9

当我们这套体系走的足够远后,我们整个系统可能就是这样的了:

分分快三计划 10

更新率

我们有时候想要的是一旦增量包发布,用户拿着手机就马上能看到最新的内容了,而这样需要app调用增量包的频率增高,所以我们是设置每30分钟检查一次更新。

体系设计

在我看来,体系的设计与出现不是一朝一夕的事情,也不是凭空设计,脱离业务,每当我们需要在我们的技术体系中添加模块的时候都需要思考一些问题:

我们提出的这个东西是要真实解决我们开发中的什么痛点?

这个会需要我们具有一定的特质:

① 有强烈的意识,能了解性能的缺陷,开发效率低下的原因,并能提供有效的处理办法,再此之上进行抽象

② 对于团队中搁置的老大难问题,要想办法进行有效的推动、处理

而,我们体系化的东西也不是过家家产生的,这种比较通用的设计必须要出文档,必须与人商量,技术方案里必须的流程图、时序图都要规范,还要把设计要完成的关键参数标注好,最后作为验收标准。方案出来之后要确认方案如何落地,操作路线是什么,执行计划是什么,怎么处理团队间的阻力。

比如我们上面做的通用的登录注册页面,就需要相关的文档,要描述清楚,我们这个东西的边界是什么,能解决什么问题,有什么限制,体系设计推动任重道远,你我共勉。

调试

一个Hybrid项目,要最大限度的符合前端的开发习惯,并且要提供可调试方案

我们之前说过直接将所有请求用native发出有一个最大的问题就是调试不方便,而正确的hybrid的开发应该是有70%以上的时间,纯业务开发者不需要关心native联调,当所有业务开发结束后再内嵌简单调一下即可。

因为调试时候需要读取测试环境资源,需要server端qa接口有个全局开关,关闭所有的增量读取

关于代理调试的方法已经很多人介绍过了,我这里不再多说,说一些native中的调试方案吧,其实很多人都知道。

技术体系化

在小公司,因为很多基础设施都不成熟,这个会让我们有将技术业务体系化&服务化的机会,而体系化后的技术便是公司的财富也是研发团队的技术壁垒,他能大幅度的提高效率,这个是一个生态体系,一旦生态体系成熟后,牵一发而动全一身,就不是任何人能轻易接手的了。

初到公司的时候,我们的系统是这么个状态:

分分快三计划 11

每个H5项目有一个自己独立的登录注册,native又有自己的native的登录注册,甚至server端的服务都彼此独立,这样用户之间变形成了信息孤岛,这会引发很多问题:

① 每次做一个H5项目都必须做一个登录注册,徒增工作量

② 我们多出一个APP后,APP又产生了自己的登录注册

③ 我们H5项目内嵌至native后,账号没法打通

④ 当我们用户越来越多,子系统越来越杂的时候,我们的用户会越来越乱

这个时候,我们需要做的一件事情,就是整理所有的子系统,将我们的账号系统体系化。

边界问题

在我们使用Hybrid技术前要注意一个边界问题,什么项目适合Hybrid什么项目不适合,这个要搞清楚,适合Hybrid的项目为:

① 有60%以上的业务为H5

② 对更新(开发效率)有一定要求的APP

不适合使用Hybrid技术的项目有以下特点:

① 只有20%不到的业务使用H5做

② 交互效果要求较高(动画多)

任何技术都有适用的场景,千万不要妄想推翻已有APP的业务用H5去替代,最后会证明那是自讨苦吃,当然如果仅仅想在APP里面嵌入新的实验性业务,这个是没问题的。

结语

本来写此文是因为昨天驾考挂了,当时写出来的东西,感觉比较消极,于是今日抽一些时间重新整理一番,梳理思绪情感,一起积极面对2017年吧!!!

其实,小公司真的有很多独有的优势,很多坑等着你去做,去思考,只要能把这些坑一个个填平,那么必将会有长足的进步,也能尽快的突破自我瓶颈。

文中想法系个人想法,或许有问题,请积极交流。

最后,科目三补考求过!!!

一些坑点

Hybrid化

当做到H5与Native账号打通后,我们便可以实行我们的Hybrid化进程了,这里简单以Header为例。

分分快三计划 12

 

主流Hybrid都是使用的Native的Header,原因有很多:

① 稳定,防假死

我们料不到前端会出什么错,特别是第三方网站,一旦前端出错如果iOS连个退出的按钮都没有,那么就app假死了,这个比crash还讨厌

② 体验

我们在刚打开一个H5页面时,可能会有白屏,如果header也不在的话,体验会比较差

而我们在设计Header交互时候,需要考虑到前端的使用习惯,最好能保持业务代码一致,处于不同宿主容器表现不同,这里的设计是左中右的设计,图中就是我们能提供的所有header样式了,不够也没办法咯。

完了我们需要对tagname定制化,header中的所有按钮的唯一标识为tagname,所以tagname不得重复,其次常用tagname会有一个默认图标,需要定制化的话,就读取线上资源。

这里back比较特殊,在webview中会检查history的记录,如果大于1则后退,否则会退回上一步操作。 我们可以看出,back的功能是很单一的,往往不能满足我们的需求,所以常常使用forward pop动画当做back使用,而这一做法将引起令人头疼的history错乱问题,针对这种情况我们有一些特殊API,但是因为这个API需要Native支持,所以使用需要慎重,最好是新增一个native接口,用于跳转后,清除所有的history webview。

Header约定是Hybrid的重要一环,也是移动体系化,技术体系化中重要的一小环,与之对应的会有:

① 分享约定

② 登录唤醒约定

③ 离线包机制

④ 跳转机制

......

这里体系化做的足够好的话就会出现类似微信SDK一般的东西,但是这个要看你们是不是有足够多的第三方接入方需要你们费这个神了,但是只要做到这一些,你的移动端便已经体系化了,所有的H5项目的账号系统与基本native打通是完成了。

这种体系化的东西形成后需要做到通用,比如两个app能同时运行同一个H5站点,甚至离线包机制都是一致的,header交互也是一致的

一些小特性

为了让H5的表现更加像native我们会约定一些小的特性,这种特性不适合通用架构,但是有了会更有亮点。

用户&订单渠道

可以相信,所有这一切必定会将你问懵,一般来说,不是所有前端一开始设计便能考虑到这些问题,也不是考虑到这些问题就能设计得好,这里简单以用户业务渠道做一个说明。

为了解决以上问题,我们在设计用户表的时候就得新增一些字段了(比较痛苦的是最开始没有这些东西,后面加就恼火了):

① 项目来源,标志该用户(订单)来源于哪个子系统

② 业务来源,标志该用户(订单)来源于什么渠道

这个渠道就比较复杂了,可以是推广人的拼音,可以是一个活动的标志......

这个设计其实比较简单,就是新增几个数据表字段嘛,真正的难点在于前端&native调用,一般来说,我们希望业务开发无感的便存入进去了,所以我们可以这样设计:

① 在url(cookie也行,就是麻烦)上加入一个channel的参

这里如果不使用cookie需要前端框架做处理,保证每次跳转将这个channel参数一直带下去

② 每次ajax请求的时候将这种新增一个入common的字段,让server端自动处理

所以,业务开发只需要在url做处理(生成二维码的时候带上参数),前端框架统一处理后,每次请求就自动带上了,比如:

分分快三计划 13

native处理方案类似,这里处理完了,我们便可以收集到用户(订单)来源于哪个渠道了,有了这个数据收集便能很好的做后续的分析。

交互约定

根据之前的学习,我们知道与Native交互有两种交互:

① URL Schema

② JavaScriptCore

而两种方式在使用上各有利弊,首先来说URL Schema是比较稳定而成熟的,如果使用上文中提到的“ajax”交互方式,会比较灵活;而从设计的角度来说JavaScriptCore似乎更加合理,但是我们在实际使用中却发现,注入的时机得不到保障。

iOS同事在实体JavaScriptCore注入时,我们的原意是在webview载入前就注入所有的Native能力,而实际情况是页面js已经执行完了才被注入,这里会导致Hybrid交互失效,如果你看到某个Hybrid平台,突然header显示不正确了,就可能是这个问题导致,所以JavaScriptCore就被我们弃用了。

JavaScriptCore可能导致的问题:
① 注入时机不唯一(也许是BUG)
② 刷新页面的时候,JavaScriptCore的注入在不同机型表现不一致,有些就根本不注入了,所以全部hybrid交互失效

如果非要使用JavaScriptCore,为了解决这一问题,我们做了一个兼容,用URL Schema的方式,在页面逻辑载入之初执行一个命令,将native的一些方式重新载入,比如:

1 _.requestHybrid({
2     tagname: 'injection'
3 });

这个能解决一些问题,但是有些初始化就马上要用到的方法可能就无力了,比如:

① 想要获取native给予的地理信息

② 想要获取native给予的用户信息(直接以变量的方式获取)

作为生产来讲,我们还是求稳,所以最终选择了URL Schema。

明白了基本的边界问题,选取了底层的交互方式,就可以开始进行初步的Hybrid设计了,但是这离一个可用于生产,可离落地的Hybrid方案还比较远。

大公司or小公司

之前经常有人发文说到底要去小公司还是大公司,思考几年的经历,其实大公司小公司不重要,好的团队才重要!

大公司一般是什么都有,只需要你有一颗学习的心,多折腾多问,便能吸取大量的养分;而小公司也有一个巨大的优势,便是什么都没有,只要你有心,就能把大公司的东西通通实现一篇,这种实践来的知识技能可比学习要来的珍贵的多!

初到公司时发现了一种现象:

① 身边很多小伙伴没有买房但是都有自己的车子

② 多数小伙伴下班就回家了

可以很清晰的感受到这里的一种慢节奏,这个没什么不对,生活与工作要分开嘛,但这却让我感觉到了危机与忧虑,最大的忧虑是多数小伙伴可能不会在意到公司的财富了,天地不仁以万物为刍狗,其实生活是非常公平的,每个人的机遇其实都差不多,很多财富摆在那里,就看你是不是要去拿。

之前面试的时候,有人会问我知识获取的途径,也许是我比较low的缘故,我一直信奉一个原则:

听过 < demo过 < 实际工作用过 < 实际工作中被坑过 < 实际工作中被多次坑过或者深入研究总结过

风之积也不厚,其负大翼也无力也。网上有很多深度好文,如果没有一定基础,其实看了没有什么意义,只会在心里感叹,我尼玛这人好牛逼。

我学习的多数知识是直接从项目中来的,这个时候就需要你有一个好的团队了,我十分庆幸自己曾在携程无线待过,携程无线在前端工程化&hybrid&公共服务化一块走的比较远,而我当时又很好学,平时没事就在这之上挖掘,吸收学习,当时一些半懂不懂的知识,在后续的实践中也逐步融会贯通了,其带来的财富令我受益至今。

而,人的知识除了受限于自己的学习主动性以外,也受限至他的视野,当时我的视野就在前端方面打不开,没有去研究携程的日志统计系统与发布系统,到现在需要用到这部分知识时才感到苦不堪言而后悔莫及。

如果各位有机会到大公司去,一定要认认真真搞清楚,你自己所在领域里面,该公司的财富积累是什么,然后狠狠去挖掘他,了解他的历史故事,各种处理细节,更多的不是关注他怎么做,而是要关注他们为什么这么做,然后多问多demo,假以时日落地到实践中,这块财富就装入你的口袋了。回想自己的择业,我事实上是比较后悔自己当时为了一点小钱而放弃了阿里(成体系的前端团队)去了百度(新团队,不成体系,甚至前端框架都不统一),如果带着谦卑的态度去阿里吸取一番技术的给养想必会受用无穷吧。

技术的学习,这个需要一个学习的态度,一个学习的恒心,其实只要是持续的投入,便一定会有收获的。

iOS静态资源缓存

Android有一个全局开关,控制静态资源部读取缓存,但是iOS中研究了好久,都没有找到这个开关,而他读取缓存又特别厉害,所以所有的请求资源在有增量包的情况下,最好加上时间戳或者md5

工作态度

我在上海工作期间学习到的另一个受用无穷的知识便是“正能量”了,其实正能量并不能让你多写几行代码,但是他会令你的工作状态持续上升,与之对应的是负能量,如果你身边有小伙伴产生负能量的话,就一定要小心了,负能量就确确实实能让你少写几行代码了。

当时携程无线解散的时候,需要我们并入其他团队,不知不觉间就生出了一些负面情绪,我们是后爹后妈养的,过去肯定没好日子过了,于是那段时间各种消沉,也有换工作的准备了;而团队两个老大哥的表现却截然相反,一个老大哥仍旧是勤勤恳恳的工作,帮助团队乃至整个公司渡过了当时比较困难的技术交接时期,另一个老大哥积极的协助新团队推动新框架,甚至那个框架都不是我们写的。

后来,我经常与两个老大哥交流,一个老大哥(来自华为)传给了我“忍、滚、狠”的绝技,另一个老大哥带着我领会了皮实的意义,其实这些道理真的很简单,我们处于顺境的时候,自然意气风发,那么当我们处于逆境中的时候,我们就应该自暴自弃、消沉不已吗?

时至今日,我有点什么疑惑的地方都经常喜欢请教两位老大哥,我也从他们身上学到了,其实在抓技术的同时,协调推动能力也是至关重要的,因为现在很多业务都是几个部门在做,如果没有良好的推动能力的话,极有可能iOS一套东西,Android一套,前端自成一套,这种对整个公司来说是一种浪费,需要有人站出来整理整合。

前言

接上文:(阅读本文前,建议阅读前两篇文章先)

浅谈Hybrid技术的设计与实现

浅谈Hybrid技术的设计与实现第二弹

根据之前的介绍,大家对前端与Native的交互应该有一些简单的认识了,很多朋友就会觉得这个交互很简单嘛,其实并不难嘛,事实上单从Native与前端的交互来说就那点东西,真心没有太多可说的,但要真正做一个完整的Hybrid项目却不容易,要考虑的东西就比较多了,单从这个交互协议就有:

① URL Schema

② JavaScriptCore

两种,到底选择哪种方式,每种方式有什么优势,都是我们需要深度挖掘的,而除此之外,一个Hybrid项目还应该具有以下特性:

① 扩展性好——依靠好的约定

② 开发效率高——依赖公共业务

③ 交互体验好——需要解决各种兼容问题

我们在实际工作中如何落地一个Hybrid项目,如何推动一个项目的进行,这是本次我们要讨论的,也希望对各位有用。

文中是我个人的一些开发经验,希望对各位有用,也希望各位多多支持讨论,指出文中不足以及提出您的一些建议

设计类博客


iOS博客

Android博客

代码地址:

因为IOS不能扫码下载了,大家自己下载下来用模拟器看吧,下面开始今天的内容。

总体概述在第一章,有兴趣大家去看

细节设计在第二章,有兴趣大家去看

本章主要为打补丁

前言

昨日,我请了一天假去考科目三,结果第一把挂在了没完全关闭灯光上,第二把挂在转弯时没有观察后方车辆,当听到师傅一句“下去”的时候,我那是悲痛的面红耳赤,这让我很郁闷,晚上也就不想回去上班了,回家后仍然有点低沉,在这种情况下,不写点毒鸡汤,好像已经不能好好的调节心情了,看看时间年底了,便写写今年的总结吧。

回想自己学车的点点滴滴,其实是很认真的,一旦有时间就去学习,练习时候也表现比较正常,晚上还会冥想整个考试流程,但最后临门一脚仍旧出错了,这个时候可以说我问心无愧了,也可以说我努力过了,虽然失败了,但我应该得到尊重。

现在看来说这种话的小伙伴不过在自我安慰罢了,做的过程中我确实很努力,也真的很认真,但是最后产生不了成果,做的事情不能落地,那么这一切的努力可以说毫无意义。将这一次的驾照考试映射到一次重要项目开发的话:

产品努力出需求 ->研发努力加班干 -> 测试努力加班测试 -> 上线 -> 大流量挂啦......

开发的过程中,研发天天加班,测试也天天加班,但是最后项目上线后就是挂了,那么之前的努力并不会换来丰收的果实,即将来临的可能是老板的怒号,团队的动荡,而不论是考试挂了,还是项目挂了,都有幸被我经历了。回头想想,人生嘛,什么都应该经历一发嘛,深深的体会一下挂了的那种无力感,也是不错的嘛,想到这里,我竟然有些释怀的感觉,只不过是重头再来(自我安慰而已啦)......

再回想16年初回到了成都,开始了愉快的“慢节奏”生活。不曾想,现在的公司给予自己的平台居然是其它地方所没有的,不论从工作强度还是业务复杂度来说,都是很对胃口的,偶尔的工作强度甚至超过了上海,嗯,这个很“成都”。

文中为个人观点,不喜勿喷

公共业务的设计-体系化

在Hybrid架构中(其实就算在传统的业务中也是),会存在很多公共业务,这部分公共业务很多是H5做的(比如注册、地址维护、反馈等,登录是native化了的公共业务),我们一个Hybrid架构要真正的效率高,就得把各种公共业务做好了,不然单是H5做业务,效率未必会真的比Native高多少。

底层框架完善并且统一后,便可以以规范的力量限制各业务开发,在统一的框架下开发出来的公共业务会大大的提升整体工作效率,这里以注册为例,一个公共页面一般来说得设计成这个样子:

公共业务代码,应该可以让人在URL参数上对页面进行一定定制化,这里URL参数一般要独特一些,一面被覆盖,这个设计适用于native页面

分分快三计划 14

URL中会包含以下参数:

① _hashead 是否有head,默认true

② _hasback 是否包含回退按钮,默认true

③ _backtxt 回退按钮的文案,默认没有,这个时候显示为回退图标

④ _title 标题

⑤ _btntxt 按钮的文案

⑥ _backurl 回退按钮点击时候的跳转,默认为空则执行history.back

⑦ _successurl 点击按钮回调成功时候的跳转,必须

只要公共页面设计为这个样子,就能满足多数业务了,在底层做一些适配,可以很轻易的一套代码同时用于native与H5,这里再举个例子:

如果我们要点击成功后去到一个native页面,如果按照我们之前的设计,我们每个Native页面皆已经URL化了的话,我们完全可以以这种方向跳转:

1 requestHybrid({
2     tagname: 'forward',
3     param: {
4         topage: 'nativeUrl',
5         type: 'native'
6     }
7 });

这个命令会生成一个这样的url的链接:

_successurl == hybrid://forward?param={"topage":"nativeUrl","type":"native"}

完了,在点击回调时要执行一个H5的URL跳转:

window.location = _successurl

而根据我们之前的hybrid规范约定,这种请求会被native拦截,于是就跳到了我们想要的native页面,整个这一套东西就是我们所谓的体系化:

分分快三计划 15

补足体系

上述是业务方的数据收集,这个属于精准定制结果,直接接口存储,除此之外,我们还需要对整个子系统进行数据打点采集,比如页面pv uv 按钮点击,这个是比较简单的需求,如果一个H5站点用于了多个容器(微信、qq),而每个容器(渠道)产生的pv信息需要记录起来的话,便会有些麻烦。

数据采集这块是我最近准备做的东西,事实上这块我也很有一点一筹莫展的感觉,首先碰到第一个问题就比较令人头疼?

我们到底是应该自己从无到有做一套采集打点系统还是应该直接使用友盟或者百度统计这种第三方的东西?

这里因为我也还没有想清楚,便不做展开,当这块形成后我们整个体系就变成了这个样子了:

分分快三计划 16

经过将近一年的努力,我们逐步构建了这个移动体系化,并且正在向各部分添砖加瓦,现在在以下模块仍然有所缺失:

① 数据可视化缺失,如上所言,这块是我们接下来需要补足的,这里又包括了数据采集,存储,分析,展示多个方面,总之可以做的很多。

② 通用IM消息系统缺失

我们现在Native使用的自身的IM,H5与Native由于原来北京成都两个团队而选择了融云体系,现在整个消息系统没有打通,这里是需要打通的,以后就算大家选用第三方的服务,都一定记得让自己server端做一次收口工作,做一次proxy,这个如果后期需要改造更换消息系统会轻易的多!

③ 日志监控

我们的日志监控与预警一块做的也不够完善,这里包括前端预警与server端预警,这块接下来要加强

④ 全站https化

......

其实除了上面的一些,应该还有很多其他体系模块没有被提出,比如:

① 开发环境

一般环境分为开发、QA、预览(生产某一个机器)、生产四个环境,环境是比较好做区分的,但是难点在于通用的发布系统与各环境的数据处理问题,比如QA环境就是需要一些生产环境的数据,这个时候该怎么做???

② 小流量发布

有些时候,我们为了测试,可能需要小流量发布,一方面为了关注流量变化,一方面为了确认没有错误,我们需要这种系统,同时也需要我们的可视化系统记录各种情况的转化率等数据

这里也仅仅是我(前端角度)所了解的移动体系,也许换个人来,又会大有不同,不管是什么样的体系,都一定要保证,自己的公司是有一套开发所依赖的体系的,这个东西会极大的提升开发效率与稳定性!

离线更新

根据之前的约定,Native中如果存在静态资源,也是按频道划分的:

webapp //根目录
├─flight
├─hotel //酒店频道
│  │  index.html //业务入口html资源,如果不是单页应用会有多个入口
│  │  main.js //业务所有js资源打包
│  │
│  └─static //静态样式资源
│      ├─css 
│      ├─hybrid //存储业务定制化类Native Header图标
│      └─images
├─libs
│      libs.js //框架所有js资源打包
│
└─static //框架静态资源样式文件
    ├─css
    └─images

我们这里制定一个规则,native会过滤某一个规则的请求,检查本地是否有该文件,如果本地有那么就直接读取本地,比如说,我们会将这个类型的请求映射到本地:

http://domain.com/webapp/flight/static/hybrid/icon-search.png
//===>>
file ===> flight/static/hybrid/icon-search.png

这样在浏览器中便继续读取线上文件,在native中,如果有本地资源,便读取本地资源:

分分快三计划 17

但是我们在真实使用场景中却遇到了一些麻烦。

落地项目

真实落地的业务为医联通,有兴趣的朋友试试:

分分快三计划 18

分分快三计划 19

账号切换&注销

账户注销本没有什么注意点,但是因为H5 push了一个个webview页面,这个重新登录后这些页面怎么处理是个问题。

我们这边设计的是一旦重新登录或者注销账户,所有的webview都会被pop掉,然后再新开一个页面,就不会存在一些页面展示怪异的问题了。

增量的粒度

其实,我们最开始做增量设计的时候就考虑了很多问题,但是真实业务的时候往往因为时间的压迫,做出来的东西就会很简陋,这个只能慢慢迭代,而我们所有的缓存都会考虑两个问题:

① 如何存储&读取缓存

② 如何更新缓存

浏览器的缓存读取更新是比较单纯的:

浏览器只需要自己能读到最新的缓存即可

而APP的话,会存在最新发布的APP希望读到离线包,而老APP不希望读到增量包的情况(老的APP下载下来增量包压根不支持),更加复杂的情况是想对某个版本做定向修复,那么就需要定向发增量包了,这让情况变得复杂,而复杂即错误,我们往往可以以简单的约定,解决复杂的场景。

思考以下场景:

我们的APP要发一个新的版本了,我们把最初一版的静态资源给打了进去,完了审核中的时候,我们老版本APP突然有一个临时需求要上线,我知道这听起来很有一些扯淡,但这种扯淡的事情却真实的发生了,这个时候我们如果打了增量包的话,那么最新的APP在审核期间也会拉到这次代码,但也许这不是我们所期望的,于是有了以下与native的约定:

Native请求增量更新的时候带上版本号,并且强迫约定iOS与Android的大版本号一致,比如iOS为2.1.0Android这个版本修复BUG可以是2.1.1但不能是2.2.0

然后在服务器端配置一个较为复杂的版本映射表:

## 附录一   
// 每个app所需的项目配置
const APP_CONFIG = [
   'surgery' => [        // 包名
        'channel' => 'd2d',      // 主项目频道名
        'dependencies' => ['blade', 'static', 'user'],    // 依赖的频道
        'version' => [   // 各个版本对应的增量包范围,取范围内版本号最大的增量包
            '2.0.x' => ['gte' => '1.0.0', 'lt' => '1.1.0'],    
            '2.2.x' => ['gte' => '1.1.0', 'lt' => '1.2.0']
        ],
        'version_i' => [    // ios需特殊配置的某版本

        ],
        'version_a' => [    // Android需特殊配置的某版本

        ]
    ]
];

这里解决了APP版本的读取限制,完了我们便需要关心增量的到达率与更新率,我们也会担心我们的APP读到错误的文件。

不要命就用swift

苹果官方出了swift,于是我们iOS团队好事者尝试了感觉不错,便迅速在团队内部推广了起来,而我们OC本身的体量本来就有10多万行代码量,我们都知道一个道理:

重构一时爽,项目火葬场

而重构过程中肯定又会遇到一些历史问题,或者一些第三方库,代码总会有一点尿不尽一点冗余,而不知道swift是官方有问题还是怎么回事,每次稍微多一些改动就需要编译一个多小时!!!!你没看错,是要编译一个多小时。

一次,我的小伙伴在打游戏,被我揪着说了两句,他说他在编译,我尼玛很不屑的骂了他,后面开始调iOS时,编译了2小时!!!从那以后看见他打游戏我一点脾气都没有了!!!

这种编译的感觉,就像吃坏了肚子,在厕所蹲了半天却什么也没拉出来一样!!!所以,不要命就全部换成swift吧。

如果有一定历史包袱的业务,或者新业务,最好不要全面使用新技术,不成熟的技术,如果有什么不可逆的坑,那么会连一点退路都没有了。

Android

Android需要能FQ的chrome,然后输入chrome://inspect/#devices即可,前提是native同事为你打开调试模式,当然Android也可以使用模拟器啦,但是Android的真机表现过于不一样,还是建议使用真机测试。

header-搜索

根据我们之前的约定,header是比较中规中矩的,但是由于产品和视觉强迫,我们实现了一个不一样的header,最开始虽然不太乐意,做完了后感觉还行......

分分快三计划 20

这块工作量主要是native的,我们只需要约定即可:

 1 this.header.set({
 2     view: this,
 3     //左边按钮
 4     left: [],
 5     //右边按钮
 6     right: [{
 7         tagname: 'cancel',
 8         value: '取消',
 9         callback: function () {
10             this.back();
11         }
12     }],
13     //searchbox定制
14     title: {
15         //特殊tagname
16         tagname: 'searchbox',
17         //标题,该数据为默认文本框文字
18         title: '取消',
19         //没有文字时候的占位提示
20         placeholder: '搜索医院、科室、医生和病症',
21         //是否默认进入页面获取焦点
22         focus: true,
23 
24         //文本框相关具有的回调事件
25         //data为一个json串
26         //editingdidbegin 为点击或者文本框获取焦点时候触发的事件
27         //editingdidend 为文本框失去焦点触发的事件
28         //editingchanged 为文本框数据改变时候触发的事件
29         type: '',
30         data: '' //真实数据
31     },
32     callback: function(data) {
33         var _data = JSON.parse(data);
34         if (_data.type == 'editingdidend' && this.keyword != $.trim(_data.data)) {
35             this.keyword = $.trim(_data.data);
36             this.reloadList();
37         }
38 
39     }
40 });

浅谈Hybrid技术的设计与实现第三弹——落地篇,浅谈hybrid技术落地

正确读取

这里可能有点杞人忧天,因为Native程序不是自己手把手开发的,总是担心APP在正在拉取增量包时,或者正在解压时,读取了静态文件,这样会不会读取错误呢,后面想了想,便继续采用了之前的md5打包的方式,将落地的html中需要的文件打包为md5引用,如果落地页下载下来后,读不到本地文件就自己会去拉取线上资源咯。 

iOS

首先,你需要拥有一台Mac机,然后打开safari;在偏好设置中将开发模式打开:

分分快三计划 21

然后打开模拟器,即可开始调试咯:

分分快三计划 22

native代理请求

在H5想要做某一块老的App业务,这个APP80%以上的业务都是Native做的,这类APP在接口方面就没有考虑过H5的感受,会要求很多信息如:

① 设备号

② 地理信息

③ 网络情况

④ 系统版本

有很多H5拿不到或者不容易拿到的公共信息,因为H5做的往往是一些比较小的业务,像什么个人主页之类的不重要的业务,Server端可能不愿意提供额外的接口适配,而使用额外的接口还有可能打破他们统一的某些规则;加之native对接口有自己的一套公共处理逻辑,所以便出了Native代理H5发请求的方案,公共参数会由Native自动带上。

 1 //暂时只关注hybrid调试,后续得关注三端匹配
 2 _.requestHybrid({
 3     tagname: 'apppost',
 4     param: {
 5         url: this.url,
 6         param: params
 7     },
 8 
 9     callback: function (data) {
10         scope.baseDataValidate(data, onComplete, onError);
11     }
12 });

这种方案有一些好处,接口统一,前端也不需要关注接口权限验证,但是这个会带给前端噩梦!

前端相对于native一个很大的优点,就是调试灵活,这种代理请求的方式,会限制请求只能在APP容器中生效,对前端调试造成了很大的痛苦

从真实的生产效果来说,也是很影响效率的,容易导致后续前端再也不愿意做那个APP的业务了,所以使用要慎重......

注入cookie

前端比较通用的权限标志还是用cookie做的,所以Hybrid比较成熟的方案仍旧是注入cookie,这里的一个前提就是native&H5有一套统一的账号体系(统一的权限校验系统)。

因为H5使用的webview可以有独立的登录态,如果不加限制太过混乱难以维护,比如:

我们在qq浏览器中打开携程的网站,携程站内第三方登录可以唤起qq,然后登录成功;完了qq浏览器本来也有一个登录态,发现却没有登录,点击一键登录的时候再次唤起了qq登录。

当然,qq作为一个浏览器容器,不应该关注业务的登录,他这样做是没问题的,但是我们自己的一个H5子应用如果登录了的话,便希望将这个登录态同步到native,这里如果native去监控cookie的变化就太复杂了,通用的方案是:

Hybrid APP中,所有的登录走Native提供的登录框

每次打开webview native便将当前登录信息写入cookie中,由此前端就具有登录态了,登录框的唤起在接口处统一处理:

 1 /*
 2 无论成功与否皆会关闭登录框
 3 参数包括:
 4 success 登录成功的回调
 5 error 登录失败的回调
 6 url 如果没有设置success,或者success执行后没有返回true,则默认跳往此url
 7 */
 8 HybridUI.Login = function (opts) {
 9 };
10 //=>
11 requestHybrid({
12     tagname: 'login',
13     param: {
14         success: function () { },
15         error: function () { },
16         url: '...'
17     }
18 });
19 //与登录接口一致,参数一致
20 HybridUI.logout = function () {
21 };

回退更新

我们在hybrid中的跳转,事实上每次都是新开一个webview,当A->B的时候,事实上A只是被隐藏了,当B点击返回的时候,便直接将A展示了出来,而A不会做任何更新,对前端来说是无感知的。

事实上,这个是一种优化,为了解决这种问题我们做了一个下拉刷新的特性:

 1 _.requestHybrid({
 2     tagname: 'headerrefresh',
 3     param: {
 4         //下拉时候展示的文案
 5         title: '123'
 6     },
 7     //下拉后执行的回调,强暴点就全部刷新
 8     callback: function(data) {
 9         location.reload();
10     }
11 });

但,这个总没有自动刷新来的舒服,于是我们在页面第一次加载的时候约定了这些事件:

 1 // 注册页面加载事件
 2  _.requestHybrid({
 3      tagname: 'onwebviewshow',
 4      callback: function () {
 5         
 6      }
 7  });
 8 // 注册页面影藏事件
 9 _.requestHybrid({
10     tagname: 'onwebviewhide',
11     callback: function () {
12         scope.loopFlag = false;
13         clearTimeout(scope.t);
14     }
15 });

在webview展示的时候触发,和在webview隐藏的时候触发,这样用户便可以做自动数据刷新了,但是局部刷新要做到什么程度就要看开发的时间安排了,技术好时间多自然体验好。

本文由分分快三计划发布,转载请注明来源

关键词: 分分快三计划