Weex尝鲜

Weex是阿里开源的类React Native技术,其实按知乎上的讨论基本可以说是整合Vue.js+React Native造的轮子(如何看待阿里无线前端发布的Weex?)。Weex是一款轻量级的移动端跨平台动态性技术解决方案,主要致力于使用Web方式开发出Native性能的App。Weex学习成本较RN相对较低, 可以说就是使用简易版的HTML/CSS/JavaScript以及自定义的一些组件和规则开发.we文件,完成界面控件布局、样式、数据绑定以及简单的事件绑定等。Weex提供了node.js的小工具可以将.we文件转换成js bundle 文件,客户端引入了weex sdk就可以解析js bundle文件,最终完成页面渲染。接下来在简单尝试下weex。

weex配置

.we文件开发其实任何编辑器都可以,不过需要使用Node安装Weex提供的.we文件转换工具,该工具可以将.we文件转换成weex sdk能够识别和解析的js bundle文件。

npm install -g weex-toolkit

主要介绍下Android端配置:
1、首先引入weex sdk,gradle添加weex依赖

compile 'com.taobao.android:weex_sdk:0.5.1@aar'

2、确保声明了网络权限

<uses-permission android:name="android.permission.INTERNET" />

3、配置ImageView加载网络图片形式
weex需要我们手动配置网络图片加载,否则imageview将无法正常工作。通常我们可以使用第三方图片加载库,这里我引入了Picasso来帮助我们加载网络图片。配置时机可以放在Application初始化中,这样全局有效。

private void initWeex(){
    InitConfig.Builder configBuilder = new InitConfig.Builder().setImgAdapter(new IWXImgLoaderAdapter() {
        @Override
        public void setImage(String url, ImageView view, WXImageQuality quality, WXImageStrategy strategy) {
            Picasso.with(getApplicationContext()).load(url).into(view);
        }
    });
    WXSDKEngine.initialize(this, configBuilder.build());
}

通过以上配置,客户端基本的weex环境也就配置好了。

.we文件开发

.we文件主要包括三大部分,<template></template>声明组件,<style></style>定义组件样式,<script></script>声明组件data、events,业务逻辑处理等,基本类似web端开发。template中可以使用{{}}进行data binding,将script中的data和events绑定到相应的组件中。具体语法不再介绍,下面是一个类似ViewPager的自动轮播banner例子。

<template>
    <div style="flex-direction: column;">
        <slider class="slider" interval="2000" auto-play="true">
            <div class="slider-pages" repeat="{{headline}}" onclick="openUrl(headline[$index].url)">
                <image class="image" src="{{image}}"></image>
                <text class="title">{{title}}</text>
            </div>
            <indicator class="indicator" if="shouldShowIndicators()"></indicator>
        </slider>
    </div>
</template>

<style>

    .image { 
        width: 750; 
        height: 260; 
    }

    .title { 
        margin-top: 20;
        margin-bottom: 20;
        text-align: left; 
        flex: 1; 
        color: black; 
        font-size: 35; 
    }

    .slider {
        width: 750;
        height: 450;
    }

    .slider-pages {
        padding-top: 30;
        flex-direction: column;
        width: 750;
        height: 400;
    }

    .indicator {
        height: 20;
        width: 750;
        position:absolute;
        left: 1;
        bottom: 1;
        item-color: grey;
        item-selectedColor: orange;
        item-size: 20;
    }

</style>

<script>
var weexModule = require('@weex-module/weexModule');
module.exports = {
    data: {
      headline:[]
    },
    methods: {
      openUrl: function (url) {
        weexModule.startActivity(url, function(err){
            console.log(err);
        });
      },
      shouldShowIndicators: function(){
        return this.headline.length > 1;
      }
    }
}

</script>

style默认屏幕宽度为750px,所以如果组件宽度为整屏宽度,直接定义为750即可。绑定onclick事件,其实只是属性设置并不是方法调用,如果不带对应方法不带参数直接使用方法名即可,但是其他地方如果进行方法调用,必须得加(),表示方法的调用,例如if="shouldShowIndicators()"

自定义Module

在script中我们引用了自定义的Module,负责与Native端通信,处理具体的业务逻辑。要引用自定义Module需要事先使用WXSDKEngine的registerModule方法进行注册,可以在Application启动时注册一些通用的Module,也可以在需要使用时再去注册一些具体业务逻辑Module。

try {
    WXSDKEngine.registerModule("weexModule", WeexModule.class);
} catch (WXException e) {
    e.printStackTrace();
}

自定义Module时,方法访问权限必须声明为public,并且必须使用@WXModuleAnno注解标识。这里,weexModule是个简单的负责Activity跳转的Module,并且回调了Activity启动结果。对应的WeexModule代码如下:

public class WeexModule extends WXModule {
    
