美文网首页
iOS 端Weex初体验

iOS 端Weex初体验

作者: 炎成 | 来源:发表于2018-10-28 21:06 被阅读60次

    Weex是一种轻量级、可扩展、高性能框架,兼顾性能与动态性于一身,让移动开发者通过简捷的前端语法写出原生级别的性能体验,并支持iOS、安卓及Web等多端部署。

    开发者只需要在本地导入Weex的SDK,就可以通过HTML/CSS/JavaScript网页的这套编程语言来开发Native级别的Weex界面。这意味着可以直接用现有Web开发的编辑器和IDE的代码补全、提示、检查等功能。从而也给前端人员开发Native端,较低的开发成本和学习成本。Weex默认打的JS bundle只包含业务JS代码,体积小很多,基础JS库包含在Weex SDK中,与Facebook的ReactiveNative相比更加轻便。同时把生成的JS bundle轻松部署到服务器端,客户端通过请求来获取新的资源,以解决传统通过提交审核,不能快速迭代的问题。前端开发者,可以通过现有编译器编写.we或者.vue文件。而客户端只需要从服务端请求检测是否需要更新,获取到的JS Bundle通过WeexSDK即可解析为.js文件,从而客户端只需要处理布局和渲染即可。

    iOS客户端集成WeexSDK的几个步骤

    1. 导入WeexSDK
      下载源代码,将/ios/sdk整个目录拷贝到项目中,用pod安装Weex
    #Weex支持的最低版本是iOS7.0.
        platform :ios, '7.0'
        #指定WeexSDK所在的路径是`./sdk/`这里的`.`代表`Profile`文件所在目录
        pod 'WeexSDK', :path=>'./sdk/'
    
    1. WeexSDK初始化
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.backgroundColor = [UIColor whiteColor];
        
        // 在这里进行初始化SDK
        [self initWeexSDK];
        
        self.window.rootViewController = [[WXRootViewController alloc] initWithRootViewController:[YMLoadWeexViewController new]];
        [self.window makeKeyAndVisible];
        
        return YES;
    }
    
    - (void)initWeexSDK
    {
        [WXAppConfiguration setAppGroup:@"XXXX"];
        [WXAppConfiguration setAppName:@"XXXX"];
        
        [WXSDKEngine initSDKEnvironment];
        
        [WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
        [WXSDKEngine registerHandler:[WXEventModule new] withProtocol:@protocol(WXEventModuleProtocol)];
        [WXSDKEngine registerComponent:@"select" withClass:NSClassFromString(@"WXSelectComponent")];
        [WXSDKEngine registerModule:@"event" withClass:[WXEventModule class]];
    }
    

    WXSDKEngine的初始化最为关键,主要就是注册Components,Modules,Handlers 和 执行JSFramework。

    Components(组件),即客户端封装的控件,用来提供给前端人员开发使用。在WXSDKEngine初始化的时候,已经默认注册了23种Components,这些都是Weex已经帮助移动端开发者提前写好的一些通用组件,同时可以通过fireEvent调用js方法。若想实现定制化的控件,客户端开发可自定义Components,同时也需要进行注册。

    //这个方法是必须要重写的
    - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstanc;
    //官方提供每一个自定义的Component都可以调用fireEvent,用来调用js方法
    - (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params ;
    
    #import "YMLabelComponent.h"
    
    @implementation YMLabelComponent
    
    WX_EXPORT_METHOD(@selector(testFunction))
    
    - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstanc{
        self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstanc];
        if (self) {
            self.cssNode->style.dimensions[CSS_WIDTH] = 51;
            self.cssNode->style.dimensions[CSS_HEIGHT] = 31;
        }
        return self;
    }
    
    -(UIView *)loadView {
        UILabel *label = [[UILabel alloc] init];
        label.backgroundColor = [UIColor redColor];
        label.text = @"我是个测试Component";
        label.textColor = [UIColor blackColor];
        return label;
    }
    
    - (void)updateStyles:(NSDictionary *)styles {
        
    }
    - (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params {
        
    }
    
    - (void)testFunction {
        DLog(@"这是一个Test");
    }
    @end
    

    Module 是完成一个操作的方法集合,在 Weex 的页面中,允许开发者 require 引入,调用 module 中的方法,也就是与js进行交互,WeexSDK 在启动时候,已经注册了一些内置的 module。自定义 module, 需要让自己的 class 遵循 WXModuleProtocol 这个protocol, 通过 WX_EXPORT_METHOD 这个宏暴露出需要透出到 JavaScript 调用的方法,注册 module , 就可以完成一个简单 module 的自定义。

    @interface YMMapModule ()
    
    @property (nonatomic, strong) YMMapMgr *mapMgr;
    
    @end
    
    @implementation YMMapModule
    
    @synthesize weexInstance;
    
    WX_EXPORT_METHOD(@selector(getUserLocation:callback:))
    
    - (void)getUserLocation:(NSDictionary *)dic callback:(WXModuleCallback)callback {
        [self.mapMgr startLocationCallback:callback];
    }
    
    - (YMMapMgr *)mapMgr {
        if (_mapMgr == nil) {
            _mapMgr = [YMMapMgr shared];
        }
        return _mapMgr;
    }
    @end
    

    Handler 是 WeexSDK engine 中一个 service 的概念,它可以被 component、module 和其他的 handler 实现中调用。handler 更多的是针对 native 开发同学来开发和使用,在其他的 component 和 module 中被调用。调用者不关注 handler 具体的实现,只通过接口获得需要的数据或者调用对应的 handler 方法,每个 protocol对应的 handler 在 app 生命周期期间只有一个实例。

    //图片下载功能
    @implementation WXImgLoaderDefaultImpl
    - (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url imageFrame:(CGRect)imageFrame userInfo:(NSDictionary *)userInfo completed:(void(^)(UIImage *image,  NSError *error, BOOL finished))completedBlock
    {
        if ([url hasPrefix:@"//"]) {
            url = [@"http:" stringByAppendingString:url];
        }
        return (id<WXImageOperationProtocol>)[[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
        } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
        if (completedBlock) {
            completedBlock(image, error, finished);
        }
        }];
    }
    @end
    
    1. 渲染
      weex支持两种渲染模式,一种是整个界面,一种是界面某一部分.你需要给需要渲染的weex视图指定特定的URL,然后把它添加到父控件中.
    @interface YMLoadWeexViewController ()
    
    @property (nonatomic, assign) BOOL isRoot;
    @property (nonatomic, strong) NSURL *jsUrl;
    @property (nonatomic, strong) WXSDKInstance *instance;
    @property (nonatomic, strong) UIView *weexView;
    @property (nonatomic, strong) YMMapMgr *mapMgr;
    
    @end
    
    @implementation YMLoadWeexViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        [self loadWeex];
        if (self.mapMgr == nil) {
            self.mapMgr = [YMMapMgr shared];
        }
    
    }
    - (void)loadWeex {
        if (!_jsUrl) {
            if (self.isRoot) {  
                NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
                NSString *rootUrlstring;
                if ([YMPreloadMgr sharedMgr].userToken.length) {
                    rootUrlstring = [NSString stringWithFormat:@"%@/dist/weex-app.weex.js",documentPath];
                } else {
                    rootUrlstring = [NSString stringWithFormat:@"%@/dist/app-select-user.weex.js",documentPath];
                }
                self.jsUrl = [NSURL fileURLWithPath:rootUrlstring];
            } else {
                return;
            }
        }
        self.navigationController.navigationBarHidden = YES;
        [self render];
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationRefreshInstance:) name:@"RefreshInstance" object:nil];
    }
    - (void)render
    {
        [_instance destroyInstance];
        _instance = [[WXSDKInstance alloc] init];
        if([WXPrerenderManager isTaskExist:[self.jsUrl absoluteString]]){
            _instance = [WXPrerenderManager instanceFromUrl:self.jsUrl.absoluteString];
        }
        
        _instance.viewController = self;
        _instance.frame = CGRectMake(0, 20, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height -20-49);
        
        __weak typeof(self) weakSelf = self;
        [MBProgressHUD showHUDAddedTo:self.view animated:YES];
        //WXSDKInstance创建完毕
        _instance.onCreate = ^(UIView *view) {
            [weakSelf.weexView removeFromSuperview];
            weakSelf.weexView = view;
            [weakSelf.view addSubview:weakSelf.weexView];
            UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, weakSelf.weexView);
        };
        //渲染失败
        _instance.onFailed = ^(NSError *error) {
            [MBProgressHUD hideHUDForView:weakSelf.view animated:YES];
            if ([[error domain] isEqualToString:@"1"]) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                    NSMutableString *errMsg=[NSMutableString new];
                    [errMsg appendFormat:@"ErrorType:%@\n",[error domain]];
                    [errMsg appendFormat:@"ErrorCode:%ld\n",(long)[error code]];
                    [errMsg appendFormat:@"ErrorInfo:%@\n", [error userInfo]];
                    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"render failed" message:errMsg delegate:weakSelf cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
                    [alertView show];
                });
            }
        };
        //渲染完成
        _instance.renderFinish = ^(UIView *view) {
            WXLogDebug(@"%@", @"Render Finish...");
            [MBProgressHUD hideHUDForView:weakSelf.view animated:YES];
            [weakSelf updateInstanceState:WeexInstanceAppear];
        };
      
        if (!self.jsUrl) {
            WXLogError(@"error: render url is nil");
            return;
        }
        if([WXPrerenderManager isTaskExist:[self.jsUrl absoluteString]]){
            WX_MONITOR_INSTANCE_PERF_START(WXPTJSDownload, _instance);
            WX_MONITOR_INSTANCE_PERF_END(WXPTJSDownload, _instance);
            WX_MONITOR_INSTANCE_PERF_START(WXPTFirstScreenRender, _instance);
            WX_MONITOR_INSTANCE_PERF_START(WXPTAllRender, _instance);
            [WXPrerenderManager renderFromCache:[self.jsUrl absoluteString]];
            return;
        }
        
        [_instance renderWithURL:URL options:@{@"bundleUrl":self.jsUrl.absoluteString} data:nil];
    }
    - (void)dealloc
    {
        [_instance destroyInstance];
    }
    
    1. 热更新策略
      客户端本地保存当前js版本以及每个js文件对应编码,每次启动都去请求接口,后端返回服务端版本与文件编码,客户端先根据js版本判断是否需要更新js文件,若需要则进一步对比js文件编码,处理新增或是修改替换。待下篇详解更新策略。

    相关文章

      网友评论

          本文标题:iOS 端Weex初体验

          本文链接:https://www.haomeiwen.com/subject/iodmtqtx.html