点我达骑手端weex探索
Posted 点我达技术团队
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点我达骑手端weex探索相关的知识,希望对你有一定的参考价值。
前 言
由于公司采用weex来开发项目,在开发过程中遇到了很多坑,在这种背景下花了几天时间来研究一下weexSDK的源码。如果有理解不当的地方欢迎指正。
1. ios weexSDK 概述
Weex 是一套简单易用的跨平台开发方案,能以 web 的开发体验构建高性能、可扩展的 native 应用,为了做到这些,Weex 与 Vue 合作,使用 Vue 作为上层框架,并遵循 W3C 标准实现了统一的 JSEngine 和 DOM API,这样一来,你甚至可以使用其他框架驱动 Weex,打造三端一致的 native 应用。
2. 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简要概括
使用WXAppConfiguration加载app的基本配置包括AppName,AppGroup等等
通过JSContext加载jsframework main.js文件进内存备用
在jsframework 加载完毕的前提下通过JSContext调用的jsframework的 registerComponents方法加载默认组件
在jsframework 加载完毕的前提下通过JSContext调用的jsframework的registerModules方法加载默认模块
将默认的handlers加载给WXHandlerFactory的handlers存储起来备用
4.1.2 详细过程分析
1.使用WXAppConfiguration加载app的基本配置包括AppName,AppGroup等等
+ (void)weexSdk
{
// app信息配置
[WXAppConfiguration setAppGroup:@"dianwoda"];
[WXAppConfiguration setAppName:@"点我达"];
[WXAppConfiguration setExternalUserAgent:@"ExternalUA"];
......
}
2.通过JSContext加载jsframework main.js文件进内存备用/br>
+ (void)initSDKEnvironment
{
// 加载jsframework main.js文件
NSString *filePath = [[NSBundle bundleForClass:self] pathForResource:@"main" ofType:@"js"];
NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
// 将script交给initSDKEnvironment处理
[WXSDKEngine initSDKEnvironment:script];
.....
}
- (void)executeJSFramework:(NSString *)frameworkScript
{
// 断言参数是否合法
WXAssertParam(frameworkScript);
if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
// 系统大于8.0加载网络
[_jsContext evaluateScript:frameworkScript withSourceURL:[NSURL URLWithString:@"main.js"]];
}else{
// 小于8.0加载本地frameworkScript
[_jsContext evaluateScript:frameworkScript];
}
}
3.在jsframework 加载完毕的前提下通过JSContext调用的jsframework的registerComponents方法加载默认组件
3.1 注册默认的组件
+ (void)_registerDefaultComponents
{
// 加载container组件
[self registerComponent:@"container" withClass:NSClassFromString(@"WXDivComponent") withProperties:nil];
// 加载div组件
[self registerComponent:@"div" withClass:NSClassFromString(@"WXComponent") withProperties:nil];
....
}
3.2 通过WXComponentFactory工厂生产配置
// 通过WXComponentFactory工厂生产配置
[WXComponentFactory registerComponent:name withClass:clazz withPros:properties];
NSMutableDictionary *dict = [WXComponentFactory componentMethodMapsWithName:name];
/*
生成的配置结构
{
methods = (
);
type = div;
}
*/
3.3 jsframework 未加载完毕通过_methodQueue 队列缓冲未注册的组件
- (void)callJSMethod:(NSString *)method args:(NSArray *)args
{
if (self.frameworkLoadFinished) {
// 加载完毕直接调用JSFramework的registerComponents方法
[self.jsBridge callJSMethod:method args:args];
} else {
// 如果JSFramework未加载完毕那么将配置加入到_methodQueue队列中待命
[_methodQueue addObject:@{@"method":method, @"args":args}];
}
}
3.4 jsframework 加载完毕通过JSContext调用的jsframework的registerComponents方法加载默认组件
- (void)registerComponents:(NSArray *)components
{
WXAssertBridgeThread();
// 判断配置是否合法
if(!components) return;
// 通过JSContext调用JSFramework的registerComponents方法注册模块
[self callJSMethod:@"registerComponents" args:@[components]];
}
3.5 流程图
4. 在jsframework 加载完毕的前提下通过JSContext调用的jsframework的registerModules方法加载默认模块
4.1 注册默认的模块
+ (void)_registerDefaultModules
{
// 注册dom模块
[self registerModule:@"dom" withClass:NSClassFromString(@"WXDomModule")];
// 注册navigator模块
[self registerModule:@"navigator" withClass:NSClassFromString(@"WXNavigatorModule")];
.....
}
4.2 通过WXModuleFactory工厂生产配置
// 通过WXModuleFactory工厂生产配置
NSString *moduleName = [WXModuleFactory registerModule:name withClass:clazz];
NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName];
/*
生成的配置结构
{
modal = (
addEventListener,
removeAllEventListeners,
alert,
toast,
prompt,
confirm
);
}
*/
4.3 jsframework 未加载完毕通过_methodQueue 队列缓冲未注册的模块
- (void)callJSMethod:(NSString *)method args:(NSArray *)args
{
if (self.frameworkLoadFinished) {
// 如果jsframework加载完毕调用registerModules方法注册
[self.jsBridge callJSMethod:method args:args];
} else {
// 否则加入任务队列待命
[_methodQueue addObject:@{@"method":method, @"args":args}];
}
}
4.4 jsframework 加载完毕通过JSContext调用的jsframework的registerModules方法加载默认组件
- (void)registerModules:(NSDictionary *)modules
{
// 断言是否在com.taobao.weex.bridge线程
WXAssertBridgeThread();
// 参数错误处理
if(!modules) return;
// 通过JSContext调用JSFramework的registerModules方法注册模块
[self callJSMethod:@"registerModules" args:@[modules]];
}
4.5 流程图
5. 将默认的handlers加载给WXHandlerFactory的handlers存储起来备用
5.1 注册默认的handler
+ (void)_registerDefaultHandlers
{
// 默认注册WXResourceRequestHandler协议的实现
[self registerHandler:[WXResourceRequestHandlerDefaultImpl new] withProtocol:@protocol(WXResourceRequestHandler)];
// 默认注册WXNavigationProtocol协议的实现
......
}
5.2 WXHandlerFactory工厂管理handlers待用
+ (void)registerHandler:(id)handler withProtocol:(Protocol *)protocol
{
WXAssert(handler && protocol, @"Handler or protocol for registering can not be nil.");
WXAssertProtocol(handler, protocol);
[[WXHandlerFactory sharedInstance].handlers setObject:handler forKey:NSStringFromProtocol(protocol)];
}
/*
生产的配置结构
{
WXResourceRequestHandler = "<WXResourceRequestHandlerDefaultImpl: 0x6080002230a0>";
}
*/
5.3 流程图
4.1.3 流程总结
4.2 weex引擎加载weex页面的的过程
4.2.1 总体流程图
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
{
args = (
{
attr = {
"data-v-294bc866" = "";
};
ref = "_root";
style = {
backgroundColor = "#FFFFFF";
bottom = 0;
left = 0;
right = 0;
top = 0;
};
type = div;
}
);
method = createBody;
module = dom;
}
)
2.weex会调用WXDomModule createBody方法 创建rootCSSNode将self.weexInstance.frame丢给FlexBox算法强力驱动计算布局 3.核心部分
typedef NSInteger(^WXJSCallNative)(NSString *instance, NSArray *tasks, NSString *callback);
typedef NSInteger(^WXJSCallAddElement)(NSString *instanceId, NSString *parentRef, NSDictionary *elementData, NSInteger index);
typedef NSInvocation *(^WXJSCallNativeModule)(NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *args, NSDictionary *options);
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 核心部分分析
JSFramework在app启动会初始化一次,多个页面都共享一份JSFramework
会去注册默认的组件模块协议
当native需要渲染页面的时候,会主动调用weex 的createInstance方法,其中code参数就是JS Bundle转换成的String。JSFramework接收到后就会开始解析,并开始sendTasks(id, tasks)
weex端通过sendTasks(id, tasks)发送消息给native会通过JSBridge调用OC Native方法。tasks里面会指定功能的模块名、方法名以及参数。
native也会调用receiveTasks(id, tasks)方法,调用JS的方法主要通过fireEvent,或者callback的方式
4.4.2 流程图
以上是关于点我达骑手端weex探索的主要内容,如果未能解决你的问题,请参考以下文章