difference-Weex-Vue2-x

Weex 和 Vue 2.x 的语法差异

Updated time: 14/06/2017

Overview

Weex Vue
生命周期 ready: function() {} mounted: function() {}
条件指令 if="{{!foo}}" v-if="!foo"
循环指令 repeat="{{item in list}}" v-for="item in list"
样式类名 class="btn btn-{{type}}" :class="['btn', 'btn-' + type]"
内联样式 style="color:{{textColor}}" :style="{ color: textColor }"
事件绑定 onclick="handler" @click="handler"
原生事件 onclick="xxx" `@click.native=”xxx”`
数据绑定 src="{{rightItemSrc}}" :src="rightItemSrc"
内容/槽 <content></content> <slot></slot>
数据初始化 data: { value: 'x' } data: function() { return { value: 'x' } }
标签 ID id="xxx" ref="xxx"
获取节点 this.$el('xxx') this.$refs.xxx

Reference

See the source code of weex-vue-migration for more details:

LifeCycle Hooks 生命周期钩子

weex vue Description
init beforeCreate 组件实例刚刚被创建,组件属性如data计算之前
created created 组件实例创建完成,属性已绑定,但DOM还未生成
beforeMount 模板编译/挂载之前
ready mounted 模板编译/挂载之后
beforeUpdate 组件更新之前
updated 组件更新之后
activated for keep-alive, 组件被激活时调用
deactivated for keep-alive, 组件被移除时调用
beforeDestroy 组件被销毁前调用
destroyed destroyed 组件被销毁后调用

在weex中,使用{{…}}在<template>中绑定在<script>里定义的数据;在vue中,需要在要绑定的属性前加 : 。如以下示例。

  • 类名

    • weex

      1
      <div class="btn btn-&#123;{type}}"></div>
    • vue

      1
      <div :class="['btn', 'btn-' + type]"></div>
  • 样式绑定

    • weex

      1
      <div style="color:{{textColor}}"></div>
    • vue

      1
      <div :style="{color: textColor}"></div>
  • weex

    1
    <image src="..." if="{{shown}}"></image>

    or

    1
    <image src="..." if="shown"></image>
  • vue

    1
    <image src="..." v-if="shown"></image>
  • weex: repeat

    • $index为索引

      1
      2
      3
      <div repeat="{{list}}">
      <text>No. {{$index + 1}}</text>
      <div>

      or

      1
      2
      3
      <div repeat="{{v in list}}">
      <text>No. {{$index + 1}}, {{v.nickname}}</text>
      </div>
    • 对象参数的顺序

      1
      2
      3
      <div repeat="{{(key, value) in list}}">
      <text>No. {{key + 1}}, {{value.nickname}}</text>
      </div>
    • track-by

      1
      <div repeat="{{item in items}}" track-by="item.id" class="{{gender}}"></div>
  • vue: v-for

    • 移除$index索引

    • 对象参数的改变:改为(value, key), 与通用的对象迭代器保持一致

      1
      2
      3
      <div repeat="{{(value, key) in list}}">
      <text>No. {{key + 1}}, {{value.nickname}}</text>
      </div>
    • track-by 替换为v-bind

      1
      <div v-for="item in items" v-bind:key="item.id">
  • weex

    1
    data: { value: 'x' }
  • vue

    1
    props: { value: { default: 'x' } }

动态数据

1
data: function () { return { value: 'x' } }

  • 获取节点

    • weex: this.$el('xxx')

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <template>
      <container>
      <text id="top">Top</text>
      </container>
      </template>
      <script>
      module.exports = {
      methods: {
      toTop: function () {
      var top = this.$el('top')
      }
      }
      }
      </script>
    • vue

      1
      2
      this.$refs.xxx

  • 事件绑定

    • weex

      1
      <div onclick="handler"></div>
    • vue

      1
      <div @click="handler"></div>
  • 事件触发

    • weex: dispatch和broadcast

      1
      this.$dispatch()
      1
      this.$broadcast()
    • vue: emit

      1
      this.$emit()

    注:Weex 的 $dispatch 与组件无关,在任意组件中都可以通过 $on 捕获到,Vue 的$emit 用于触发组件(标签)的自定义事件。

  • 原生事件

    • weex

      1
      onclick="xxx"
    • vue

      1
      @click.native="xxx"

migration-from-weex

如何将原有 Weex 项目改造成 Vue 版本

Updated time: 14/06/2017

Weex 本身有一套语法规则,和 Vue 本身很相似,现在 Weex 与 Vue 有了官方合作,支持将 Vue 2.x 作为内置的前端框架,我们也推荐大家使用 Vue 2.x 的语法开发原生应用。对于现存旧版的 .we 文件,建议大家将其改造成 Vue 版本。

要解决的问题

将内核切换成 Vue 之后,原先基于 Weex 语法开发的项目将如何过渡到 Vue ?

首先需要明确一点:Weex 原有的前端框架也会继续存在于 WeexSDK 中,依然支持 .we 文件格式的写法。

此外,由于 .we.vue 文件的格式本身就比较接近,所以迁移成本比较小,建议大家将现有 .we 格式的文件都转换成 .vue 格式。我们也推出了相应的工具和方法辅助迁移,在内部也有大量的成功实践,下边将重点介绍一下将 .we 文件转成 .vue 文件的方法。

第一步,借助工具实现语法转换

首先介绍一个工具: weex-vue-migration ,它可以自动将 .we 文件转为 .vue 文件,绝大多数的模板语法都能自动转换,语法差异如下:

Weex Vue
生命周期 ready: function() {} mounted: function() {}
条件指令 if="{{!foo}}" v-if="!foo"
循环指令 repeat="{{item in list}}" v-for="item in list"
样式类名 class="btn btn-{{type}}" :class="['btn', 'btn-' + type]"
内联样式 style="color:{{textColor}}" :style="{ color: textColor }"
事件绑定 onclick="handler" @click="handler"
原生事件 onclick="xxx" `@click.native=”xxx”`
数据绑定 src="{{rightItemSrc}}" :src="rightItemSrc"
内容/槽 <content></content> <slot></slot>
数据初始化 data: { value: 'x' } data: function() { return { value: 'x' } }
标签 ID id="xxx" ref="xxx"
获取节点 this.$el('xxx') this.$refs.xxx

