Weex尝鲜

WinfredTrae 4年前
   <p>Weex是阿里开源的类React Native技术,其实按知乎上的讨论基本可以说是整合Vue.js+React Native造的轮子( <a href="/misc/goto?guid=4959716521874749547" rel="nofollow,noindex">如何看待阿里无线前端发布的Weex?</a> )。Weex是一款轻量级的移动端跨平台动态性技术解决方案,主要致力于使用Web方式开发出Native性能的App。Weex学习成本较RN相对较低, 可以说就是使用简易版的HTML/CSS/JavaScript以及自定义的一些组件和规则开发.we文件,完成界面控件布局、样式、数据绑定以及简单的事件绑定等。Weex提供了node.js的小工具可以将.we文件转换成js bundle 文件,客户端引入了weex sdk就可以解析js bundle文件,最终完成页面渲染。接下来在简单尝试下weex。</p>    <h2><strong>weex配置</strong></h2>    <p>.we文件开发其实任何编辑器都可以,不过需要使用Node安装Weex提供的.we文件转换工具,该工具可以将.we文件转换成weex sdk能够识别和解析的js bundle文件。</p>    <pre>  npm install -g weex-toolkit</pre>    <p>主要介绍下Android端配置:</p>    <p>1、首先引入weex sdk,gradle添加weex依赖</p>    <pre>  compile 'com.taobao.android:weex_sdk:0.5.1@aar'</pre>    <p>2、确保声明了网络权限</p>    <pre>  <uses-permission android:name="android.permission.INTERNET" /></pre>    <p>3、配置ImageView加载网络图片形式</p>    <p>weex需要我们手动配置网络图片加载,否则imageview将无法正常工作。通常我们可以使用第三方图片加载库,这里我引入了Picasso来帮助我们加载网络图片。配置时机可以放在Application初始化中,这样全局有效。</p>    <pre>  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());  }</pre>    <p>通过以上配置,客户端基本的weex环境也就配置好了。</p>    <h2><strong>.we文件开发</strong></h2>    <p>.we文件主要包括三大部分, <template></template> 声明组件, <style></style> 定义组件样式, <script></script> 声明组件data、events,业务逻辑处理等,基本类似web端开发。template中可以使用{{}}进行data binding,将script中的data和events绑定到相应的组件中。具体语法不再介绍,下面是一个类似ViewPager的自动轮播banner例子。</p>    <pre>  <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></pre>    <p>style默认屏幕宽度为750px,所以如果组件宽度为整屏宽度,直接定义为750即可。绑定onclick事件,其实只是属性设置并不是方法调用,如果不带对应方法不带参数直接使用方法名即可,但是其他地方如果进行方法调用,必须得加(),表示方法的调用,例如 if="shouldShowIndicators()" 。</p>    <h2><strong>自定义Module</strong></h2>    <p>在script中我们引用了自定义的Module,负责与Native端通信,处理具体的业务逻辑。要引用自定义Module需要事先使用WXSDKEngine的registerModule方法进行注册,可以在Application启动时注册一些通用的Module,也可以在需要使用时再去注册一些具体业务逻辑Module。</p>    <pre>  try {      WXSDKEngine.registerModule("weexModule", WeexModule.class);  } catch (WXException e) {      e.printStackTrace();  }</pre>    <p>自定义Module时,方法访问权限必须声明为public,并且必须使用@WXModuleAnno注解标识。这里,weexModule是个简单的负责Activity跳转的Module,并且回调了Activity启动结果。对应的WeexModule代码如下:</p>    <pre>  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);      }  }</pre>    <h2><strong>自定义Component</strong></h2>    <p>weex目前只支持一些常用的组件,如果有需要,只要遵循weex规范,我们完全可以自定义组件。在配置weex客户端环境时,我们使用了第三方图片请求库Picasso使得imageview能够直接加载url。其实公司项目里已经有了强大的自造轮子——NetworkImageView,我们并不想引用其他库增加App size,但是template中无法直接使用Native组件,我们需要自定义Component进行简单的包装。例如:</p>    <pre>  @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);      }  }</pre>    <p>如上代码所示,通常自定义Component只需要重写initView方法,替换mHost为所需的原生组件即可。使用@WXComponentProp注解可以为组件添加自定义属性,在template中声明组件时设置属性就可以调用对应方法。最后和Module类似,自定义Component也需要向weex注册,可以在Application启动时注册通用Component,也可以在需要时注册业务耦合较大的Component。</p>    <pre>  try {      WXSDKEngine.registerComponent("myimageview", NetworkImageViewComponent.class);  } catch (WXException e) {      e.printStackTrace();  }</pre>    <p>经过以上操作我们已经可以在template中使用我们自定义的MyImageView了。简单修改下.we文件:</p>    <pre>  <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></pre>    <p>这里值得注意的是,.we文件中的MyImageView在通过weex自动转换工具转换成的js bundle文件中type被标识为"myimageview",也就是说会转换为全小写,因此在注册Component时应该尽量使用小写key。当然我们也可以手动修改js bundle文件,不过自定义组件多了会比较繁琐。</p>    <h2><strong>Native端渲染</strong></h2>    <p>客户端渲染工作主要包括解析js bundle文件还原Native端组件,至于数据请求可以直接在Native端发送网络请求,也可以在script中通过js调用weex内置的网络请求Module——WXStreamModule的sendHttp方法进行网络请求,请求到的数据会通过WXBridgeManager回调给js端。测试时为了方便,直接在Native端进行网络请求,然后将请求到的数据塞给weex,weex进行render渲染,渲染成功后更新listview,将最终得到的native view塞进listview。调用weex渲染的主要代码如下:</p>    <pre>  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());  }</pre>    <p>测试时直接使用的客户端本地的由.we文件转换来的js bundle文件,生产环境通常应该从服务端拉取。需要注意的是,一个WXSDKInstance实例只负责一次页面渲染,如果想重新加载模版渲染页面,需要将WXSDKInstance实例destroy再重新new一个,如果后期只是更新数据直接refresh即可。另外,使用WXSDKInstance.registerRenderListener可以注册对渲染结果的监听。</p>    <pre>  @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);  }</pre>    <p>onViewCreated会在首次渲染成功后回调,拿到了native view,剩下的一切都非常熟悉了,放到你期望的容器中显示即可。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/18fac5db8429</p>    <p> </p>