点我达骑手端weex探索

Posted 点我达技术团队

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点我达骑手端weex探索相关的知识,希望对你有一定的参考价值。

前 言

由于公司采用weex来开发项目,在开发过程中遇到了很多坑,在这种背景下花了几天时间来研究一下weexSDK的源码。如果有理解不当的地方欢迎指正。

1. ios weexSDK 概述

Weex 是一套简单易用的跨平台开发方案,能以 web 的开发体验构建高性能、可扩展的 native 应用,为了做到这些,Weex 与 Vue 合作,使用 Vue 作为上层框架,并遵循 W3C 标准实现了统一的 JSEngine 和 DOM API,这样一来,你甚至可以使用其他框架驱动 Weex,打造三端一致的 native 应用。

点我达骑手端weex探索

点我达骑手端weex探索

2. weex工作原理

点我达骑手端weex探索以上是weex官方提供的原理图

Weex把JS Framework内置在客户端的SDK里面,用来解析从服务器上下载的或本地加载的JS Bundle,客户端拿到JS Bundle以后,传给JS Framework,JS Framework解析完成以后会输出Json格式的Virtual DOM,客户端Native只需要专心负责 Virtual DOM 的解析和布局、UI 渲染。然而这一套解析,布局,渲染的逻辑SDK基本实现。最后,weex通过3端sdk解析JS Bundle 实现iOS/android/html5 三端的一致性。

3. weexSDK 基本使用

1.使用WXAppConfiguration 配置app基本信息,初始化weex引擎[WXSDKEngine initSDKEnvironment]
2.使用WXSDKInstance加载url

4. weexSDK 源码分析

     4.1 weex引擎初始化的过程

     4.2 weex引擎加载weex页面的的过程

     4.3 weex页面与native交互的过程

     4.4 总结

4.1 weex引擎初始化的过程
4.1.1简要概括
  1. 使用WXAppConfiguration加载app的基本配置包括AppName,AppGroup等等

  2. 通过JSContext加载jsframework main.js文件进内存备用

  3. 在jsframework 加载完毕的前提下通过JSContext调用的jsframework的                 registerComponents方法加载默认组件  

  4. 在jsframework 加载完毕的前提下通过JSContext调用的jsframework的registerModules方法加载默认模块

  5. 将默认的handlers加载给WXHandlerFactory的handlers存储起来备用

4.1.2 详细过程分析

1.使用WXAppConfiguration加载app的基本配置包括AppName,AppGroup等等

 
   
   
 
  1. + (void)weexSdk

  2. {

  3.    // app信息配置

  4.    [WXAppConfiguration setAppGroup:@"dianwoda"];

  5.    [WXAppConfiguration setAppName:@"点我达"];

  6.    [WXAppConfiguration setExternalUserAgent:@"ExternalUA"];

  7. ......

  8. }

2.通过JSContext加载jsframework main.js文件进内存备用/br>

 
   
   
 
  1. + (void)initSDKEnvironment

  2. {

  3.    // 加载jsframework main.js文件

  4.    NSString *filePath = [[NSBundle bundleForClass:self] pathForResource:@"main" ofType:@"js"];

  5.    NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

  6.    // 将script交给initSDKEnvironment处理

  7.    [WXSDKEngine initSDKEnvironment:script];

  8.   .....

  9. }

  10. - (void)executeJSFramework:(NSString *)frameworkScript

  11. {  

  12.    // 断言参数是否合法

  13.    WXAssertParam(frameworkScript);

  14.    if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {

  15.        // 系统大于8.0加载网络

  16.        [_jsContext evaluateScript:frameworkScript withSourceURL:[NSURL URLWithString:@"main.js"]];

  17.    }else{

  18.        // 小于8.0加载本地frameworkScript

  19.        [_jsContext evaluateScript:frameworkScript];

  20.    }

  21. }

3.在jsframework 加载完毕的前提下通过JSContext调用的jsframework的registerComponents方法加载默认组件