想要了解更多语法差异的细节,可以参考这篇文章:《Weex 和 Vue 2.x 的语法差异》

使用方法

首先安装工具:

1
npm install weex-vue-migration -g

转换文件:

1
weex-vue-migrate demo.we

转换成功后,将会在当前目录下生成 demo.vue 文件,控制台将会有如下输出:

1
2
[Success]: Migrate demo.we => demo.vue in 33ms
Migration finished in 0.035s

除了逐个转换 .we 文件以外,weex-vue-migration 还支持批量转换整个目录,参考其说明文档可以了解更详细的使用方法。

注意事项

转换工具将不再支持 Weex 中废弃的语法,如果代码中有如下写法,建议先手动修改再做转换。

  1. 忽略 require('@weex-components') 语句,可以通过 npm 包的方式引入外部组件。
  2. 无法转换 repeat="list" 写法,仅支持 repeat="item in list" 格式。
  3. 不支持转换 <script type="config"></script>,目前 Vue 中不支持原有的降级配置。

第二步,手动调整代码细节

模板和样式的转换都可以借助工具轻易转换过来,<script> 中基本的语法也可以转换;但是由于 javascript 的写法比较灵活,仅仅使用工具做转换,并不一定能完美过渡。工具只能处理语法但是理解不了代码中的逻辑,在 Weex 和 Vue 的框架特性存在一些差异,有些差异还是需要手动修改才可以生效。

提示:在代码中使用的“黑科技”越多,项目就越难以转换。

样式单位

.we 文件写样式时,开发者通常都不写长度单位,默认会被视为 px。在新的 Vue 版本的 Web 渲染器中,<style> 中的样式将会直接转化成 CSS class,如果不写单位、浏览器将无法正确识别,会导致在 Web 端无法正常渲染。Native 环境中不受影响。

尽管不影响 Native 页面的渲染,也建议给样式长度加上单位 px

旧框架中的内置属性

  • vm._app
    • vm._app.differ
    • vm._app.doc
    • vm._app.updateActions()

事件派发机制

  • $dispatch$broadcast$call 方法已经废弃。
  • $emit 行为不一致。
    可以使用 Vuex 管理数据状态。

直接操作 Virtual-DOM

Weex 和 Vue 中的 Virtual-DOM 格式并不相同,如果你使用了 this.$el('id') 获取了某个组件的 element 之后,又修改了其中的某些属性或者调用了某些方法,这些操作在 Vue 中很难找到直接的对应写法。

从另一个角度讲,我们也非常不建议在 Weex 或 Vue 项目中直接操作 Virtual-DOM,这些写法都应该修改。

调整开发环境和工具

在文件转换完成后,还需要重新调整一下开发环境。

文件的编译

weex-loader 同时支持编译 .we.vue 文件,如果你使用的是 webpack 来配置编译环境,将不需要做任何改变就能直接编译 .vue 文件。

需要注意的是,Vue 本身就是一个独立的前端框架,使用 Vue 编写的项目在 Web 上完全可以不依赖 Weex 容器运行。在这种情况下,需要配置基于 vue-loader 的编译脚本生成适用于 Web 平台 js 文件;然后引入 Vue 格式的 Weex 组件库就可以在 Web 中。

辅助工具

Weex 提供了 weex-toolkit 的脚手架工具来辅助开发和调试、weex-pack 实现打包原生应用;同样在 Vue 中也有
vue-cli 脚手架工具。Weex 和 Vue 的工具互相做了适配,建议在创建项目和开发
Vue 项目的时候使用 vue-cli ,在调试时使用 weex-toolkit,在打包原生应用时使用 weex-pack

platform-difference

Weex 和 Web 平台的差异

Updated time: 14/06/2017

Weex 是一个跨平台解决方案,Web 平台只是其一种运行环境,除此之外还可以在 Android 和 iOS 客户端中运行。原生开发平台和 Web 平台之间的差异,在功能和开发体验上都有一些差异。

Weex 环境中没有 DOM

DOM(Document Object Model),即文档对象模型,是 HTML 和 XML 文档的编程接口,是 Web 中的概念。Weex 的运行环境以原生应用为主,在 Android 和 iOS 环境中渲染出来的是原生的组件,不是 DOM Element。

不支持 DOM 操作

既然原生环境中不支持 Web API,没有 ElementEventFile 等对象,详细列表可以参考 Web APIs on MDN。不支持选中元素,如 document.getElementById
document.querySelector 等;当然也不支持基于 DOM API 的程序库(如 jQuery)。

有限的事件类型

Weex 支持在标签上绑定事件,和在浏览器中的写法一样,但是 Weex 中的事件是由原生组件捕获并触发的,行为和浏览器中有所不同,事件中的属性也和 Web 中有差异。

  • 并不支持 Web 中所有的事件类型,详情请参考《通用事件》
  • 不区分事件的捕获阶段和冒泡阶段,相当于 DOM 0 级事件。

Weex 环境中没有 BOM

BOM(Browser Object Model),即浏览器对象模型,是浏览器环境为 javascript 提供的接口。Weex 在原生端没有并不基于浏览器运行,不支持浏览器提供的 BOM 接口。

没有 windowscreen 对象

Weex 中并未提供浏览器中的 windowscreen 对象,不支持使用全局变量。如果是想要获取设备的屏幕或环境信息,可以使用 WXEnvironment 变量。

  • WXEnvironment
    • weexVersion: WeexSDK 的版本。
    • appName: 应用的名称。
    • appVersion: 应用的版本。
    • platform: 运行平台,可能的值是 WebAndroidiOS 之一。
    • osName: 系统的名称。
    • osVersion: 系统版本。
    • deviceWidth: 设备宽度。
    • deviceHeight: 设备高度。

没有 document 对象

在浏览器中 document 表示了当前活动的文档模型,在 Android 和 iOS 环境中并没有这个对象,也不支持与其相关的 DOM 操作。

没有 historylocationnavigator 对象

  • history 保存了当前页面的历史记录,并且提供了前进后退操作。
  • location 记录了当前页面 URL 相关的信息。
  • navigator 记录了当前浏览器中的信息。

这些接口与浏览器自身的实现有关,可以控制页面的前进后退并且获取状态信息。虽然在 Android 和 iOS
中也有“历史”和“导航”的概念,但是它是用于多个管理视图之间的跳转的。换句话说,在浏览器中执行“前进”、“后退”仍然会处于同一个页签中,在原生应用中“前进”、“后退”则会真实的跳转到其他页面。

