某手机品牌市场营销项目总结

前言

本次的发开是全程参与的,所以算是第一个跑完了完整的流程的项目,很有纪念意义呀。

框架及数据结构

  • MVC

  • SAP UI5(高度封装的jQuery库)

  • lodash

  • moment

  • odata services

预热

  • 首先是各个前端坐在一起讨论出来的一套前端代码规范:
    Controller:

    多个Controller都会用到的公用方法请放到/src/common/BaseController.js中
    各个页面的Controller extends BaseController (instead of standard controller)

  • View:

    使用XML View
    当View比较复杂的时候, 请将View拆分为多个fragment

  • Model:

    使用ODataModel V2

  • Constants:

    用到的常量请统一放到/src/common/Constants.js里面
    常量以大写字母和下划线组成

  • Formatter:

    多个Controller都会用到的Formatter请放到/src/common/Formatter.js中
    单个Controller用到的Formatter请放在Controller对应的Formatter文件中
    在Controller中用Formatter = _.assign(CommonFormatter, ControllerFormatter) 进行组合

  • Utils:

    多个Controller都会用到的utility function请放到/src/common/Utils.js中
    单个Controller中定义的utility function请放到Controller对应的helper文件中
    Utility function均为pure function

  • Css:

    Css文件放在/src/css目录下
    多个页面会用到的style放到style.less文件中
    单个view会用到的style放到[VIEW_NAME].less文件中
    不强制使用less语法

  • Internationalization:

    页面中出现的所有Label请在i18n中定义
    所有Label支持中英文两个版本,分别放在i18n_zh.properties和i18n_en.properties两个文件中
    默认为英文版本

  • Localization:

    金额格式(千分位,小数位)由Locale (或language)决定, 不能hardcode
    日期, 时间格式由Locale (或language)决定, 不能hardcode

  • Name Convention:

    变量名(except for Constants),函数名, 文件夹名遵循camelCase, 文件名首字母大写
    i18n命名用全大写+下划线, 第一个单词表示控件类型, 第一部分为common, 后面按页面划分, 写注释
    全部用双引号

  • Neatness:

    函数不可以过长(最长不超过40行), 过长函数请拆分为多个函数
    使用moment&lodash等utility库来简化代码

  • Others:

    用MessageManager来做前后端错误消息的统一管理
    用EventBus来做消息广播以及页面之间的消息传递

  • Comment Format:

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * Convenience method for setting the view model in sub controllers.
    *
    * @param {sap.ui.model.Model} oModel the model instance
    * @param {string} sName the model name
    * @returns {sap.ui.mvc.View} the view instance
    *
    */

其次是实验阶段,实验UI5里可能使用到的组件接受渲染的数据结构,再和后台沟通定义。

认真研究UI5的文档官方文档1.52.9版本

正式开发中踩过的坑及解决方案

  • UI5官方的坑中之坑-json model的loaddata方法
    可以看出来这个官方文档就很坑。
    首先,bCache这个参数默认的是给的false,但是type却给的是string???[黑人问号脸]
    其次,他的cache都默认给的false了,但是还是会去拉缓存,真的让人觉得很绝望呢!!!(亲测1.52.9版本)

    后来我去看了最新版的官方文档,还拉不拉缓存我不清楚了,但是文档上false对应string类型我现在还处在震惊之中(目前的最新版本是1.6)

  • 还有一个UI5的官方bug… 真的很坑呀。有一个组件datePicker这样使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var oDate = new sap.m.DatePicker("datepicker", {
    value: {
    path: 'date',
    type: 'sap.ui.model.type.Date',
    formatOptions: {
    style: 'full'
    }
    }
    });

    在1.52.9版本会有这个问题dete picker初始点击的时候会闪跳一下,后面就正常了。后来换了一个最新版本就没有这个问题了,可以在这个链接里实验一下。

    试验点这里

  • oData Model的两个可以使用POST的封装方法都不支持深层次结构!!!
    但是team里有人用create方法处理深层次结构,我有点蒙,不太明白为什么可以~ 所以还是使用的jQuery的ajax方法。

  • 在处理上传图片的业务的时候因为是上传到阿里云,而且ux的需求是上传后的图片要显示出来,所以最开始的方案是用户上传图片直接上传到阿里云,然后通过返回的一个加了签名的URL去访问,但是后来发现由于业务逻辑的复杂,可能会导致阿里云上文件的冗余。于是采用的是,在前端做保存,统一提交保存到后台的时候,再去做上传的处理。这样避免了很多业务逻辑去判断很多东西。而将上传的图片在本地做转码成base64的格式,这是一个js原生的方法。使用如下:

    1
    2
    3
    4
    5
    6
    7
    let reader = new FileReader();
    reader.readAsDataURL(files);
    reader.onload = function(e) {
    img.setSrc(this.result);
    };
    //files是上传的而图片
    //dataUrl除了this.result表示 如果用e的话就是e.currentTarget.result
  • 在项目后期,ux提了一个bug,就是路由不同层级之间跳转的出入场方式不对。后来去翻阅了UI5的官方推荐使用里面,我们在manifest里面找到一个在target里使用的属性viewLevel。他会根据定义的层级不同去change出入场方式。代码如下:

1
2
3
4
5
6
7
8
9
10
11
"targets": {
"overview": {
"viewName": "Overview",
"viewPath": "xxx.xxxx.xxxx.xx.overview",
"viewLevel": 1
},
"notFound": {
"viewName": "NotFound",
"viewPath": "xxx.xxxx.xxxx.xx.notfound",
"viewLevel": 2
}
  • 在项目中,因为我们有两个team,所以在只读态跳转到编辑态的时候实现的效果出现了偏差。我们只读态和编辑态因为使用的组件都有所不同,所以拆成了两个view去处理,而team 2是采用复用的方式,但也不是完全复用。是针对只读和编辑拆成了两个fragment,这样在切换状态的时候就是没有滑动效果的。但是为了风格的统一,我们找到了一个比较好的解决方案就是拆成两个路由。如果直接从list页勾选一条进编辑态那么路由就是edit,如果是从单条信息的只读态切换到编辑态,那么路由就是editShow并且为两个路由写两个对应的target,但其中一个target的transition就要配成show因为在manifest里面,路由之间切换默认的是slide的方式。

    routing config:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    "routing": {
    "config": {
    "routerClass": "sap.m.routing.Router",
    "viewType": "XML",
    "viewPath": "xxx.xxx.xx.view",
    "controlId": "app",
    "controlAggregation": "pages",
    "transition": "slide",
    "bypassed": {
    "target": "notFound"
    },
    "async": true
    }

    route:

    1
    2
    this.getRouter().addRoute({ name: "edit", pattern: "edit/{id}", target: "edit" });
    this.getRouter().addRoute({ name: "editShow", pattern: "editShow/{id}", target: "editShow" });

    target:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    "edit": {
    "viewName": "Edit",
    "viewPath": "xxx.xxxx.xxx.edit",
    "viewLevel": 2
    },
    "editShow": {
    "viewName": "Edit",
    "viewPath": "xxx.xxxx.xxx.edit",
    "viewLevel": 2,
    "transition": "show"
    }

    虽然route和target都不是一样的,但他们指向的view文件都是同一个,这样就能避开直接使用私有属性去修改transition因为UI5没有把修改transition的方法暴露出来,真的很不方便。

  • odata model 的read方法有一个属性叫urlParameters里面可以写一些参数在请求的时候会带上,如expand、select等等,但是要注意,自己写这些参数的时候要自己加上$,如:

1
2
3
4
5
6
7
odataModel.read(url, {
urlParameters: {
$expand: "xxxx"
},
success: (data) => {},
error: (data) => {}
)
  • 在我们的项目中,有用到阿里云的服务器,存储文件、图片等资源。所以使用了OSS对象,但是因为初期时间紧张的关系,在前端处理的时候一直是hard code。但是到后期有要求通过后台一个暴露的接口去控制。但是D、Q系统都是用的是一样的OSS。这期一个铺垫,到快上线的时候,突然就爆发了一个问题,确实是我自己的失误,我只记住修改controller里对这个的使用了,但是因为时间比较久了,我忘记了formatter里面也是用了这个对象,但是D、Q系统都是一样的,所以完全测不出来这个bug,但是到了生产系统,就开始暴露出这个问题了。所以,我也在这件事情之后告诫自己,全局替换的去查找,而不是凭记忆。也要求自己更严谨对待这些事情。但是从外部的角度,也需要考虑,D、Q系统使用同一个OSS但是生产系统不是,这是需要冒着很大的风险的,开发自己在D系统测不出来,测试在Q系统也测不出来,所以,如果有bug,必然只有生产系统才会知道。这对质量管控是非常不友好的。

总结

虽然UI5有很多坑点,但是和后台交互集成的过程,更为漫长…

首先一定要阻止后台私自修改字段和表结构但不告诉前台!!!

其次还要阻止在后台随便建一些脏数据

在UI开发的时候,应该对照ux稿事先和后台同事商量好数据结构和必要的判断逻辑

并且在开发的时候遵循开发规范,这样在别人去维护你的代码的时候,更明确逻辑,浪费更少的时间。