3.1 注册默认的组件

 
   
   
 
  1. + (void)_registerDefaultComponents

  2. {

  3.    // 加载container组件

  4.    [self registerComponent:@"container" withClass:NSClassFromString(@"WXDivComponent") withProperties:nil];

  5.    // 加载div组件

  6.    [self registerComponent:@"div" withClass:NSClassFromString(@"WXComponent") withProperties:nil];

  7. ....

  8. }

3.2 通过WXComponentFactory工厂生产配置

 
   
   
 
  1.    // 通过WXComponentFactory工厂生产配置

  2.    [WXComponentFactory registerComponent:name withClass:clazz withPros:properties];

  3.    NSMutableDictionary *dict = [WXComponentFactory componentMethodMapsWithName:name];

  4. /*

  5. 生成的配置结构

  6. {

  7.    methods =     (

  8.    );

  9.    type = div;

  10. }

  11. */

3.3 jsframework 未加载完毕通过_methodQueue 队列缓冲未注册的组件

 
   
   
 
  1. - (void)callJSMethod:(NSString *)method args:(NSArray *)args

  2. {  

  3.    if (self.frameworkLoadFinished) {

  4.        // 加载完毕直接调用JSFramework的registerComponents方法

  5.        [self.jsBridge callJSMethod:method args:args];

  6.    } else {

  7.        // 如果JSFramework未加载完毕那么将配置加入到_methodQueue队列中待命

  8.        [_methodQueue addObject:@{@"method":method, @"args":args}];

  9.    }

  10. }

3.4 jsframework 加载完毕通过JSContext调用的jsframework的registerComponents方法加载默认组件

 
   
   
 
  1. - (void)registerComponents:(NSArray *)components

  2. {

  3.    WXAssertBridgeThread();

  4.    // 判断配置是否合法

  5.    if(!components) return;

  6.    // 通过JSContext调用JSFramework的registerComponents方法注册模块

  7.    [self callJSMethod:@"registerComponents" args:@[components]];

  8. }

3.5 流程图

点我达骑手端weex探索

4. 在jsframework 加载完毕的前提下通过JSContext调用的jsframework的registerModules方法加载默认模块

4.1 注册默认的模块

 
   
   
 
  1. + (void)_registerDefaultModules

  2. {

  3.    // 注册dom模块

  4.    [self registerModule:@"dom" withClass:NSClassFromString(@"WXDomModule")];

  5.    // 注册navigator模块

  6.    [self registerModule:@"navigator" withClass:NSClassFromString(@"WXNavigatorModule")];

  7. .....

  8. }

4.2 通过WXModuleFactory工厂生产配置

 
   
   
 
  1. // 通过WXModuleFactory工厂生产配置

  2.    NSString *moduleName = [WXModuleFactory registerModule:name withClass:clazz];

  3.    NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName];

  4. /*

  5. 生成的配置结构

  6. {

  7.    modal =     (

  8.        addEventListener,

  9.        removeAllEventListeners,

  10.        alert,

  11.        toast,

  12.        prompt,

  13.        confirm

  14.    );

  15. }

  16. */

4.3 jsframework 未加载完毕通过_methodQueue 队列缓冲未注册的模块

 
   
   
 
  1. - (void)callJSMethod:(NSString *)method args:(NSArray *)args

  2. {  

  3.    if (self.frameworkLoadFinished) {

  4.        // 如果jsframework加载完毕调用registerModules方法注册

  5.        [self.jsBridge callJSMethod:method args:args];

  6.    } else {

  7.        // 否则加入任务队列待命

  8.        [_methodQueue addObject:@{@"method":method, @"args":args}];

  9.    }

  10. }

4.4 jsframework 加载完毕通过JSContext调用的jsframework的registerModules方法加载默认组件

 
   
   
 
  1. - (void)registerModules:(NSDictionary *)modules

  2. {

  3.    // 断言是否在com.taobao.weex.bridge线程

  4.    WXAssertBridgeThread();

  5.    // 参数错误处理

  6.    if(!modules) return;

  7.    // 通过JSContext调用JSFramework的registerModules方法注册模块

  8.    [self callJSMethod:@"registerModules" args:@[modules]];

  9. }

4.5 流程图

点我达骑手端weex探索

5. 将默认的handlers加载给WXHandlerFactory的handlers存储起来备用