此外 Weex 也提供了 navigator 模块来操作页面的跳转,使用方法参考《navigator 导航控制》

能够调用移动设备原生 API

在 Weex 中能够调用移动设备原生 API,使用方法是通过注册、调用模块来实现。其中有一些模块是 Weex 内置的,如 clipboard 、 navigator 、storage 等。

为了保持框架的通用性,Weex 内置的原生模块有限,不过 Weex 提供了横向扩展的能力,可以扩展原生模块,具体的扩展方法请参考《iOS 扩展》《Android 扩展》

有些接口在浏览器环境中也存在,不过在使用时应该注意浏览器的兼容性;如剪贴板功能,出于安全性考虑,绝大多数浏览器都限制其使用。

integrate-devtool-to-ios

集成 Devtools 到 iOS

Updated time: 14/06/2017

Weex Devtools 能够方便调试 Weex 页面,但此功能离不开 Native 的支持。如何让你的 App 也集成 Devtools,在本章将会详细说明 iOS 端如何接入 Weex Devtools。

iOS 应用接入

添加依赖

方法一:cocoapods 依赖

在工程目录的 podfile 添加如下代码

1
2
source https://github.com/CocoaPods/Specs.git,
pod 'WXDevtool', '0.7.0', :configurations => ['Debug'],

目前有如下几个版本:

0.7.0, 0.6.1, 0.1.1, 0.1.0 [master repo]


可以通过更新本地 podspec repo,pod search 来查询最新版本,在 podfile 文件添加依赖。

推荐在DEBUG模式下依赖。

方法二:github 源码依赖
  1. 拉取最新的WXDevtool代码。

  2. 按照如下图示:直接拖动source目录源文件到目标工程中

  1. 按照红框中配置勾选

在相对较大的互联网 App 研发中, framework 静态库被广泛应用,所以推荐使用方法一接入。

集成功能

如果按照方法一接入:podfile 的方式,添加头文件包含:

1
#import <TBWXDevtool/WXDevtool.h>

如果按照方法二接入:源码依赖的方式,添加头文件包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#import "WXDevtool.h"

查看 WXDevtool 头文件如下:

object-c
#import <Foundation/Foundation.h>
@interface WXDevTool : NSObject
/**
* set debug status
* @param isDebug : YES:open debug model and inspect model;
* default is NO,if isDebug is NO, open inspect only;
* */
+ (void)setDebug:(BOOL)isDebug;
/**
* get debug status
* */
+ (BOOL)isDebug;
/**
* launch weex debug
* @param url : ws://ip:port/debugProxy/native, ip and port is your devtool server address
* eg:@"ws://30.30.29.242:8088/debugProxy/native"
* */
+ (void)launchDevToolDebugWithUrl:(NSString *)url;
@end

`setDebug`:参数为 `YES` 时,直接开启 debug 模式,反之关闭,使用场景如下所述
在你自己的程序中添加如下代码:
object-c
[WXDevTool launchDevToolDebugWithUrl:@"ws://30.30.31.7:8088/debugProxy/native"];

其中的 ws 地址正是 Weex debug 控制台中出现的地址,直接 copy 到 launchDevToolDebugWithUrl 接口中。

如果程序一启动就开启 Weex 调试,需要在 WeexSDK 引擎初始化之前添加代码:

1
2
[WXDevTool setDebug:YES];
[WXDevTool launchDevToolDebugWithUrl:@"ws://30.30.31.7:8088/debugProxy/native"];

附加页面刷新功能
  • 为什么需要页面刷新功能?

    如下图所示,当点击 debugger 按钮时,js 的运行环境会从手机端(JavaScriptCore)切换到 Chrome(V8),这时需要重新初始化 Weex 环境,重新渲染页面。页面渲染是需要接入方在自己的页面添加。

  • 什么场景下需要添加页面刷新功能?

    • 点击 debugger 按钮调试
    • 切换 RemoteDebug 开关
    • 刷新 Chrome 页面(command+R)
  • 如何添加刷新

    在 Weex 页面初始化或 viewDidLoad 方法时添加注册通知,举例如下:

    1
    [[NSNotificationCenter defaultCenter] addObserver:self selector:notificationRefreshInstance: name:@"RefreshInstance" object:nil];

最后千万记得dealloc 方法中取消通知,如下所示

1
2
3
4
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

页面刷新实现,先销毁当前 instance,然后重新创建 instance,举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 - (void)render
{
CGFloat width = self.view.frame.size.width;
[_instance destroyInstance];
_instance = [[WXSDKInstance alloc] init];
_instance.viewController = self;
_instance.frame = CGRectMake(self.view.frame.size.width-width, 0, width, _weexHeight);

__weak typeof(self) weakSelf = self;
_instance.onCreate = ^(UIView *view) {
[weakSelf.weexView removeFromSuperview];
weakSelf.weexView = view;
[weakSelf.view addSubview:weakSelf.weexView];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, weakSelf.weexView);
};
_instance.onFailed = ^(NSError *error) {

};

_instance.renderFinish = ^(UIView *view) {
[weakSelf updateInstanceState:WeexInstanceAppear];
};

_instance.updateFinish = ^(UIView *view) {
};
if (!self.url) {
return;
}
NSURL *URL = [self testURL: [self.url absoluteString]];
NSString *randomURL = [NSString stringWithFormat:@"%@?random=%d",URL.absoluteString,arc4random()];
[_instance renderWithURL:[NSURL URLWithString:randomURL] options:@{@"bundleUrl":URL.absoluteString} data:nil];
}

具体实现可参考 playground WXDemoViewController.m 文件

说明:目前版本需要注册的通知名称为固定的 “RefreshInstance”,下个版本会添加用户自定义 name 。

使用

如果未安装 Debugger Server,在命令行执行 npm install -g weex-toolkit 既可以安装调试服务器,运行命令 weex debug 就会启动 DebugServer
并打开一个调试页面(详情请查看 《Get started》)。页面下方会展示一个二维码,这个二维码用于向 App 传递 Server 端的地址建立连接。

  1. 日志级别控制

    日志级别可以控制native端关于weex的日志。

    日记级别描述如下:

    1
    2
    3
    4
    5
    6
    7
    Off       = 0,
    Error = Error
    Warning = Error | Warning,
    Info = Warning | Info,
    Log = Log | Info,
    Debug = Log | Debug,
    All = NSUIntegerMax

    解释:off 关闭日志,Warning 包含 Error、Warning,Info 包含 Warning、Info,Log 包含 Info、Log,Debug 包含 Log、Debug,All 包含所有。

  2. Vdom/Native tree选择

