这篇文章是笔者近期关于Weex在iOS端的一些研究和实践心得,和你们一块儿分享分享,也算是对学习成果的总结。文章里面提到的作法也许不是最佳实践,也许里面的方法称不算是一份标准的指南手册,因此标题就只好叫“伪最佳实践指北”了。有更好的方法欢迎你们一块儿留言讨论,一块儿学习。html
因为笔者不太了解Android,因此如下的文章不会涉及到Android。前端
自从Weex出生的那一天起,就没法摆脱和React Native相互比较的命运。React Native宣称“Learn once, write anywhere”,而Weex宣称“Write Once, Run Everywhere”。Weex从出生那天起,就被给予了一统三端的厚望。React Native能够支持iOS、Android,而Weex能够支持iOS、Android、HTML5。vue
在Native端,二者的最大的区别可能就是在对JSBundle是否分包。React Native官方只容许将React Native基础JS库和业务JS一块儿打成一个JS bundle,没有提供分包的功能,因此若是想节约流量就必须制做分包打包工具。而Weex默认打的JS bundle只包含业务JS代码,体积小不少,基础JS库包含在Weex SDK中,这一点Weex与Facebook的React Native和微软的Cordova相比,Weex更加轻量,体积小巧。react
在JS端,Weex又被人称为Vue Native,因此 React Native 和 Weex 的区别就在 React 和 Vue 二者上了。webpack
笔者没有写过React Native,因此也无法客观的去比较二者。不过知乎上有一个关于Weex 和 React Native很好的对比文章《weex&React Native对比》,推荐你们阅读。ios
前两天@Allen 许帅也在Glow 技术团队博客上面发布了一篇《React Native 在 Glow 的实践》这篇文章里面也谈了不少关于React Native实践相关的点,也强烈推荐你们去阅读。git
关于小白想入门Weex,固然最基础的仍是要通读文档,文档是官方最好的学习资料。官方的基础文档有两份:github
在文档手册里面包含了Weex全部目前有的组件,模块,每一个组件和模块的用法和属性。遇到问题能够先过来翻翻。颇有可能有些组件和模块没有那些属性。npm
看完官方文档之后,就能够开始上手构建工程项目了。
我司在知乎上面写了4篇关于《Weex入坑指南的》。这四篇文章仍是很值得看的。
Weex也和前端项目同样,拥有它本身的脚手架全家桶。weex-toolkit + weexpack + playground + code snippets + weex-devtool。
weex-toolkit是用来初始化项目,编译,运行,debug全部工具。
weexpack是用来打包JSBundle的,实际也是对Webpack的封装。
playground是一个上架的App,这个能够用来经过扫码实时在手机上显示出实际的页面。
code snippets这个是一个在线的playground。
我相信你们应该都有Native的App,若是真的App都没有,那就用weexpack命令初始化一个新的项目。若是已经有App项目了,那么weex命令就只是用来运行和调试的。
已经有iOS项目的,能够经过cocospod直接安装Weex的SDK,初始化SDK之后,Native就可使用Weex了。加载的JS的地址改为本身公司服务器的IP。
#define CURRENT_IP @"your computer device ip"
// ...
// 修改端口号到你的端口号
#define DEMO_URL(path) [NSString stringWithFormat:@"http://%@:8080/%s", DEMO_HOST, #path]
// 修改 JS 文件路径
#define HOME_URL [NSString stringWithFormat:@"http://%@:8080/app.weex.js", DEMO_HOST]复制代码
这样整个项目就能够跑起来了。
这里还有一点须要说明的是,项目虽然跑起来了,可是每次运行都须要启动npm,打开Weex的前端环境。这里有两个作法。
第一种作法是直接Hook Xcode的run命令,在Xcode配置里面加入启动npm的脚本。好比下面这样:
第二种作法就是每次运行以前,本身手动npm run dev。我我的仍是喜欢这种方式,由于在Xcode运行完成以前,必定能够在命令行上面打完这些命令。
再说说如何Debug,这块使用的是weex-devtool。
这个工具和前端在Chrome里面调试的体验基本相同。
具体使用方法看这两篇文章便可,这里再也不赘述:
《Weex 入坑指南:Debug 调试是一门手艺活》
《Weex调试神器——Weex Devtools使用手册》
在平常开发中,咱们能够所有本身开发完全部的Weex界面,固然还能够用一些已有的优秀的轮子。Weex的全部优秀的轮子都在Weex Market里面。
在这个Market里面有不少已经写好的轮子,直接拿来用,能够节约不少时间。
好比这里很火的weex-chart。weex-chart图表插件是经过g2-mobile依赖gcanvas插件实现的
若是你想使用Weex Market的Plugin插件,你可使用weex plugin 命令:
$ weex plugin add plugin_name复制代码
你只须要输入插件的名称就能够从远程添加插件到你本地的项目,好比添加 weex-chart,咱们能够输入命令:
$ weex plugin add weex-chart复制代码
咱们可使用plugin remove移除插件,好比移除安装好的 weex-cahrt:
$ weex plugin remove weex-chart复制代码
这个插件库里面我用过weex-router,还不错,用它来作weex的路由管理。推荐使用。
weex官方提供了weexpack命令。我以为这个命令是提供给不懂iOS的前端的人用的。若是是Native来打包,依旧使用的Xcode的Archive打包。
彻底不懂iOS的前端开发者可使用weexpack build ios 打包,中间会要求输入证书,开发者帐号等信息。都输入正确之后就能够打出ipa文件了。全程傻瓜操做。
若是是iOS开发者,原来怎么打包如今仍是怎么打包。只很少JS这块要单独进行打包。建议是把Weex这块单独用一个git分支进行管理,专门针对这个分支进行weexpack或者Webpack进行打包。webpack的具体配置由每一个公司本身配置。
这里额外说一点,这一点也是前端大神告诉个人。webpack打完包之后是能够经过webpack官方网站查看这个包里面究竟打入了哪些文件和依赖。虽然我打包都是一股脑的都打完,可是资深前端开发也许还会再去检查一下是否有多的文件被打进去了。极限压缩包的体积,1KB的文件也很少放进去。
再谈谈发布的问题。因为有了Weex之后,每次发布都会把上个版本累计到这个版本的hotPatch都累计修复掉,并在新版里面直接内置最新的JSBundle文件。内置JS的目的也是为了首屏加载秒开。
关于热更新的做用你们都明白,否则用Weex的意义就少了好多。不过这里还有一点须要说明的是——热更新的策略。
在平常开发过程当中,咱们在浏览器上面连着手机调试,也并非实时刷新的。(不过经过在手机上扫描二维码,而且手机和电脑在同一个局域网以内,能够作到实时更新)
因此在实际生产环境中,热更新的策略应该是这样:有新的HotPatch就下发到客户端,而后客户端在下次启动的时候,先比对版本信息,若是是新版本,就去加载这个最新的HotPatch,而后渲染在屏幕上。
曾经我幻想着能实时在线更新,就是线上一发布,全部用户在联网的状况下,下发HotPatch完毕之后直接加载,联网的用户能够实现秒级别的热更新。这种虽然能够作到,可是意义不大。作法是专门维护一套Websocket,直连服务器,下发完毕之后能够经过调用Native的通知,Native客户端本身刷新页面便可。(目前应该没有多少公司是这样作的吧?)
关于JSBundle的版本管理这块是应该交给前端来管理。前端可能会用版本号来管理各个包的版本。部署也会牵扯到每一个公司前端部署的流程。他们会更加了解。部署通常也会放到CDN上加速。
若是说Weex一点坑都没有,那是不可能的。
好比说在某些界面连续Push的时候,页面边缘会有一些线条从屏幕上扫过。还有捕捉JS错误或者异常的时候,Weex并不能可靠的捕捉到异常,这点须要靠Native来作,Native捕捉到异常之后再传递事件给JS Runtime去处理。
计算页面宽高尺寸这点是最须要注意的。Weex进行界面适配的时候是用750为标准的,因此须要根据750去换算。还有一点是Weex里面有四舍五入的操做,是会丢失一点精度的。具体这块请看《Weex 事件传递的那些事儿》这篇文章里面的源码分析。
Weex JS 引擎也不支持 HTML DOM APIs 和 HTML5 JS APIs,这包括 document, setTimeout 等。
Weex关于Web标准的实现如今尚未达到100%,因此用Vue来写Weex的话,有些是不支持的。
好比说一些CSS样式,最使人想不到的就是不支持< br>,还不支持< form>,< table>,< tr>,< td>,不支持CSS percentage 单位,不支持相似 em,rem,pt 这样的 CSS 标准中的其余长度单位。不支持 hsl(), hsla(), currentColor, 8个字符的十六进制颜色。
Weex对W3C上的FlexBox的规范也没有支持彻底,暂不支持inline,也不支持Z轴上面的变化,不过移动端在Z轴上的需求真的没有。Weex的Layout是用的Yoga以前的某个版本,解决问题的方式也比较直接,后期升级到最新版的Yoga,即可以支持更多的Flex的标准了。
具体还有不支持的就要多翻翻文档,好比这里的《Weex 目前不支持的Web 标准有哪些》。这些最好先看看,内心有个数,以避免开发时候遇到一些莫名的bug,却不知最终是由于不支持致使的。
而后还有一些是组件暂时还不支持同步方法。这里是Vue 2.0还不支持,官方预计是在 0.12 版本支持。
额外提醒一点,因为苹果前段时间对JSPatch的封杀,因此致使Weex官方对自定义模块给出了一个警告:
Weex 全部暴露给 JS 的内置 module 或 component API 都是安全和可控的, 它们不会去访问系统的私有 API ,也不会去作任何 runtime 上的 hack 更不会去改变应用原有的功能定位。
若是须要扩展自定义的 module 或者 component ,必定注意不要将 OC 的 runtime 暴露给 JS , 不要将一些诸如 dlopen(), dlsym(), respondsToSelector:,performSelector:,method_exchangeImplementations() 的动态和不可控的方法暴露给JS, 也不要将系统的私有API暴露给JS
上述警告特别强调了不要用dlopen(), dlsym(), respondsToSelector:,performSelector:,method_exchangeImplementations()这几个函数。这也是为何一样是用Weex有些人没有经过审核,有些人却能经过审核的缘由。
据说安卓上有Refresh Control的一些bug,安卓在Weex上的表现我没有怎么了解过,不过这块若是出如今iOS上,我以为能够直接用Native来替换掉这块,有bug的地方都用原生来作。
总之Weex仍是多多少少有一些问题,可是目前使用来看,不影响使用,只要懂得灵活变通,遇到实在过不去的坎,或者是真的一时hold不住的bug,那么多考虑用原生来替代。
接下来讲一下稍微高级的玩法。如下这些即便没有作,也不影响Weex正常上线。
Weex默认是支持页面降级的。好比出现了错误,就会降级到H5。这里建议最好作一个线上的开关。我司在处理页面降级的问题上采起了两种级别的开关:
除了降级之后,还对应采起了灰度的策略,这样保证线上bug下降到最低。
好比在用户量低峰期的时候开启开关进行灰度。还有一级灰度就经过线上实时错误监控平台来控制,若是由于突发事件致使Crash率陡升,那么就当即关闭Weex的开关,当即进行降级处理。
在Weex给的官方Demo里面有一个M的小圆点浮框,点开会看到以下的界面:
在这里咱们点开性能的按钮:
在这里咱们能够看到监控了CPU,帧率,内存,电量,流量等数据,这些数据也是咱们在Native APM中监控的常见数据。固然,这个M圆点并不没有开源。因此这块须要各个公司本身作一套本身的监控系统。这块可能每一个公司的前端已经作好了,因此Weex须要接入到前端的性能监控里。
若是咱们再点开工具的界面,就会看到以下的选项:
这里就有埋点监控。在初期可能Weex埋点仍是由Native进行埋点,由于各家都有自家的Native完整的埋点系统了。后期埋点这块也能够交给前端在前端埋点。
暂时笔者尚未实践过Weex的增量更新,因此这里就不提增量更新了。全量更新就比较简单,下发整个JSBundle,App在下次启动的时候再加载便可。Weex的包比RN的包小不少,通常就100-200K左右。阿里的一次Weex分享里面提到他们gzip压缩之后能达到60-80K。
上图是阿里在Weex Conf大会上提出的一个挑战,网络请求加上首屏渲染的时间加起来小于1秒。
这里面涉及到3方面的因素,网络下载耗时,JS和Native通讯耗时,还有渲染耗时。
网络下载耗时能够经过支持HTTP / 2,配置Spdy协议,域名收敛,支持http-cache,极致压缩JSBundle的大小,JSBundle预加载。
JSBundle预加载的时候能够在App启动时候预先下载JS。远程服务器推包的时候经过长连通道Push,这里能够是全量 / 增量,被动 / 强制更新相互结合。
阿里关于JS和Native通讯耗时,渲染耗时的相关优化见上图。这两方面笔者也没有相关的实践。
虽然Weex有属于它本身的全家桶,可是在支持了Vue 2.0之后,它的全家桶彻底能够换成Vue的全家桶。Vue,Vue-Router,Vuex,原来还有Vue-resource,不过尤大后来去掉了这个Vue-resource,更加推荐axios了。因此全家桶里面就是Vue,Vue-Router,Vuex,axios。
若是所有都换成了Vue之后,那么前端首屏渲染的速度就须要Vue来解决了。为了提升首屏渲染速度,wns缓存+直出 是必不可少的。在Vue 1. x 时代,没有 server-side-render 方案,直出须要专门给写一份首屏非Vue语法的模板。Vue2.0 server-side-render(简称Vue SSR)的推出,成功地让先后端渲染模板代码同构。
若是只用了Vue-Router之后,当打包构建应用时,JSBundle 包会变得很是大,影响页面加载。若是咱们能把不一样路由对应的组件分割成不一样的代码块,而后当路由被访问的时候才加载对应组件,这样就更加高效了。
结合 Vue 的 异步组件 和 Webpack 的 code splitting feature, 轻松实现路由组件的懒加载。减小JSBundle的体积。
还有一点须要注意的是,Vue-Router 提供了三种运行模式:
hash : 使用 URL hash 值来做路由。默认模式。
history : 依赖 HTML5 History API 和服务器配置。
abstract: 支持全部 JavaScript 运行环境,如 Node.js 服务器端。
不过Weex 环境中只支持使用 abstract 模式!
就在7天前,Vue 发布了v2.3.0版本,官方支持了SSR。因此在支持了SSR之后,能够大幅提高SEO,也能够作到首屏秒开。因此为了性能,SSR必作!
最后的最后,还有一些“前瞻性”的玩法。
JS service 和 Weex 实例在 JS runtime 中并行运行。Weex 实例的生命周期可调用 JS Service 生命周期。目前提供建立、刷新、销毁生命周期。
这块我在官方的Demo里面也没有找到相关的例子。在官方的文档里面有相关的例子。在官方手册里面有这样一句话:
重要提醒: JS Service 很是强大但也很危险,请当心使用!
可见,这块很是强大,也许能够作不少“神奇的”事情。
在官方手册《拓展JS framework》这一章节里面,提到了能够横向拓展JS framework。这个功能可能通常公司都不会去扩展。
Weex 但愿可以尊重尽量多的开发者的使用习惯,因此除了 Weex 官方支持的 Vue 2.0 以外,开发者还能够定制并横向扩展本身的或本身喜欢的 JS Framework。
定制完本身的JS Framework之后,就可能出现下面的代码:
import * as Vue from '...'
import * as React from '...'
import * as Angular from '...'
export default { Vue, React, Angular };复制代码
这样还能够横向扩展支持Vue,React,Angular。
若是在 JS Bundle 在文件开头带有以下格式的注释:
// { "framework": "Vue" }
...复制代码
这样 Weex JS 引擎就会识别出这个 JS bundle 须要用 Vue 框架来解析。并分发给 Vue 框架处理。
因此,Weex 支持同时多种框架在一个移动应用中共存并各自解析基于不一样框架的 JS bundle。
能够支持多种框架并存这点很是强大,固然尚未完,one more thing……
若是正常使用API,看官方文档,不开源码,是不会发现Rax的身影的。官方文档丝毫没有说起到它。
Rax是什么呢?
在《淘宝双促中的 Rax》这篇文章里面介绍了Rax:
Rax 是一个基于 React 方式的跨容器的 JS 框架。
Rax 通过 gzip 之后的大小 8k,与 Angular、React、Vue 相比更加轻量。相比React的43.7kb,小了太多。
Rax 在设计上抽象出 Driver 的概念,用来支持在不一样容器中渲染,好比目前所支持的:Web, Weex, Node.js 都是基于 Driver 的概念,将来即便出现更多的容器(如 VR ,AR等),Rax 也能够从容应对。Rax 在设计上尽可能抹平各个端的差别性,这也使得开发者在差别性和兼容性方面不再须要投入太多精力了。
若是说RN和Weex这些技术是用来跨端的技术,那Rax是用来跨容器的:Browser、Weex、Node.js等。
那么Weex里面加了Rax能干些什么事情呢?值得期待!