5.1 注册默认的handler

 
   
   
 
  1. + (void)_registerDefaultHandlers

  2. {

  3.    // 默认注册WXResourceRequestHandler协议的实现

  4.    [self registerHandler:[WXResourceRequestHandlerDefaultImpl new] withProtocol:@protocol(WXResourceRequestHandler)];

  5.    // 默认注册WXNavigationProtocol协议的实现

  6.    ......

  7. }

5.2 WXHandlerFactory工厂管理handlers待用

 
   
   
 
  1. + (void)registerHandler:(id)handler withProtocol:(Protocol *)protocol

  2. {

  3.    WXAssert(handler && protocol, @"Handler or protocol for registering can not be nil.");

  4.    WXAssertProtocol(handler, protocol);

  5.    [[WXHandlerFactory sharedInstance].handlers setObject:handler forKey:NSStringFromProtocol(protocol)];

  6. }

  7. /*

  8. 生产的配置结构

  9. {

  10.    WXResourceRequestHandler = "<WXResourceRequestHandlerDefaultImpl: 0x6080002230a0>";

  11. }

  12. */

5.3 流程图                                                                                                     

点我达骑手端weex探索

4.1.3 流程总结

点我达骑手端weex探索

4.2 weex引擎加载weex页面的的过程
4.2.1 总体流程图

点我达骑手端weex探索

4.2.2 详解

1.js页面资源挂载
2.jsBundleString 通过JSContext调用JSFramework的createInstance方法解析页面

4.3 weex页面与native交互的过程
4.3.1 核心部分分析

1.weex会调用sendTasks(id, tasks)发送消息调用WXJSCoreBridge callNativeBlock 具体实现在WXBridgeContext

 
   
   
 
  1. {

  2.        args =         (

  3.                        {

  4.                attr =                 {

  5.                    "data-v-294bc866" = "";

  6.                };

  7.                ref = "_root";

  8.                style =                 {

  9.                    backgroundColor = "#FFFFFF";

  10.                    bottom = 0;

  11.                    left = 0;

  12.                    right = 0;

  13.                    top = 0;

  14.                };

  15.                type = div;

  16.            }

  17.        );

  18.        method = createBody;

  19.        module = dom;

  20.    }

  21. )

2.weex会调用WXDomModule createBody方法 创建rootCSSNode将self.weexInstance.frame丢给FlexBox算法强力驱动计算布局 3.核心部分

 
   
   
 
  1. typedef NSInteger(^WXJSCallNative)(NSString *instance, NSArray *tasks, NSString *callback);

  2. typedef NSInteger(^WXJSCallAddElement)(NSString *instanceId,  NSString *parentRef, NSDictionary *elementData, NSInteger index);

  3. typedef NSInvocation *(^WXJSCallNativeModule)(NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *args, NSDictionary *options);

  4. typedef void (^WXJSCallNativeComponent)(NSString *instanceId, NSString *componentRef, NSString *methodName, NSArray *args, NSDictionary *options);

4.native调用weex方式主要通过以下2中方式
fireEvent callback

4.3.2 流程图

4.4 总结
4.4.1 核心部分分析
  1. JSFramework在app启动会初始化一次,多个页面都共享一份JSFramework

  2. 会去注册默认的组件模块协议

  3. 当native需要渲染页面的时候,会主动调用weex 的createInstance方法,其中code参数就是JS Bundle转换成的String。JSFramework接收到后就会开始解析,并开始sendTasks(id, tasks)

  4. weex端通过sendTasks(id, tasks)发送消息给native会通过JSBridge调用OC Native方法。tasks里面会指定功能的模块名、方法名以及参数。

  5. native也会调用receiveTasks(id, tasks)方法,调用JS的方法主要通过fireEvent,或者callback的方式

4.4.2 流程图


以上是关于点我达骑手端weex探索的主要内容,如果未能解决你的问题,请参考以下文章

SVG对骑手端weex绘图功能扩展实践

面试2个月,终于收到满意的Java后端开发offer!

Weex来深圳啦~美丽的3月,等你赴约

点我达分布式任务调度系统-DaJob

福利贴 | Weex:JS & Web能力拓展万物互联的探索

一个两年Java的面试总结