图一

图二

点击图一所示native选项会打开图二,方便查看native tree以及view property

图三

图四

点击图三所示 vdom 选项会打开图四,方便查看 vdom tree 以及 component property。

integrate-devtool-to-android

集成 Devtools 到 Android

Updated time: 21/07/2017

Weex Devtools 能够方便调试 Weex 页面,但此功能离不开 Native 的支持。如何让你的 App 也集成 Devtools,在本章将会详细说明 Android 端如何接入 Weex Devtools。

Android 应用接入

添加依赖

可以通过 Gradle 或者 Maven 添加对 devtools aar 的依赖,也可以直接对源码依赖。强烈建议使用最新版本,因为 Weex SDK 和 devtools都在快速的迭代开发中,新版本会有更多惊喜,同时也修复老版本中一些问题。最新的 release 版本可在这里查看。所有的 release 版本都会发布到 jcenter repo

  • Gradle 依赖

    1
    2
    3
    dependencies {
    compile 'com.taobao.android:weex_inspector:${version}'
    }
  • Maven依赖

    1
    2
    3
    4
    5
    6
    <dependency>
    <groupId>com.taobao.android</groupId>
    <artifactId>weex_inspector</artifactId>
    <version>${version}</version>
    <type>pom</type>
    </dependency>
  • 源码依赖

    需要复制 inspector 目录到你的 App 的同级目录,然后在工程的
    settings.gradle 文件下添加 include ":inspector",此过程可以参考 playground 源码的工程配置及其配置,然后在App 的 build.gralde 中添加依赖。

    1
    2
    3
    dependencies {
    compile project(':inspector')
    }

    另外 weex_inspector 中有一部分包是以 provided 的方式引入,接入方需要自行解决依赖和版本冲突。

    • provided方式引用的包
      1
      2
      3
      4
      5
      6
      7
      dependencies {
      provided 'com.google.code.findbugs:jsr305:2.0.1'
      provided 'com.android.support:appcompat-v7:23.1.1'
      provided 'com.taobao.android:weex_sdk:0.8.0'
      provided 'com.alibaba:fastjson:1.1.45+'
      ...
      }
  • 反射引用的包(0.8.0.0以上版本)

    1
    2
    3
    4
    5
    dependencies {
    compile 'com.squareup.okhttp:okhttp:2.3.0'
    compile 'com.squareup.okhttp:okhttp-ws:2.3.0'
    ...
    }

    或者

    1
    2
    3
    4
    5
    dependencies {
    compile 'com.squareup.okhttp:okhttp:3.4.1'
    compile 'com.squareup.okhttp:okhttp-ws:3.4.1'
    ...
    }

版本兼容

weex sdk weex inspector Debugger Server
0.13+ 0.12+ 0.2.39+
0.8.0.1+ 0.0.8.1+ 0.2.39+
0.7.0+ 0.0.7.13 0.2.38
0.6.0+ 0.0.2.2 -

添加 Debug 模式开关

控制调试模式的打开和关闭的关键点可以概括为三条规则。

规则一:通过 sRemoteDebugModesRemoteDebugProxyUrl 来设置开关和 Debugger Server 地址。

Weex SDK 的 WXEnvironment 类里有一对静态变量标记了 Weex 当前的调试模式是否开启分别是:

1
2
public static boolean sRemoteDebugMode; // 是否开启 debug 模式,默认关闭
public static String sRemoteDebugProxyUrl; // DebugServer的websocket地址

无论在 App 中无论以何种方式设置 Debug 模式,都必须在恰当的时机调用类似如下的方法来设置 WXEnvironment.sRemoteDebugModeWXEnvironment.sRemoteDebugProxyUrl

1
2
3
4
private void initDebugEnvironment(boolean enable, String host) {
WXEnvironment.sRemoteDebugMode = enable;
WXEnvironment.sRemoteDebugProxyUrl = "ws://" + host + ":8088/debugProxy/native";
}

规则二:修改 sRemoteDebugMode 后一定要调用WXSDKEngine.reload()

一般來說,在修改了 WXEnvironment.sRemoteDebugMode 以后调用了 WXSDKEngine.reload() 方法才能够使 Debug模式生效。WXSDKEngine.reload() 用来重置 Weex 的运行环境上下文,在切换调试模式时需要调用此方法来创建新的 Weex 运行时和 DebugBridge 并将所有的 JS 调用桥接到调试服务器执行。在 reload 过程中会调用 launchInspector,这就是 SDK 控制 Debug 模式最核心一个方法,其传入参数即为 sRemoteDebugMode,若为 true 则该方法中尝试以反射的方式获取 DebugBridge 用来在远端执行 JS,否则在本地运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void launchInspector(boolean remoteDebug) {
if (WXEnvironment.isApkDebugable()) {
try {
if (mWxDebugProxy != null) {
mWxDebugProxy.stop();
}
HackedClass<Object> debugProxyClass = WXHack.into("com.taobao.weex.devtools.debug.DebugServerProxy");
mWxDebugProxy = (IWXDebugProxy) debugProxyClass.constructor(Context.class, WXBridgeManager.class)
.getInstance(WXEnvironment.getApplication(), WXBridgeManager.this);
if (mWxDebugProxy != null) {
mWxDebugProxy.start();
if (remoteDebug) {
mWXBridge = mWxDebugProxy.getWXBridge();
} else {
if (mWXBridge != null && !(mWXBridge instanceof WXBridge)) {
mWXBridge = null;
}
}
}
} catch (HackAssertionException e) {
WXLogUtils.e("launchInspector HackAssertionException ", e);
}
}
}

只要遵循上面的原理,开启 Debug 模式的方式和时机可由接入方灵活实现。从 launchInspector 可以看到,SDK 对 devtools 的 aar 包并无强依赖,我们的 App 只需要在 Debug 包中打包该 aar 即可,这样多少可以缓解包大小问题和安全问题。

