weex实践-1

weex 开源项目已经公开近半年了.对于一个标准的android 技术宅来说, 一直希望有一种跨平台的解决方案能够弥补对于其他平台的技术缺失. 一直希望能够设计实现一款通用的APP框架, 可以进行一次开发,全平台适用. 现有weex让我看到了希望. 原因之一或许就是他的js->Native的快速体验.

现在将weex把玩过程中的一些心得记录下来,供他人参考.

一. Weex是什么?

想要使用weex, 需要先想明白的首要问题: Weex是什么? Weex 能做什么?

详细内容参考官方网站介绍:http://weex.apache.org/cn/

1. Weex 是什么?

简单来说, Weex 是利用Vue 框架的语法结构实现的一套UI界面代码,经编译转换后变成一段JS脚本, 最后这段脚本运行在手机的weex引擎后,自动转换成相应平台的原生控件.

最终结果就是能够将我们用HTML 语法写出的 网页UI效果转换成在手机上的原生控件实现.  

同时,Weex引擎允许我们用CSS样式控制页面元素的样式. 允许我们用JS代码写出界面元素的交互实现. 允许我们请求在JS中请求网络,允许我们在JS中持久化存储一些缓存数据.

最后,Weex 允许我们在 Native (JAVA) 代码和 Weex (JS)代码两者间进行顺利交互, 传递参数,相互方法调用. 一些weex 实现 不了的,就转给native实现. Native需要跨平台的就交给weex去实现.

 2. Weex 能做什么?

Weex 能够让我们用Js+css+div 实现一套APP 的UI, 并且实现 界面间的路由和交互.  然后Android +IOS 只需要实现一个外壳程序,用来显示这些UI界面. 最终就能够实现一个90%+的跨平台应用. 人力成本和时间成本基本成一半趋势减少.

如果外壳程序足够完善,那么后续其他需求,都可以复用这个外壳实现,可以做到只需要写js代码就能够实现两个平台的功能.

同时Weex 的根本优势在于, Weex最终会将js代码转换成相应平台的原生控件进行渲染. 在显示效果上将与各自平台的原生控件没有不同,渲染效率也极速. 

最后, Weex 由于是js bundle形式存在, 他可能通过服务器下发方式进行动态更新,对页面进行动态更新替换. 这对于一些动态化要求很高的程序绝对是一大利器.再也不用受iOS这种动不动就一周审核周期的约束. 随时更新. 甚至可以随时进行ABTest 这种页面交互测试.


二. Weex 的先天不足

从前边介绍来看,Weex 已经能够实现 大多数需求. 比如仿微信的程序. 比如类似知乎这种APP. 但在实践过程中还是发现有很多先天不足之处,以下列出一些比较坑的点:

1. 尺寸问题

Weex 的标准尺寸单位是PX, 并且规定了宽度满屏为750PX. 

Weex不接受PX以外的如em, 100%等单位. 因此你无法用50%这样的比例来实现一个占屏幕一半的实现, 你需要进行一定的尺寸计算,比如325PX, 最终不管实际屏幕多大, 他都会显示为屏幕一半. 

另外一个比较坑爹的地方就是在Weex中每一个控件都需要明确长宽的值. 如果不写width/height 的话,控件是死活都不会显示的

此时你或许会非常怀念Android 中的各种布局样式. 绝对的, 相对的, 层叠的,线性的. 只需要一个一个WRAP_CONTENT/MATCH_PARENT 这样的模糊尺寸约束就能够正确的布局.

而坑爹的是有时候你根本不知道你的ImageView到底有多大. 你的TextView到底占多少空间. 

典型例子:

假如你希望一个View 宽度占满屏幕, 高度也占满屏幕, 那么你的宽度应该就是750PX, 高度又该写多少呢?

需要进行计算:  

float  scale = realScreenWidth/ 750f;

float  height = realScreenHeight / scale;

最终height值就是你的控件的高度设值.

如果你的View 需要动态变化大小, 那么你就不能写在CSS样式中了, 而必须写在 <div style="width:{{widthValue}};"></div> 用Vue的数据绑定机制来进行宽高的控制. 但这种是不推荐的做法. 尽量要做到每个控件的大小都是可计算出来的确定的大小.


总之: 在Weex中,处理控件尺寸是一件很麻烦,也很痛苦的事情.  UI设计要以750PX为基准屏幕进行设计.最终就可以以确定的尺寸进行代码编写了.

2. 背景问题

在weex中 想要设置一个控件的背景图片是需要绕路的.  你可以直接给控件设置背景色, 设置渐变色, 但如果想要设置一张背景图片的话. 对不起,不支持. 

因此你需要绕个弯, 在控件内部嵌套一个上下左右不距离为0的 image控件. 用它来显示一张背景图.

这个先天不足也是能够理解的.  

目前的weex只有image 内置组件 能够显示图片,因为Weex 本来就是为移动平台设计的, 暂时无法支持本地图片的显示. 所有要显示的图片都以URL形式写入代码, 

