《weex 实践指北》第二章:客户端接入 weex(iOS 视角)
vr266261
8年前
<p>对于你的“容器”而言,weex只是View层。</p> <p>“请牢记上述的格言”,个人推荐大家在配置Cocoapods的时候,既要有weex团队打包好的SDK,也要有引入源码的准备。</p> <pre> <code class="language-ruby">pod 'WeexSDK', '0.9.4' # pod 'WeexSDK', :path => '/User/icepy/weex/ios/sdk' pod 'WXDevtool', '0.8.2' # pod 'WXDevtool', :path => '/User/icepy/WXDevtool'</code></pre> <p>默认,你已经把环境准备完毕</p> <h2>初始化</h2> <p>既然weex只是View层,对于它接入,应该就比较简单了。</p> <p>参考: <a href="/misc/goto?guid=4959733762475852539" rel="nofollow,noindex">https://weex-project.io/doc/advanced/integrate-to-ios.html</a></p> <p>参考: <a href="/misc/goto?guid=4959733762557029607" rel="nofollow,noindex">https://weex-project.io/doc/advanced/extend-to-ios.html</a></p> <p>理论上,weex的初始化应该在App启动时。但是,你也可以在特定的时候初始化weex。</p> <pre> <code class="language-ruby">[WXSDKEngine initSDKEnviroment];</code></pre> <p>当然,如果你需要有一点业务的配置,也可以使用 WXAppConfiguration ,因为这不是必须的。</p> <pre> <code class="language-ruby">[WXAppConfiguration setAppVersion:@""]; [WXAppConfiguration setAppName:@""]; [WXAppConfiguration setAppGroup:@""];</code></pre> <p>有时候日志信息,也是非常必要的,你可以使用 WXLog 来配置</p> <pre> <code class="language-ruby">[WXLog setLogLevel:WXLogLevelAll]</code></pre> <p>日志等级,有一组枚举,你可以查看一下</p> <pre> <code class="language-ruby">typedef NS_ENUM(NSUInteger, WXLogLevel){ /** * No logs 没有日志 */ WXLogLevelOff = 0, /** * Error only 仅仅是错误信息 */ WXLogLevelError = WXLogFlagError, /** * Error and warning 错误和警告 */ WXLogLevelWarning = WXLogLevelError | WXLogFlagWarning, /** * Error, warning and info 错误,警告,信息 */ WXLogLevelInfo = WXLogLevelWarning | WXLogFlagInfo, /** * Log, warning info 这里的log代表了js层面的console.log,可以在Xcode控制台中可见 */ WXLogLevelLog = WXLogFlagLog | WXLogLevelInfo, /** * Error, warning, info and debug logs */ WXLogLevelDebug = WXLogLevelLog | WXLogFlagDebug, /** * All */ WXLogLevelAll = NSUIntegerMax };</code></pre> <h2>完善你的ViewController</h2> <p>前言讲到了weex只是一层View,那么将它和UIView划等号也是可以的。在你的VC中,应该有一个URL的属性,可以接到js bundle 文件。</p> <pre> <code class="language-ruby">- (instancetype)initWithURL:(NSURL *)url { self = [super init]; if (self) { _url = url; } return self; }</code></pre> <pre> <code class="language-ruby">@interface ViewController () @property(nonatomic, strong) WXSDKInstance *instance; @property(nonatomic, strong) UIView *weexView; @property(nonatomic, strong) NSURL *url; @end</code></pre> <p>然后在 viewDidLoad 中进行初始化。</p> <pre> <code class="language-ruby">- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self render] } - (void) render { CGFloat width = self.view.frame.size.width; [self.instance destroyInstance]; self.instance = [[WXSDKInstance alloc] init]; self.instance.viewController = self; self.instance.frame = CGRectMake(self.view.frame.size.width - width, self.top, width, self.weexHeight); __weak typeof(self) weakSelf = self; self.instance.onCreate = ^(UIView *view){ [weakSelf.weexView removeFromSuperview]; weakSelf.weexView = view; [weakSelf.view addSubview:weakSelf.weexView]; UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, weakSelf.weexView); }; self.instance.onFailed = ^(NSError *error){ NSLog(@"%@",error.userInfo); }; self.instance.renderFinish = ^(UIView *view){ [weakSelf.instance fireGlobalEvent:@"geolocation" params:@{@"key":@"value"}]; }; [self.instance renderWithURL:self.url options:nil data:nil]; }</code></pre> <p>从上述的逻辑来看,应该可以推敲出,weex 渲染结果虽然需要一个UIView来承接,最后再将这个UIView替换掉系统的view,所以理论上,你可以生成多个weex的instance,来拼凑页面,只需要设置好frame即可。weex 在代码层面上提供了几个block,来让你感知到渲染阶段的几个周期,比如是渲染错误了,还是渲染完成了,还是在渲染创建之前。</p> <p>最后,别忘记了在 dealloc 里调用一下 destroyInstanc 来销毁 weex 实例,不然会内存溢出。</p> <pre> <code class="language-ruby">-(void)dealloc{ [self.instance destroyInstance]; }</code></pre> <p>到此,weex可以将你用web技术写好的页面,呈现在对应的View中。不过,一个应用如果都是纯展示,那么到此就结束了,但往往不是。也许,你还需要各种各样Native的能力,也许Native也需要各种各样JS的能力。</p> <p>且看看 weex 为我们提供了多少种方式。</p> <h2>JS Call Native</h2> <p>weex 提供了Module来让JS主动Call Native,第一步你还是需要注册你的 Module。</p> <pre> <code class="language-ruby">[WXSDKEngine registerModule:@"test-logger" withClass: [WXLoggerModule class]];</code></pre> <pre> <code class="language-ruby">// WXLoggerModule.h #import <Foundation/Foundation.h> #import <WeexSDK/WXModuleProtocol.h> @interface WXLoggerModule : NSObject <WXModuleProtocol> @end</code></pre> <pre> <code class="language-ruby">// WXLoggerModule.m #import "WXLoggerModule.h" #import <WeexSDK/WeexSDK.h> @interface WXLoggerModule() @end @implementation WXLoggerModule @synthesize weexInstance; WX_EXPORT_METHOD(@selector(info:callback:)); - (void) info: (NSDictionary *) log callback:(WXKeepAliveCallback) callback{ NSLog(@"WXLoggerModule ----> : %@",log); callback(@{@"success":@YES,@"address":@"beijing"},YES); } @end</code></pre> <pre> <code class="language-ruby">// .js 文件 var logger = require('@weex-module/test-logger'); logger.info({name:"icepy"});</code></pre> <h2>Native Call JS</h2> <p>目前来说Native主动去Call JS,还只能使用事件的方式。</p> <pre> <code class="language-ruby">var globalEvent = require('@weex-module/globalEvent'); globalEvent.addEventListener('nativecalljs',function (e){ });</code></pre> <pre> <code class="language-ruby">self.instance.renderFinish = ^(UIView *view){ [weakSelf.instance fireGlobalEvent:@"nativecalljs" params:@{@"name":@"icepy"}]; };</code></pre> <h2>组件</h2> <p>从业务层面来说JS与Native可以互相通信,这是非常重要的一点,但是如果当前端有无法实现的或者很难实现的组件时,这个时候Component就派上用场了。</p> <pre> <code class="language-ruby">[WXSDKEngine registerComponent:@"test-image" withClass:[WXImageComponent class]];</code></pre> <pre> <code class="language-ruby">//WXImageComponent.h #import <WeexSDK/WeexSDK.h> @interface WXImageComponent : WXComponent @property(nonatomic, strong) NSString *imageSrc; @end</code></pre> <pre> <code class="language-ruby">//WXImageComponent.m #import "WXImageComponent.h" static CGFloat IMAGEWIDTH = 320; static CGFloat IMAGEHEIGHT = 320; @implementation WXImageComponent -(instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance { if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) { _imageSrc = [WXConvert NSString:attributes[@"src"]]; } return self; } -(void)viewDidLoad { [super viewDidLoad]; NSLog(@"DTWXImgaeCompnent ---- > %@",self.imageSrc); UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 20, IMAGEWIDTH, IMAGEHEIGHT)]; imageView.userInteractionEnabled = YES; imageView.clipsToBounds = YES; imageView.exclusiveTouch = YES; imageView.contentMode = UIViewContentModeScaleToFill; NSURL *imageURL = [NSURL URLWithString:self.imageSrc]; NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; UIImage *image = [UIImage imageWithData:imageData]; imageView.image = image; [self.view addSubview:imageView]; } @end</code></pre> <p>使用起来就和使用Vue的组件类似</p> <pre> <code class="language-ruby"><test-image src=""></test-image></code></pre> <p>其实,组件还有大量的生命周期,在这些生命周期内,你可以进行一些处理,总体来说,这是一个很有趣的事情。</p> <p> </p> <p> </p>