    @WXModuleAnno
    public void startActivity(String url, String cb){
        Log.d("weex", "========" + url);
        boolean error = false;
        try {
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            mWXSDKInstance.getContext().startActivity(intent);
        } catch (ActivityNotFoundException e) {
            error = true;
        }
        Map<String, Object> result = new HashMap<>(1);
        result.put("error", error);
        WXBridgeManager.getInstance().callback(mWXSDKInstance.getInstanceId(), cb, result);
    }
}

自定义Component

weex目前只支持一些常用的组件,如果有需要,只要遵循weex规范,我们完全可以自定义组件。在配置weex客户端环境时,我们使用了第三方图片请求库Picasso使得imageview能够直接加载url。其实公司项目里已经有了强大的自造轮子——NetworkImageView,我们并不想引用其他库增加App size,但是template中无法直接使用Native组件,我们需要自定义Component进行简单的包装。例如:

@Component(lazyload = false)
public class NetworkImageViewComponent extends WXImage {

    public WeexComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) {
        super(instance, dom, parent, isLazy);
    }

    @Override
    protected void initView() {
        if (mContext != null) {
            mHost = new NetworkImageView(mContext); // 替换原生组件
            ((NetworkImageView) mHost).setScaleType(ImageView.ScaleType.CENTER_CROP);
        }
    }
 
    @Override
    public View getView() {
        return super.getView();
    }

    @WXComponentProp(name = "url")
    public void setImageUrl(String url) {
        ((NetworkImageView) mHost).setImage(url);
    }
}

如上代码所示,通常自定义Component只需要重写initView方法,替换mHost为所需的原生组件即可。使用@WXComponentProp注解可以为组件添加自定义属性,在template中声明组件时设置属性就可以调用对应方法。最后和Module类似,自定义Component也需要向weex注册,可以在Application启动时注册通用Component,也可以在需要时注册业务耦合较大的Component。

try {
    WXSDKEngine.registerComponent("myimageview", NetworkImageViewComponent.class);
} catch (WXException e) {
    e.printStackTrace();
}

经过以上操作我们已经可以在template中使用我们自定义的MyImageView了。简单修改下.we文件:

<template>
    <div style="flex-direction: column;">
        <slider class="slider" interval="2000" auto-play="true">
            <div class="slider-pages" repeat="{{headline}}" onclick="goWeexSite(headline[$index].url)">
                <MyImageView class="image" url="{{image}}"></MyImageView>
                <text class="title">{{title}}</text>
            </div>
            <indicator class="indicator" if="shouldShowIndicators()"></indicator>
        </slider>
    </div>
</template>

这里值得注意的是,.we文件中的MyImageView在通过weex自动转换工具转换成的js bundle文件中type被标识为"myimageview",也就是说会转换为全小写,因此在注册Component时应该尽量使用小写key。当然我们也可以手动修改js bundle文件,不过自定义组件多了会比较繁琐。

Native端渲染

客户端渲染工作主要包括解析js bundle文件还原Native端组件,至于数据请求可以直接在Native端发送网络请求,也可以在script中通过js调用weex内置的网络请求Module——WXStreamModule的sendHttp方法进行网络请求,请求到的数据会通过WXBridgeManager回调给js端。测试时为了方便,直接在Native端进行网络请求,然后将请求到的数据塞给weex,weex进行render渲染,渲染成功后更新listview,将最终得到的native view塞进listview。调用weex渲染的主要代码如下:

JSONObject json = new JSONObject();
json.put("headline", array);  //key和js端对应,相当于将数据塞进.we文件的data中
String template =  WXFileUtils.loadFileContent("weex/index.js", getContext());
if (weexLayout == null) {
    wxsdkInstance.render("headline", template, null, json.toString(), -1, -2, WXRenderStrategy.APPEND_ASYNC);
} else {
    wxsdkInstance.refreshInstance(json.toString());
}

测试时直接使用的客户端本地的由.we文件转换来的js bundle文件,生产环境通常应该从服务端拉取。需要注意的是,一个WXSDKInstance实例只负责一次页面渲染,如果想重新加载模版渲染页面,需要将WXSDKInstance实例destroy再重新new一个,如果后期只是更新数据直接refresh即可。另外,使用WXSDKInstance.registerRenderListener可以注册对渲染结果的监听。

@Override
public void onViewCreated(WXSDKInstance instance, View view) {
    weexLayout = view; // 首次渲染成功拿到native view
}

@Override
public void onRenderSuccess(WXSDKInstance instance, int width, int height) {
    adapter.notifyDataSetChanged(); // 更新listview
}

@Override
public void onRefreshSuccess(WXSDKInstance instance, int width, int height) {
    adapter.notifyDataSetChanged(); // 更新listview
}

@Override
public void onException(WXSDKInstance instance, String errCode, String msg) {
    Log.d("weex", "=======" + msg);
}

onViewCreated会在首次渲染成功后回调,拿到了native view,剩下的一切都非常熟悉了,放到你期望的容器中显示即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容