只有image组件 最终会调用 native的 IWXImgLoaderAdapter 接口 进行图片下载并设置给ImageView对象. 也即weex的image组件最终会转换成ImageView对象,提供给接口进行图片设置.

这里我们需要解决的问题有两个: 

 A. 如何能够支持显示本地图片,图标.  一些很简单的导航菜单等图标,如果还要依赖网络下载的话,简直就有点蛋疼了. 在网络慢的时候,甚至无法显示出一个完整的菜单图标.

这对于商业级产品显然是不行的. 所以必须要能够做到支持本地图片显示. 

 这个问题也好解决, 大家可以看 IWXImageLoaderAdapter 接口:

  
  
  
  
@Override
public void setImage(String url, ImageView view, WXImageQuality quality, WXImageStrategy strategy) {
//实现你自己的图片下载,否则图片无法显示。
}

其中的url 就是在Weex页面写入 image 的src字段的值.  他不仅可以写 http://xxx形式的链接 , 他可以写任意形式的字符串, 你可以写:"R.drawable.logo" , 可以写"logo.png", 可以写"本地文件相对路径" 等. 具体就看你如何识别这个url 是何种文件, 进而进行相应的图片读取即可.  唯一要注意的是 要兼顾不同平台的 本地文件表示形式, 要找到一种两个平台通用的能够找到文件的格式. 直接写文件名是可行的. 

在iOS中, 读取图片仅需要@"logo.png", 而在Android中 你可以进而组装成assets资源路径,或者如:R.drawable.logo 这样的格式 去访问本地图片.


B.  如何让其他类型的控件方便的支持背景图片的设置. 如果每一个要显示背景的控件都去嵌套image 的话,势必造成控件的嵌套层次和冗余, 不利于界面的快速渲染.

我们知道, 不管是Android,还是IOS, 其原生控件都是支持设置背景图片的.  而Weex 究其原理,最终也是将div等控件转换成原生的相应控件. 那为何无法设置背景图片呢?

原因可能是考虑到其他控件设置背景图片的URL无法及时下载,而导致界面显示看起来需要等一下才显示一个背景这样的现象. 毕竟,我们是在移动终端的低速网络下生活着.

但很多时候我们的界面就是因为一张图片而变得酷炫. 所以我们需要解决这个问题. 

解决方案:

既然Image 都可以共享出接口用来下载并显示图片. 那为何其他控件我们不能用同样的思路去解决.但这需要对weex的源码有一定的研究. 

我们需要找到控件从js 转换成View的 那一时刻. 然后做到类似ImageView 设置图片的接口.并在Native端进行处理即可.

这实际已经在对Weex进行一些高级的扩展了. 具体实现 后续再讲.

3. 皮肤问题/国际化问题.

有做过大型项目的同学都知道, 我们的应用最基本的语言国际化, 以及日夜间模式,皮肤更换等,都是必配内容. 这里的适配包括:

A. 语言文字的国际化,多语言适配.不能为外国用户单独发布一个英文包吧.

一种思路是,针对每种语言实现多个语言的bundle, 在客户端根据不同的语言设置下载相应语言的bundle文件,并进行执行. 但显然,这里的缺陷非常明显, 所有代码都是一样的, 不同的只是文字内容. 而我们却要把每种语言的bundle都重复写一遍,重复下载一遍,重复执行一遍.  耗时,耗力,管理不便.

有没有类似Android 中那种将字符串提取出来,并在需要时动态加载语言文件的方法呢.

从技术角度,是可行的.  我们将所有需要的字符串定义为javascript对象.或者javascript数组. 而在需要字符串的位置同样用占位符替代. 这样在weex端即可通过下载不同的语言文件,进而显示不同的语言. 

当然这里要考虑的是网络因素带来的不确定性, 用户无法忍受界面 每次显示英文前,首先显示一些默认的不认识的语言文字.  因此最好的办法,仍然是由本地存储这些字符串,并通过与weex的交互将字符串传递给weex. 

需要weex端以及客户端进行配合,实现一套从本地读取字符串的业务逻辑. 具体实现以后再讲.


B. 字体适配,有很多应用允许用户自定义字体, 特别是一些阅读类的APP. 适合用户的才是最棒的.

      字体适配主要针对文本类控件, 首先字体可能是本地内置支持的. 另一种可能是缓存在本地的文件. 也有可能是直接从网络上下载的.

     一般一个字体文件动折5M,10M. 不太可能全部保存在APP中, 因此通常做法是由用户选择一种字体文件,并进行下载后,应用到界面.

    weex本身不提供修改text 类内置组件的字体. 但我们知道,weex最终的根本产物还是TextView,  我们需要给Weex增加功能, 类似setSize 这样的接口,当用户下载好某字体后,通过接口设置,应用给weex引擎, 然后由引擎在解析jsbundle时, 将当前字体应用给text组件.


