阿里千牛旺旺技术团队 关注
手机版

Weex入门与进阶指南

  1. 云栖社区>
  2. 阿里千牛旺旺技术团队>
  3. 博客>
  4. 正文

Weex入门与进阶指南

思禽 2016-07-08 18:18:14 浏览25636 评论10

摘要: 前言 相比较于React Native的“Learn once, write anywhere”,Weex的口号是“Write once, run everywhere”。考虑到React Native比较任性的向下兼容性,我们也引入了Weex做一番了解。

前言

相比较于React Native的“Learn once, write anywhere”,Weex的口号是“Write once, run everywhere”。考虑到React Native比较任性的向下兼容性,我们也引入了Weex做一番了解。

本文主要分为以下几个部分:

  1. 构建Hello World程序;
  2. 集成到现有的iOS工程中;
  3. 使用Weex的高级特性;
  4. 如何为Weex做贡献;

一、Weex入门

1.1 Hello Weex

参考官方教程,我们需要先安装Node。在Mac上也可以通过Homebrew直接进行安装:brew install node

接着我们需要安装Weex CLI:npm install -g weex-toolkit,并确保版本号大于0.1.0:

$ weex --version
info 0.3.4

至此,准备工作已经到位,我们可以开始编写Weex程序了。
创建一个名为helloweex.we的文件,并编写以下代码:

<template>
  <div>
      <text>Hello Weex</text>
  </div>
</template>

通过命令行在helloweex.we文件所在的目录下执行如下命令:

$ weex helloweex.we 
info Fri Jul 08 2016 14:30:31 GMT+0800 (CST)WebSocket  is listening on port 8082 
info Fri Jul 08 2016 14:30:31 GMT+0800 (CST)http  is listening on port 8081 

此时,浏览器会打开一个新的标签页展示helloweex.we的执行效果:

_2016_07_08_2_34_04

注意到此时地址栏的内容http://127.0.0.1:8081/weex_tmp/h5_render/?hot-reload_controller&page=helloweex.js&loader=xhr包含着hot reload字样,所以可以自然联想到当我们在源文件做修改并保存后,该页面会自动刷新展示效果。

1.2 基础结构

上面的示例只是一个非常简单的雏形,而一个比较完整的Weex程序包括三个部分:模板(Template)、样式(Style)和脚本(Script)。

比如我们可以利用上文提到的hot reload,修改文本的颜色并实时查看效果:

<template>
  <div>
      <text class="title">Hello Weex</text>
  </div>
</template>

<style>
  .title { color: red; }
</style>

_2016_07_08_2_47_03

接着我们添加上第三组成部分:脚本(Script):

<template>
  <div>
      <text class="title" onclick="onClickTitle">Hello Weex</text>
  </div>
</template>

<style>
  .title { color: red; }
</style>

<script>
  module.exports = {
    methods: {
      onClickTitle: function (e) {
        console.log(e);
        alert('title clicked.');
      }
    }
  }
</script>

这样一来,当我们点击文本的时候会出现如下效果:

_2016_07_08_2_50_33

更多语法相关内容可以参考官方文档

二、集成到iOS工程

2.1 概述

上面是从前端的角度来初步看Weex的基础效果,对于客户端来讲,这类框架的一个优势就是能够结合Native代码发挥作用。比如在人手紧张的情况下可以一次开发,然后应用在不同平台终端上。

所以,这里讨论下如何将其集成到现有的iOS工程项目当中。

  1. 参考官方文档,我们先从GitHub下载Weex源码
  2. 解压后将目录下的ios/sdk复制到现有的iOS工程目录下,并根据相对路径更新既有工程的podfile,然后执行pod update将Weex iOS SDK集成进既有的iOS项目中;
  3. 在iOS Native代码中初始化Weex SDK,然后创建出要展示Weex程序的ViewController,具体见如下描述;

2.2 在iOS应用上运行Weex程序