例外若修改 WXEnvironment.sRemoteDebugMode 的时机在 WXBridgeManager 初始化和 restart 和之前则 WXSDKEngine.reload() 可忽略.

规则三:通过响应 ACTION_DEBUG_INSTANCE_REFRESH 广播及时刷新。

广播 ACTION_DEBUG_INSTANCE_REFRESH 在调试模式切换和 Chrome 调试页面刷新时发出,主要用来通知当前的 Weex容器以 Debug 模式重新加载当前页。在 playground 中的处理过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RefreshBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (IWXDebugProxy.ACTION_DEBUG_INSTANCE_REFRESH.equals(intent.getAction())) {
if (mUri != null) {
if (TextUtils.equals(mUri.getScheme(), "http") || TextUtils.equals(mUri.getScheme(), "https")) {
loadWXfromService(mUri.toString());
} else {
loadWXfromLocal(true);
}
}
}
}
}

如果接入方的容器未对该广播做处理,那么将不支持刷新和调试过程中编辑代码时的 watch 功能。

接入示例

最简单方式就是复用 Playground 的相关代码,比如扫码和刷新等模块,但是扫码不是必须的,它只是与 App 通信的一种形式,二维码里的包含 DebugServer IP 及 bundle 地址等信息,用于建立 App 和 Debugger Server 之间的连接及动态加载 bundle。在 Playground 中给出了两种开启 debug 模式的范例。

  • 范例1:通过在 XXXApplication 中设置开关打开调试模式
    1
    2
    3
    4
    5
    6
    public class MyApplication extends Application {
    public void onCreate() {
    super.onCreate();
    initDebugEnvironment(true, "xxx.xxx.xxx.xxx"/*"DEBUG_SERVER_HOST"*/);
    }
    }

这种方式最直接,在代码中直接 hardcode 了开启调试模式,如果在 SDK 初始化之前调用甚至连 WXSDKEngine.reload() 都不需要调用,接入方如果需要更灵活的策略可以将 initDebugEnvironment(boolean enable, String host)WXSDKEngine.reload() 组合在一起在合适的位置和时机调用即可。

  • 范例2:通过扫码打开调试模式
    Playground 中较多的使用扫码的方式传递信息,不仅用这种方式控制 Debug 模式的开关,而且还通过它来传入 bundle 的 url 直接调试。应当说在开发中这种方式是比较高效的,省去了修改 SDK 代码重复编译和安装 App 的麻烦,缺点就是调试工具这种方式接入需要 App 具有扫码和处理特定规则二维码的能力。除了 Playground 中的方式,接入方亦可根据业务场景对 Debugger 和接入方式进行二次开发。

Playground 集成的具体代码可参考如下两个文件:

  • 开关控制,主要参考对二维码的处理部分,详见 WXApplication.java

  • 刷新控制 ,主要参考是对容器 ACTION_DEBUG_INSTANCE_REFRESH 的处理,详见 WXPageActivity.java

牛刀小试

前置工作

如果未安装 Debugger Server,在命令行执行 npm install -g weex-toolkit 既可以安装调试服务器,运行命令 weex debug 就会启动 DebugServer
并打开一个调试页面(详情请查看 《Get Started》)。页面下方会展示一个二维码,这个二维码用于向 App 传递 Server 端的地址建立连接。

开始调试

如果你的 App 客户端完成了以上步骤那么恭喜你已经接入完毕,可以愉快的调试 Weex bundle 了,调试体验和网页调试一致!建议新手首先用官方的 Playground 体验一下调试流程。只需要启动 App 扫描 Chrome 调试页面下方的第一个二维码即可建立与 Debugger Server 的通信,Chorome 的调试页面将会列出连接成功的设备信息。

主要步骤如下

  1. 如果你要加载服务器上 bundle,第一步就是要让你的 bundle sever 跑起来. 在 Playground 中特别简单,只需要你到 Weex 源码目录下,运行 ./start 即可。
  2. 命令行运行 weex debug 启动 Debugger Server,Chrome 将会打开一个网页,在网页下方有一个二维码和简单的介绍。
  3. 启动 App 并确认打开调试模式。你将在上一步中打开的网页中看到一个设备列表,每个设备项都有两个按钮,分别是 DebuggerInspector
  4. 点击 Inspector Chrome 将创建 Inspector 网页;点击 Debugger Chrome 将创建 Debugger 网页;二者是相互独立的功能,不相互依赖。

背景知识

Devtools 组件介绍

Devtools 扩展了 Chrome Debugging Protocol,在客户端和调试服务器之间的采用 JSON-RPC 作为通信机制,本质上调试过程是两个进程间协同,相互交换控制权及运行结果的过程。更多细节还请阅读
Weex Devtools Debugger 的技术选型实录这篇文章。

  • 客户端

    Devtools 客户端作为 aar 被集成 App 中,它通过 webscoket 连接到调试服务器,此处并未做安全检查。出于安全机制及包大小考虑,强烈建议接入方只在 debug 版本中打包此 aar。

  • 服务器

    Devtools 服务器端是信息交换的中枢,既连接客户端,又连接 Chrome,大多数情况下扮演一个消息转发服务器和 Runtime Manager 的角色。

  • Web端

    Chrome 的 V8 引擎扮演着 Bundle javascript runtime 的角色。开启 debug 模式后,所有的 bundle js 代码都在该引擎上运行。另一方面我们也复用了 Chrome 前端的调试界面,例如设置断点,查看调用栈等,调试页关闭则 runtime 将会被清理。

调试的大致过程请参考如下时序图。

FAQ

在各业务接入过程中,陆续发现一些问题,对高频次的问题解答如下,开发中以 weex debug -V 的方式启动 Debugger Server 可以看到 server 端的 log 信息,对照上文中的时序图对于定位问题还是非常有帮助,建议调试中默认开启 server 端 log。

  1. 扫码 App 在 DebugServerProxy 中抛出 class not found

    已知的原因如下:

    • weex_inspector 以 provided 方式引用的包是否引入成功,如 fastjson 等。
    • weex_inspector 以 compile 方式引用的包是否引入成功,某些 app 重新引入 com.squareup.okhttp:okhttp:2.3.0com.squareup .okhttp:okhttp-ws:2.3.0 则不再报错。
    • 混淆规则影响反射。
  2. playground 扫码调试 crash

    • 已知的原因如下:

    系统为 android 6+,崩溃信息提示进程需要 android.permission.READ_PHONE_STATE 权限,代码中未做权限检查,在 0.0.2.7 版本以后已修复,不再需要此权限。

  3. 扫码后设备列表页并没有出现我的设备信息

    已知的原因如下:

    • Debugger Server 和手机在不同网段,被防火墙隔离。
    • 手机连接了 PC 端的代理,当前尚不支持。
    • 多进程连接服务器端的同一端口,比如在 Application 的 onCreate 中初始化 sdk,若多个进程连接服务器端的同一端口则报错,在 0.0.2.3 版本以后已支持多进程无此问题。
  4. 调试过程中频繁刷新连接失败,Server 端提示重新启动 App,非必现

    已知的原因如下:

    • 多线程操作网络连接引起,在频繁的即断即连时容易触发。在 0.0.7.1 版本已修复。