C. 日夜间模式适配,  到了夜间,用户的视觉对强光会非常敏感, 因此需要通过降低亮度, 降低UI色彩效果来减少对用户视觉的刺激. 这是属于用户体验很重要的一环.

     一种方案,可以将日夜间模式当成是某种通用皮肤适配. 对每一个需要改变色彩的控件都赋予动态更换颜色/背景, 文字颜色, 图片颜色增加滤镜等. 

     日夜间模式由皮肤模块去实现. 但夜间模式通常会包含比皮肤更多的控件需要修改颜色. 界面上不能出现 任务原有的较亮的控件, 都需要进行一番适配. 而皮肤可能仅仅修改了若干字体颜色, 修改了一些控件的背景等.

     因此从原理上说,这并不是一个好的方案. 但暂且先归于一类问题. 


D. 皮肤更换. 用户对于一些个性的设置是非常有吸引力.皮肤属于一种.  如何能够更换皮肤也是Weex需要考虑的事情.

     皮肤,包含一组资源, 包含字体资源,界面控件背景/背景图片, 字体颜色列表,一些弹窗样式设定,标题栏样式,图标样式(类似Launcher能够控制图标边缘效果),

这其中有一部分资源是需要在外壳层生效的. 比如标题颜色/背景等. 有一部分是需要weex端去处理的. 如图片样式,控件背景等.

这里需要实现的内容有:

1. 皮肤资源的下载,解压.

2. 皮肤资源在外壳中的动态替换,应用.

3. 皮肤资源在weex代码中的动态替换.

皮肤设置中有一部分是全局通用的,如字体应用,将应用到全局所有文本.

另一部分则是由界面自主决定的. 如某一个weex页面想要背景变成某种色彩. 因此可以将此背景色用变量来表示.在想要设置时,去跟皮肤模块动态拿色彩.

还有一部分是图片,图标类的控件. 如菜单栏图标想要换个样子的. 同样控件的图片设置就不能再以一个确定的url来表示了, 而是要以一个约定的变更来表示. 则应用皮肤时可动态根据变量的当前值进行图片加载.

具体实现可以做成皮肤的插件包进行弱依赖.

4. weex 控件ID 问题

我们知道,Android 里对每一个控件都可以定义一个数字形的ID, 用于简单查找控件,并进行操作的.  现在界面变成了weex实现, 我们还能否对其进行方便的操作呢.显示不太方便了. 因为weex中的控件 以ref="xxx" 字符串形式区别的. 我们不能再以findViewById 去控制View了. 而控制的层次由Native转入Weex中去, 而我们则只能通过与weex进行交互而间接操作View.

这也限制我们尽量使用weex去实现尽可能多的UI以及交互逻辑. 外壳代码尽量做到完全与业务无关. 如果解决了以上提到过的各种问题, 用weex实现全部功能是可行的.

需要尽快去适应这种从findViewById 转到 通过 ref 来操作元素的方式. DOM API 可以方便我们对元素的属性和样式进行简单操控.

5. Vue 文件编写

现有的教程,基本上都是将template/ style/ script 混合在一起, 在简单界面时没有问题,但当一个界面几十上百个控件,以及上千行控制逻辑时,代码就非常粗陋不堪了. 因此习惯上我们 应该对每个Vue文件进行拆分, 将三者分离开来.   因此,理想中的一个完整的界面包含三个文件: xxx.VUE ; xxx. js; xxx.css.  后两者在VUE文件中通过@import 进行导入. 

同时css文件可以用 sass /less框架等替代. 用于写出更清晰,更简明的,样式表. 这些都是能够完美实现跨平台的.不用担心兼容性问题.


6. ROUTER

简单讲Router 就是一套能够让你从当前weex页面跳转到其他页面的路由机制. 形象一点的类比如Android 中的Activity之间的跳转. 实际由ActivityManager实现的一套页面跳转机制. 可以自动缓存上个页面,可以让你前进/后退显示上一个/下一个页面等. 或者允许你直接跳到一个指定的页面. 而路由还控制着页面的生命周期,类似Activity的生命周期控制.

Vue帮我们实现了一套Router 逻辑, 可以直接使用,也可以借鉴实现 我们自己的路由操作规则. 
最终目标就是能够通过一个页面标识找到页面,并加载显示. 并且需要方便的在各页面间跳转和传递参数.

这里要注意的一个点:
如下场景: 我们在一个页面填写了用户信息,然后跳到下一个页面. 此时用户返回上个页面, 期望是上个页面填写的信息还在. 这在weex路由时需要考虑的. 即当源weex页面即将消失时需要能够保存现场.类似Activity的saveInstance操作.以便在页面重新显示时恢复数据.  但如果是主动销毁了某个weex页面, 则其缓存的数据也应当立即销毁,而不是在下次显示页面时出现脏数据.


三. 基于Weex的应用架构.




以上为理想的设计框架,其中一些模块可以根据需要进行取舍.










你可能感兴趣的:(ios,android,weex)