如何集成的文档中,前面说的比较清楚,但是在初始化Weex环境渲染Weex实例这两个小节中,可能是由于代码是从比较大的项目源码中摘录出来的,所以存在一些不必要或没有上下文的代码。

这里描述下在开发调试阶段运行Weex程序。

2.2.1 确定要运行的Weex程序

创建一个WeexDebugViewController,进行如下布局:

_2016_07_08_3_19_12

通过填入IP和文件名来定位我们要运行的Weex程序。此外,还可以结合weex helloweex.we --qr -h {ip or hostname}命令来生成二维码,进行扫描演示,不过解析二维码还是为了获取到Weex程序所在位置。

2.2.2 初始化Weex SDK

开发调试阶段我们可以先将Weex SDK的初始化放在这个WeexDebugViewController中:

- (void)initWeex {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [WXAppConfiguration setAppGroup:@"AliApp"];
        [WXAppConfiguration setAppName:@"WeexDemo"];
        [WXAppConfiguration setAppVersion:@"1.0.0"];
        
        [WXSDKEngine initSDKEnviroment];
        
        [WXLog setLogLevel:WXLogLevelVerbose];
    });
}

2.2.3 运行Weex程序的ViewController

点击ShowWeex按钮时,我们可以根据两个输入框的内容拼接出要运行的Weex程序的位置,然后将其赋值给用来渲染Weex实例的WeexShowcaseViewController

- (void)showWeex {
    NSString *str = [NSString stringWithFormat:@"http://%@:8081/%@", self.ipField.text, self.filenameField.text];
    WeexShowcaseViewController *vc = [WeexShowcaseViewController new];
    vc.weexUri = [NSURL URLWithString:str];
    [self.navigationController pushViewController:vc animated:YES];
}

接着我们来看看WeexShowcaseViewController的源码:

#import <WeexSDK/WeexSDK.h>

@interface WeexShowcaseViewController ()

@property (nonatomic, strong) WXSDKInstance *weexSDK;

@end

@implementation WeexShowcaseViewController

- (void)dealloc {
    [_weexSDK destroyInstance];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.weexSDK.viewController = self;
    self.weexSDK.frame = self.view.frame;
    
    [self.weexSDK renderWithURL:self.weexUri];
    
    __weak typeof(self) weakSelf = self;
    self.weexSDK.onCreate = ^(UIView *view) {
        [weakSelf.view addSubview:view];
    };
    
    self.weexSDK.renderFinish = ^(UIView *view) {
        ;
    };
    
    self.weexSDK.onFailed = ^(NSError *error) {
        NSLog(@"weexSDK onFailed : %@\n", error);
    };
}

- (WXSDKInstance *)weexSDK {
    if (!_weexSDK) {
        _weexSDK = [WXSDKInstance new];
    }
    return _weexSDK;
}

2.2.4 运行起来

回到终端上,切换到helloweex.we文件所在的目录,将Weex的dev server跑起来:

$ weex -s .
info Fri Jul 08 2016 15:38:59 GMT+0800 (CST)http  is listening on port 8081 
info we file in local path . will be transformer to JS bundle
please access http://30.9.112.173:8081/ 

然后在Native上填入对应的IP和程序文件名:

_2016_07_08_3_47_33

_2016_07_08_3_47_43

到此,将Weex集成到现有iOS工程中算初步告一段落。

三、Weex进阶

当集成工作完成后,会发觉现有功能不足以满足业务需求,所以Weex支持开发者做一些扩展。

3.1 实现Weex接口协议

之前的helloweex.we示例中只有一个文本元素,现在再添加一个图片元素:

<template>
  <div>
      <image class="thumbnail" src="http://image.coolapk.com/apk_logo/2015/0817/257251_1439790718_385.png"></image>
      <text class="title" onclick="onClickTitle">Hello Weex</text>
  </div>
</template>

<style>
  .title { color: red; }
  .thumbnail { width: 100; height: 100; }
</style>