注入自定义WebSocket Client

目前Inspector以反射的方式动态调用了okhttp-ws库中的相关代码,可以兼容的okhttp与okhttp-ws版本为:

  • okhttp, okhttp-ws 2.7.5版本以下
  • okhttp3, okhttp3-ws 3.5版本以下
    如果客户端中集成的版本与上述版本不匹配,则可以使用 WeexInspector.overrideWebSocketClient 方法来注入自定义的WebSocket实现,示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class CustomWebSocketClient implements IWebSocketClient {
private WebSocket ws;
@Override
public boolean isOpen() {
return ws != null;
}
@Override
public void connect(String wsAddress, final WSListener listener) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(5, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(5, TimeUnit.SECONDS);
okHttpClient.setWriteTimeout(5, TimeUnit.SECONDS);
Request request = new Request.Builder().url(wsAddress).build();
WebSocketCall webSocketCall = WebSocketCall.create(okHttpClient, request);
webSocketCall.enqueue(new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Request request, Response response) throws IOException {
ws = webSocket;
listener.onOpen();
}
@Override
public void onMessage(BufferedSource payload, WebSocket.PayloadType type) throws IOException {
if (WebSocket.PayloadType.TEXT == type) {
listener.onMessage(payload.readUtf8());
}
}
@Override
public void onPong(Buffer payload) {
//ignore
}
@Override
public void onClose(int code, String reason) {
listener.onClose();
}
@Override
public void onFailure(IOException e) {
listener.onFailure(e);
}
});
}
@Override
public void close() {
if (ws != null) {
try {
ws.close(CloseCodes.NORMAL_CLOSURE, "Normal closure");
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void sendMessage(int requestId, String message) {
if (ws != null) {
try {
ws.sendMessage(WebSocket.PayloadType.TEXT, new Buffer().writeString(message, Charset.defaultCharset()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

extend-jsfm

拓展 JS framework

Updated time: 14/06/2017

这部分扩展能力还在讨论尝试中,可能随时会有调整,请留意。

Weex 希望能够尊重尽可能多的开发者的使用习惯,所以除了 Weex 官方支持的 Vue 2.0 之外,开发者还可以定制并横向扩展自己的或自己喜欢的 JS Framework。完整一套 JS Framework 的定制和扩展需要以下几个步骤:

  1. 首先你要有一套完整的 JS Framework。
  2. 了解 Weex 的 JS 引擎的特性支持情况。
  3. 适配 Weex 的 native DOM APIs。
  4. 适配 Weex 的初始化入口和多实例管理机制。
  5. 在 Weex JS runtime 的 framework 配置中加入自己的 JS Framework 然后打包。
  6. 基于该 JS Framework 撰写 JS bundle,并加入特定的前缀注释,以便 Weex JS runtime 能够正确识别。

Weex JS 引擎的特性支持情况

  • 在 iOS 下,Weex 使用的是系统自带的 JavaScriptCore,所以 ES 支持情况取决于操作系统的版本。目前保守判断,ES5 的特性市面上主流的 iOS 设备都是可以完美支持的,但是 ES6+ 的特性存在一定的碎片化。
  • 在 Android 下,Weex 使用的是 UC 提供的 v8 内核,出于体积、性能和稳定性的考虑,我们使用的并不是最新版本的 v8 内核,同样的保守判断,ES5 特性能够全部支持,包括严格模式、Object.freeze 等。
  • Weex JS 引擎不支持 HTML DOM APIs 和 HTML5 JS APIs,这包括 document, setTimeout 等。
  • 在此基础上,我们加入了 Promise 的 polyfill,以及 console 的 polyfill。
  • 额外的,为了尽可能的确保 JS 引擎的长效内存管理,我们对一个通用的全局对象进行了 Object.freeze() 冻结操作,这包括:
    • Object
    • Object.prototype
    • Array
    • Array.prototype
    • String.prototype
    • Number.prototype
    • Boolean.prototype
    • Error.prototype
    • Date.prototype
    • RegExp.prototype

适配 Weex 的初始化入口和多实例管理机制

开发者提供的 JS Framework 最终需要包装成一个 CommonJS 包,并且这个包需要对外暴露以下方法:

框架初始化

  • init(config)

    • config
      • Document
      • Element
      • Comment
      • TaskSender
      • CallbackManager

    该方法会把 Weex 提供的 Native DOM 类和两个辅助类放到 config 参数中,并允许框架本身完成初始化。

小提示:同时,框架作者也能够通过在框架初始化时传入不同的 config 来进行框架的测试或环境模拟。

参数格式介绍

  • TaskSender: wip…
  • CallbackManager: wip…

注册可用的 native 组件和模块

  • registerComponents(components)
  • registerModules(modules)
    这两个方法会在框架初始化之后立刻调用,这样框架就能够知道当前的客户端支持哪些组件和模块,在一些特殊逻辑或必要的情况下,有机会为框架本身提供参考信息。

参数格式介绍

  • components: Array: 描述组件的数组,每一项包括:
    • type: string: 组件名称,比如 div
    • methods: string[]: 可选项,该组件支持的方法名称列表,这些方法可以遵循 Weex 的 native DOM APIs 的组件方法调用方式。
  • modules: Object: 描述一系列模块的散列表,每一项的 key 是模块名,每一项的值是一个数组,数组里的每一项描述了一个该模块中的一个方法,该方法的信息包括:
    • name: string: 方法名
    • args: string[]: 参数个数和类型描述
      例如:
      1
      2
      3
      4
      5
      6
      7
      8
      registerComponents([
      { type: 'web', methods: ['goBack', 'goForward', 'refresh']}
      ])
      registerModules({
      event: [
      {name: 'openURL', args: ['string']}
      ]
      })

多实例生命周期管理

  • createInstance(instanceId, code, config, data, env)
  • refreshInstance(instanceId, data)
  • destroyInstance(instanceId)
    每个 Weex 页面都有被创建、被销毁两个必经阶段,同时在 Weex 页面运行过程中,native 有机会主动向 Weex 页面发送消息,不同的框架可以根据自己框架的设计对这样的消息有自己的理解和设计实现。
参数格式介绍
  • instanceId: string: 该 Weex 页面的唯一 id,由 native 产生。
  • code: string: 该 Weex 页面的 JS bundle 的代码,通过 native 传入。
  • config: Object?: 该 Weex 页面的配置信息,比如代表该 bundle 地址的 bundleUrl,由 native 配置产生,和 JS bundle 本身的内容无关。
  • data: Object?: Native 有机会在创建一个 Weex 页面的时候,传入一份外部数据,JS 框架也有机会接此机会为相同的 JS bundle 配合不同的 data 生成不同的页面内容。
  • env: Object?: 当前 Weex 页面的相关环境信息,各字段的含义:
    • info: Object: 框架 info 信息,详见之后的 “JS Bundle 格式要求”。
    • config: Object: 等同该方法的第三个参数 config
    • callbacks: CallbackManager: 该 Weex 页面唯一的 CallbackManager 实例。
    • created: number: 该 Weex 页面的创建时间毫秒数。
    • framework: string: 该 Weex 页面基于的框架名,等同于 info.framework

Native 通信

  • receiveTasks(instanceId, tasks)
    Native 除了通过 refreshInstance 方法向 JS 框架层发送消息之外,更多的会通过 receiveTasks 发送用户事件或方法回调给 JS 框架。

比如用户点击了一个按钮,native 就会发送一个 fireEvent 类型的任务给 JS 框架,然后 JS 框架再处理相应的事件逻辑。这部分工作机制和 native DOM 接口中的 addEvent 的设计有关。

再比如用户发起了一个 fetch 网络请求,当请求在 native 端完成时,会以一个 callback 类型的任务发给 JS 框架。由于 native 无法传递 JavaScript 中的 function,所以实际上知会发送一个 callbackId 给 JS 框架。这部分工作机制和之前出现过的 CallbackManager 的设计有关。

辅助方法

  • getRoot(instanceId): JSON
    该方法可以返回文档主体结点的完整 JSON 描述,开发者可以以此查看到完整的 native DOM 树,具体返回值的格式和 native DOM 接口中的 toJSON() 方法返回值格式一致。此功能多用作开发者工具扩展。

在 WeexSDK 中配置 JS Framework

准备好你的 JS Framework 代码

1
2
3
4
5
6
7
8
9
// your-own-js-framework.js
export function init (config) { ... }
export function registerComponents (components) { ... }
export function registerModules (modules) { ... }
export function createInstance (id, code, config, data, env) { ... }
export function destroyInstance (id) { ... }
export function refreshInstance (id, data) { ... }
export function recieveTasks (id, tasks) { ... }
export function getRoot (id) { ... }

注册一个 JS Framework

1
2
3
4
import * as Vue from '...'
import * as React from '...'
import * as Angular from '...'
export default { Vue, React, Angular };

然后打包 JS runtime,集成到 WeexSDK 中。

JS Bundle 格式要求

框架 info

你需要保障基于该 JS Framework 的 JS Bundle 在文件开头带有如下格式的注释,我们称其为框架 info:

1
2
// { "framework": "Vue" }
...

这样 Weex JS 引擎就会识别出这个 JS bundle 需要用 Vue 框架来解析。并分发给 Vue 框架处理。同理,Weex 支持同时多种框架在一个移动应用中共存并各自解析基于不同框架的 JS bundle。

extend-to-html5

HTML5 扩展

Updated time: 14/06/2017

Weex 本身提供了很多内置组件和模块,也具备横向扩展的能力,允许开发者自行扩展和定制。需要注意的是,Weex 是一个跨平台的解决方案,扩展其内置组件或模块,需要在三端(Android、iOS、Web)中都有相应的实现。

Weex 将内核切换成 Vue 2.x 之后,在 Web 端扩展 Vue 组件将变得更加容易。

扩展 Web 组件

Vue.js 本身就是一个独立的前端框架,在浏览器中完全能够不基于 Weex 容器渲染。因此,针对 Weex 平台扩展 Vue.js 的 Web 端组件,和直接使用 Vue.js 开发一个 Web
组件是一样的。具体的组件编写方法可以参考其官方文档:组件 ,另外建议使用 .vue 格式的文件编写组件,使用方法参考:单文件组件

扩展组件示例

以扩展 <sidebar> 为例,首先应该编写组件自身的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- sidebar.vue -->
<template>
<div class="sidebar">
<slot></slot>
</div>
</template>
<style scoped>
.sidebar {
/* ... */
}
</style>
<script>
export default {
props: [],
data () {
return {}
}
}
</script>

然后在使用之前,全局注册 <sidebar> 组件:

1
2
3
4
import Vue from 'vue'
import Sidebar from './path/to/sidebar.vue'
// 全局注册 sidebar 组件
Vue.component('sidebar', Sidebar)

在扩展 Weex 组件时,如果只使用了 Weex 提供的内置组件,并且使用的都是 Weex 支持的样式,那么就和普通的自定义组件无异,不需要 Native 端再有相应的实现。

如果你定制组件时不得不用到目前 Weex 不支持的标签和样式,在这种情况下才是真正的“扩展”了 Weex 的组件,你还需要在 Android 和 iOS 中有相应的实现,不然会导致渲染异常。

扩展 Web 模块

除了通用组件以外,Weex 还有提供了通用的模块,可以方便的调用原生 API。通常来说,注册 Weex 模块要求三端都得有相应的实现,否则会影响其正常的使用。

注册模块

如果你引入了 weex-vue-render 这个库,那么在全局能获取到 weex 这个变量,其中提供了 registerModule 方法可以注册模块。

API 格式

  • registerModule
    1. name: {String} 必选,模块名称。
    2. define: {Object} 必选,模块的定义。

注册模块示例

下边的代码注册了一个名为 guide 的模块:

1
2
3
4
5
6
7
8
weex.registerModule('guide', {
greeting () {
console.log('Hello, nice to meet you. I am your guide.')
},
farewell () {
console.log('Goodbye, I am always at your service.')
}
})

使用模块

weex 上提供了 require 方法用于获取已注册的模块,直接传递模块名即可:

1
2
3
4
5
// 获取模块
const guide = weex.requireModule('guide')
// 可以直接调用模块中的方法
guide.greeting()
guide.farewell()

上述写法在 Native 环境中依然有效,只不过模块中的方法是由 Native 提供的。

extend-to-android

Android 扩展

Updated time: 17/07/2017

Weex 提供了扩展机制,可以根据自己的业务进行定制自己的功能。
主要分为两类扩展:

  • Module 扩展 非 UI 的特定功能。例如 sendHttp、openURL 等。
  • Component 扩展 实现特别功能的 Native 控件。例如:RichTextview,RefreshListview 等。
  • Adapter 扩展 Weex 对一些基础功能实现了统一的接口,可实现这些接口来定制自己的业务。例如:图片下载等。

Module 扩展

  1. Module 扩展必须继承 WXModule 类。
  2. 扩展方法必须加上 @WXModuleAnno 注解。Weex 会根据注解来判断当前方法是否要运行在 UI 线程,和当前方法是否是扩展方法。
  3. Weex是根据反射来进行调用 Module 扩展方法,所以Module中的扩展方法必须是 public 类型。
  4. 同样因为是通过反射调用,Module 不能被混淆。请在混淆文件中添加代码:-keep public class * extends com.taobao.weex.common.WXModule{*;}
  5. Module 扩展的方法可以使用 int, double, float, String, Map, List 类型的参数
  6. 完成 Module 后一定要在初始化时注册 WXSDKEngine.registerModule("myModule", MyModule.class); 否则会报类似错误:ReportException :undefined:9: TypeError: Object #<Object> has no method 'printLog'
    示例如下:
1
2
3
4
5
6
public class MyModule extends WXModule {
@WXModuleAnno(runOnUIThread = true)
public void printLog(String msg) {
Toast.makeText(mWXSDKInstance.getContext(),msg,Toast.LENGTH_SHORT).show();
}
}

JS 调用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>
<text onclick="click">点击我测试</text>
</div>
</template>
<script>
module.exports = {
methods: {
click: function() {
weex.requireModule('myModule').printLog("我是一个测试!");
}
}
}
</script>

支持 synchronous/asynchronous 回调

你可以添加 @JSMethod(uiThread = false或true) 注释来选择 moudle 的回调模式。请参见以下示例:

1
2
3
4
5
6
7
8
9
10
  // as sync-callback mode
@JSMethod (uiThread = false)
public void testSyncCall(){
WXLogUtils.d("WXComponentSyncTest : Thread.currentThread().getName());
}
// as async-callback mode
@JSMethod (uiThread = true)
public void testAsyncCall(){
WXLogUtils.e("WXComponentASynTest : Thread.currentThread().getName() );
}

Component 扩展

  1. Component 扩展类必须集成 WXComponent.
  2. Component 对应的设置属性的方法必须添加注解 @WXComponentProp(name=value(value is attr or style of dsl))
  3. Weex sdk 通过反射调用对应的方法,所以 Component 对应的属性方法必须是 public,并且不能被混淆。请在混淆文件中添加代码 -keep public class * extends com.taobao.weex .ui.component.WXComponent{*;}
  4. Component 扩展的方法可以使用 int, double, float, String, Map, List 类型的参数
  5. 完成 Component 后一定要在初始化时注册 WXSDKEngine.registerComponent("richtext",RichText.class);
    示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RichText extends WXComponent {
public RichText(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) {
super(instance, dom, parent, isLazy);
}
@Override
protected void initView() {
mHost=new TextView(mContext);
((TextView)mHost).setMovementMethod(LinkMovementMethod.getInstance());
}
@WXComponentProp(name = "tel")
public void setTelLink(String tel){
SpannableString spannable=new SpannableString(tel);
spannable.setSpan(new URLSpan("tel:"+tel),0,tel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
((TextView)mHost).setText(spannable);
}
}

JS 调用如下:

1
2
3
4
5
<template>
<div>
<richText tel="12305" style="width:200;height:100">12305</text>
</div>
</template>

Adapter扩展

图片下载:

需要时集成接口 IWXImgLoaderAdapter,实现 setImage 方法。

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ImageAdapter implements IWXImgLoaderAdapter {
public ImageAdapter() {
}
@Override
public void setImage(final String url, final ImageView view,
WXImageQuality quality, WXImageStrategy strategy) {
WXSDKManager.getInstance().postOnUiThread(new Runnable() {
@Override
public void run() {
if(view==null||view.getLayoutParams()==null){
return;
}
if (TextUtils.isEmpty(url)) {
view.setImageBitmap(null);
return;
}
String temp = url;
if (url.startsWith("//")) {
temp = "http:" + url;
}
if (view.getLayoutParams().width <= 0 || view.getLayoutParams().height <= 0) {
return;
}
Picasso.with(WXEnvironment.getApplication())
.load(temp)
.into(view);
}
},0);
}
}

组件方法支持

从WeexSDK 0.9.5开始,你可以定义组件方法

  • 在组件中如下声明一个组件方法

    1
    2
    3
    4
    @JSMethod
    public void focus(){
    //method implementation
    }
  • 注册组之后,你可以在weex 文件中调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <template>
    <mycomponent id='mycomponent'></mycomponent>
    </template>
    <script>
    module.exports = {
    created: function() {
    this.$el('mycomponent').focus();
    }
    }
    </script>

注:工程要添加依赖 compile 'com.squareup.picasso:picasso:2.5.2'

SDK混淆规则

若要在APP中使用混淆,请在相应的配置文件中添加如下规则:

1
2
3
4
5
6
7
-keep class com.taobao.weex.WXDebugTool{*;}
-keep class com.taobao.weex.devtools.common.LogUtil{*;}
-keep public class * extends com.taobao.weex.ui.component.WXComponent{*;}
-keep public class * extends com.taobao.weex.common.WXModule{*;}
-keepclassmembers class ** {
@com.taobao.weex.ui.component.WXComponentProp public *;
}