《weex 实践指北》第二章:客户端接入 weex(iOS 视角)

vr266261 3年前
   <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>