<script>
  module.exports = {
    methods: {
      onClickTitle: function (e) {
        console.log(e);
        alert('title clicked.');
      }
    }
  }
</script>

然后再执行:$ weex helloweex.we 来运行查看效果:

_2016_07_08_4_28_01

可以在浏览器里看到这次多了一张图片。但是如果是运行在Native端,图片则得不到展示:

_2016_07_08_4_37_08

这是由于Weex SDK没有提供图片下载能力,需要我们来实现。

3.2 实现图片下载协议WXImgLoaderProtocol

这个基本可以参考官方文档来实现。

3.2.1 定义图片下载Handler

#import <WeexSDK/WeexSDK.h>

@interface WeexImageDownloader : NSObject <WXImgLoaderProtocol>

@end

3.2.2 实现协议接口

这个类必须遵循WXImgLoaderProtocol协议,并实现该协议定义的接口:

#import "WeexImageDownloader.h"
#import <SDWebImage/SDWebImageManager.h>

@implementation WeexImageDownloader

- (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url
                                          imageFrame:(CGRect)imageFrame
                                            userInfo:(NSDictionary *)options
                                           completed:(void(^)(UIImage *image,  NSError *error, BOOL finished))completedBlock {
    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

3.2.3 注册Handler

[WXSDKEngine registerHandler:[WeexImageDownloader new] withProtocol:@protocol(WXImgLoaderProtocol)];

这样一来,再次运行程序就可以看到图片了:

_2016_07_08_5_45_09

这样设计的好处主要是考虑了不同App依赖的网络库或者图片下载缓存库不同,避免Weex强依赖于一些第三方库,遵循依赖抽象而不是具体的原则。

BTW,我个人感觉Weex缩写成WXWeexImageLoaderProtocol缩写成WXImgLoaderProtocol,不是很好看。

3.2 自定义UI组件

如果Weex的内置标签不足以满足要求时,我们可以自定义Native组件,然后暴露给.we文件使用。

比如我们可以定义一个WeexButton,继承自WXComponent,然后将其注册进Weex SDK:

[WXSDKEngine registerComponent:@"weex-button" withClass:[WeexButton class]];

这样一来,我们就可以在.we文件中使用这个标签了:

<weex-button class="button" title="hello"></weex-button>

标签中的属性我们可以在初始化函数中获得:

- (instancetype)initWithRef:(NSString *)ref
                       type:(NSString*)type
                     styles:(nullable NSDictionary *)styles
                 attributes:(nullable NSDictionary *)attributes
                     events:(nullable NSArray *)events
               weexInstance:(WXSDKInstance *)weexInstance {
    self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
    if (self) {
        _title = [WXConvert NSString:attributes[@"title"]];
    }
    return self;
}

通过这些属性,我们可以在组件生命周期中修改组件的样式,比如设置按钮的title:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.innerButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    self.innerButton.frame = self.view.bounds;
    [self.view addSubview:self.innerButton];
    [self.innerButton setTitle:self.title forState:UIControlStateNormal];
    [self.innerButton addTarget:self action:@selector(onButtonClick:) forControlEvents:UIControlEventTouchUpInside];
}

3.3 自定义模块

除了UI组件之外,有些时候我们希望JS层面能够调用Native的一些功能,比如通过JS代码让Native打开一个特定的ViewController。这时候,我们可以自定义一个模块向JS层面暴露API:

@synthesize weexInstance;

WX_EXPORT_METHOD(@selector(call:withParam:callback:))
- (void)call:(NSString *)api withParam:(NSDictionary *)param callback:(WXModuleCallback)callback {

注意点如下:

  1. 需要遵循WXModuleProtocol协议;
  2. 需要合成(synthesizeweexInstance属性;
  3. 使用WX_EXPORT_METHOD来暴露API;
  4. 使用WXModuleCallback进行回调;

完成以上编码后,向Weex SDK注册:[WXSDKEngine registerModule:,就可以在.we文件中使用了:

<script>
  module.exports = {
    methods: {
      onClickTitle: function (e) {
        var mymodule = require('@weex-module/mymodule');
        mymodule.call('api', {}, function(ret) {
        });
      }
    }
  }
</script>

四、为Weex做贡献

由于Weex刚开源不久,如果开发者发现一些问题或者需要改善的地方,可以直接在GitHub上进行fork,修改完后提交Pull Request

【云栖快讯】你想见的Java技术专家都在这了,向大佬提问,有问题必答  详情请点击

网友评论

1F
alwwl

您好,我按照这个写下来,前面在浏览器上没问题,但在iOS应用上运行Weex,一直显示不了weexweex2
麻烦可以帮我看下吗。

思禽

我看你的截图 第二张黑色背景的 是Xcode控制台输出吧?

mengtuo

我也出现和他一样的问题,有解决办法么?

mengtuo

@alwwl 我也出现这个问题了,请问您是怎么解决的呢!

路西法。

@alwwl @mengtuo 我也遇到了这个问题,请问 后来找到解决办法了吗?

评论
2F
clarkyu

npm install -g weex-toolkit 时,提示下面的信息:
npm WARN deprecated native-or-bluebird@1.2.0: 'native-or-bluebird' is deprecated. Please use 'any-promise' instead.

inred2365 赞同
inred2365

我用这个命令安装的时候也报这个问题,并且最后回滚了,后来又直接用帖子里的pkg安装,虽然貌似装好了,但实际上,连run个hello weex都还是报错
(FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-22)
events.js:160
throw er; // Unhandled 'error' event

赶脚还是环境没装好

评论
3F
路西法。

@inred2365 @alwwl @mengtuo 我也遇到了 和一楼一样的问题,请问 有什么解决办法了吗? 不知道 什么导致的~

4F
路西法。

@思禽 我也遇到了 和一楼一样的问题,请问 知道 是什么问题吗? 不知道 什么导致的~

5F
aaroncaolun

同样遇到这样的问题,npm install -g weex-toolkit 时,提示下面的信息:
npm WARN deprecated native-or-bluebird@1.2.0: 'native-or-bluebird' is deprecated. Please use 'any-promise' instead.

请大牛看看要怎么解决?

6F
jwwilson

把二级页面的navigationBar隐藏一下试试看。估计是被挡住了。

7F
8号幽灵

这种是什么问题呢?
C:UsersAdministratorAppDataRoamingnpm>weex -v
C:UsersAdministratorAppDataRoamingnpmnode_modulesweex-toolkitnode_modules.0.2.5@xtoolkitsrcxtoolkit.js:12
class XToolkit {
^^^^^

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:374:25)
at Object.Module._extensions..js (module.js:405:10)
at Module.load (module.js:344:32)
at Function.Module._load (module.js:301:12)
at Module.require (module.js:354:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (C:\Users\Administrator\AppData\Roaming\npm\node_modules\weex-toolkit\bin\weex:3:16)
at Module._compile (module.js:398:26)
at Object.Module._extensions..js (module.js:405:10)
8F
小羽_

不是写给初学者看的,拜拜

9F
1895510556696281

请问 weex -s .
这个启动的命令需要配置那些内容 发现并没有这个命令

1895510556696281

weex xxx.we 是好的 可以用官方的app扫码 查看

评论
10F
1912101492178388

mac 环境已经搭建完成
但是本文所述1.1部分运行helloweex.we文件,浏览器页面被自动调起,但是页面元素没有“hello weex”字样,为什么?

思禽
文章84篇 | 关注45
关注
阿里云移动APP解决方案,助力开发者轻松应对移动app中随时可能出现的用户数量的爆发式增长、... 查看详情
业内领先的面向企业的一站式研发提效平台(研发效能),通过项目流程管理和专项自动化提效工具,能... 查看详情
移动测试(Mobile Testing)是为广大企业客户和移动开发者提供真机测试服务的云平台... 查看详情
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效... 查看详情
阿里云总监课正式启航

阿里云总监课正式启航