深入折腾 Weex,知乎日报客户端开发
最后效果如下:
这个 demo 主要实践 4 件事情:
1. 模块注册
2. 组件注册
2. Native 方法调用
4. Native 页面切换 & 参数传递
整个 app 只有两个页面,都是纯 Weex 编写的,对于第一个页面非常的简单,布局类似于 iOS 的 TableView
<template>
<scroller>
<container repeat="{{list}}" news_id="{{news_id}}" οnclick="onclick">
<container class="cell">
<image class="thumb" src="{{src}}"></image>
<text class="title">{{title}}</text>
</container>
<text class="separator"></text>
</container>
</scroller>
</template>
然后我们使用 this.$sendHttp 发起 http 请求,这里知道,Weex 仅仅是一个渲染引擎,对于 http 请求,mtop 请求,甚至图片加载等功能,是需要业务注入实现类的。比如 WeexDemo 里面的 sendHttp 就是注入了 WXStreamModule 模块,然后在实现里面使用了 NSURLConnection,这个设计非常不错,在具体的业务当中,你可以把实现代码替换成任何你想要的 HTTP 库。
[WXSDKEngine registerModule:@"image" withClass:[WXImageModule class]];
[WXSDKEngine registerModule:@"stream" withClass:[WXStreamModule class]];
[WXSDKEngine registerModule:@"event" withClass:[WXEventModule class]];
这就是 Weex 注册模块的原理,其中图片模块是用 SDWebImage 实现的。我们同样也可以将其他的 Native 方法注册到模块里面,例如:
WX_EXPORT_METHOD(@selector(log:));
- (void)log:(NSString *)text {
NSLog(@"LOG: %@", text);
}
然后在 JavaScript 代码里面只需要用 this.$call("event", "log", "Hello, World!"); 就能在 Native 里面输出 Hello World! 这提供了强大的扩展能力。
首页的展示是较为简单的,用的是 Weex 自带的 Image 和 Text,但是详情页是一个 Web 页面,貌似我还没找到 Weex 提供的 WebView 渲染组件。(但是奇怪的是,我自建 WXWebViewComponent 的时候提示 Duplicated Symbol,难道是还未实现么),所以我们需要自建一个 WXComponent,用于 WebView 渲染。
当然我们也可以使用 Native 代码打开一个 WebView,不过这样显然不如纯 Weex 实现有趣。
#import "WXHtmlNodeComponent.h"
#import <WebKit/WebKit.h>
@interface WXHtmlNodeComponent()
@property (nonatomic, strong) NSString *URL;
@property (nonatomic, strong) NSString *html;
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation WXHtmlNodeComponent
WX_CUSTOM_ATTRIBUTE(url, URL, NSString)
WX_CUSTOM_ATTRIBUTE(html, html, NSString)
+ (Class)viewClass {
return [WKWebView class];
}
- (void)applyPropertiesToView:(UIView *)view {
[super applyPropertiesToView:view];
WKWebView *webView = (WKWebView *)view;
if (self.html.length > 0) {
[webView loadHTMLString:self.html baseURL:nil];
} else if (self.URL.length > 0) {
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.URL]]];
}
}
这个 Component 支持 url 和 html 两种 attribute,意味着可以从 url 或者 html 代码渲染他。我们把它注册到 webnode 这个名字上面,就能在详情页使用它了:
<template>
<container>
<WebNode url="{{context.url}}" class="webnode"></WebNode>
</container>
</template>
<script>
module.exports = {
data: {
context: "{{context}}"
}
}
</script>
<style>
.webnode {
height: 1222;
flex: 1;
}
</style>
然后最最关键的事情来了,当用户点击一行的时候,我们要从首页跳到详情页,而这两个页面都是 Weex 的(目前貌似 Weex 没有提供这样的能力)。
但是没有关系,我们有 Module,这里我实现了一套逻辑,包括了本地页面 Push 和 Weex 页面上下文传递逻辑。- (void)push:(NSDictionary *)params {
ViewController *controller = [[ViewController alloc] init];
controller.URL = [NSURL URLWithString:params[@"url"]];
controller.context = params[@"context"];
UINavigationController *navigator = [(AppDelegate *)[[UIApplication sharedApplication] delegate] navigator];
[navigator pushViewController:controller animated:YES];
}
然后在首页 onclick 的时候,只要把 context 带到 Module 里面的 push 方法,往下透传过下一个 Weex 页面,就能完成下个页面的初始化。
onclick: function(e) {
var news_id = e.target.attr["news_id"];
var context = this;
var api = this.api(news_id);
this.get(api, function(json) {
var params = {
url: "next weex url",
context: {
url: json.share_url,
title: json.title
}
};
context.$call("event", "push", params);
});
}
具体来说,这里面使用了一个数据绑定上面的技巧,我把 weex 页面上的 data 字段放置了一个 "{{context}}"
<script>
module.exports = {
data: {
context: "{{context}}"
}
}
</script>
这是一个占位符,会在 Native Code 里面被 Push 传进来的 context 替换(这是一个 NSDictionary),处理的过程只是做一次字符串替换
#import "WXHelper.h"
#import "JSONHelper.h"
@implementation WXHelper
+ (NSString *)scriptWithURL:(NSURL *)URL context:(NSDictionary *)context {
NSString *script = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:nil];
if (context == nil || ![context isKindOfClass:[NSDictionary class]]) {
return script;
}
return [script stringByReplacingOccurrencesOfString:@"\"{{context}}\""
withString:JSONStringWithObject(context) ?: @""];
}
@end
然后我们的上下文就会被绑定到下一个 weex 页面,其实根据这个思路,我们可以做更多的 Weex 和本地的交互,甚至把它做成 Framework 方便使用。
整个流程如下:
首页 weex 渲染 --> onclick --> module 传递上下文到下一个 weex --> weex 自定义组件 WebView 渲染
以上就是整个 demo 的完整思路。
最后
Weex 的模块注册、组件注册非常的有用,给业务扩展自己的能力,自定义自己的功能提供了强大的基础。
当然也希望 Weex 可以在 Native 交互方面提供更多有用的方法。
上一篇: Java : 类与对象(一)
下一篇: kafka实战篇(二):消息消费实战
推荐阅读