weex 实践(前端视角)

2,540 阅读7分钟

功能介绍

如上图所示,本次weex实践将订单展示页面进行了weex化,这个界面交互简单,用weex完全可以实现,同时也使用了weex提供的大部分基础能力。包括三端共同实现component、moduleshopBase、抽离we文件组件、a链接跳转、list组件使用、开发以及三端调试等。

源码管理以及web拓展

在weex里我们谈到组件component以及模块module跟一般的前端框架谈论的组件不太一样。比如在vue中我们会把一些比较通用的UI抽离出来单独引用,我们把这些抽离出来的逻辑UI对象称为组件component。但是weex需要保证三端一致,三端共同实现的公共逻辑UI我们称之为组件。为了保证三端一致,一样的效果需要在三端各自实现一个版本,然后通过weex提供的能力将组件各自注册进各自的sdk中。我姑且称这种类型的组件为下层组件(自创)

三端一同实现并且注册好下层组件之后,上层的.we中就能抽离不同的下层组件组合成新的组件进行复用,姑且称这类组件为上层组件。官方提供的上层组件

weex官方已经封装实现了大部分实用的下层组件,比如image/input/text等,但在业务中我们会需要定制一些component和module,在本次实践中,我们配合webpack单独管理weex源码weex-html5以及定制的component和module。

weex-extend目录

[weex-extend]
  | -- .babelrc
  | -- .eslintrc
  | -- .gitignore
  | -- LICENSE
  | -- README.md
  | -- [bin]
  |     | -- dist-browser.sh
  | -- [demo]
  |     | -- [build]
  |     |     | -- index.js
  |     |     | -- test.js
  |     | -- demo.png
  |     | -- index.we
  | -- [dist]
  |     | -- weex.js
  |     | -- weex.min.js
  | -- index.html
  | -- package.json
  | -- postcss.config.js
  | -- [src]
  |     | -- [components]
  |     |     | -- [a]
  |     |     |     | -- a.js
  |     | -- main.js
  |     | -- [modules]
  |     |     | -- [shop-base]
  |     |     |     | -- README.md
  |     |     |     | -- index.js
  |     |     |     | -- package.json
  |     |     |     | -- style.less
  |     |     | -- [shop-modal]
  |     |     |     | -- index.js
  |     |     |     | -- style.less
  | -- webpack.config.js
  | -- webpack.transform.config.js

其中main.js为主入口文件,用来注册components和modules。

// import the original weex-html5.
import weex from 'weex-html5'

import shopBase from './modules/shop-base/index.js';
import a from './components/a/a.js';
import shopModal from './modules/shop-modal/index.js';
// install the component.

// base module

weex.install(shopBase)

weex.install(a)

weex.install(shopModal)

[demo]在此处的作用是测试编写的component和module

在这个项目中就可以更新管理官方提供的weex-html5和定制的component。webpack打包生成的[dist]文件夹中weex.js和weex.min.js分别用在测试环境和生产环境。

注意0.4.1版本的weex-html5不包含组件,需要手动注册一下。

三端组件跳转约定

在官方提供的playground-demo中打开一个新页面提供了navigation模块的push方法,但是打开页面的url做了诸多判断,目的在于根据平台的不同对url设置不同的值,在H5端url对应的是页面地址,在native端则是weex页面对应的jsbundle文件。

如果在实际业务中也这样处理是不妥的,我们的方案是在.we文件中全部使用组件,且组件中的href的值都统一写成前端熟悉的页面地址。比如http://m.showjoy.com。对于前端而言,组件的逻辑就是在页面中生成一个a链接,点击跳转相应页面。而在native端,要运行weex是要执行对应的jsbundle文件的,所以在native端需要通过在线参数的方式维护一份配置文件。

[
   {

       "page":"order",

       "url":"https://xxx/order.js",

       "md5":"323827382huwhdjshdjs",

       "h5":"http://order.html"

       "v":"1.5.0"

    },
    {

       "page":"detail",

       "url":"https://xxx/detail.js",

       "md5":"323827382huwhdjshdjs",

       "h5":"http://detail.html"

       "v":"1.5.0"
    }
]

当在native的页面中请求到order.html页面时,native会检查本地是否有对应的jsbundle文件,有则使用,否则从json中对应的线上地址下载再进行渲染。

这样处理的好处显而易见,在编写业务代码的时候对于请求的新页面的书写方式与传统前端开发无异,另外在native维护这样一份数据还可以进行jsbundle热更新以及提前下载的作用。

测试环境与线上环境不同

在传统前端H5环境下不管a链接跳转还是api异步请求,我们一般都会用相对地址,浏览器会自动添加主域名信息,比如,最终的访问路径会是http://xxx.com/shop/commission或者测试环境下的http://xxx.net/shop/commission。但是在native中原生是没有这样的能力的。所以在编写业务时需要编写完整的地址。

开始我们的设想方案是在三端共同实现一个isOnline的module方法,前端在编写代码的时候根据这个方法返回的值动态设置com或者net,以此区分测试环境还是生产环境。

created: function () {
    let self = this;
    // shopBase 为定制的module
    shopBase.isOnline(function (json) {
        if (json.result) {
            self.suffix = '.com'
        }
    })
}

//template

这样是能完成需求的,但是在回调中得到想要的值总是会遇到不爽的地方,而且这样不好维护,每个页面都要有这样的逻辑,后来通过native端的配合,在native端也实现了填写相对地址就能访问对应地址的能力,完美的解决了这个问题。

自定义弹框

项目初期我们根据weex官网推荐的方式抽离成上层组件如何实现一个模态框,但是在部分低端安卓机上native的弹框表现比较糟糕,点击按钮需要等待大约0.7s左右弹框才会出现,用户等待感知时间太长。这是其一,其二是类似modal这样高频使用场景都要在页面中引入一个component太过麻烦。

// template

所以我们尝试使用module进行弹框的编写,三端各自实现一遍弹框。在之前提到的管理源码的项目里添加一个shop-modal项目,按照module教程编写好弹框逻辑,最后注册到weex-html5中即可。

'use strict'
import modal from 'modals'

// 因为是webpack参与构建,任何资源包括css都可以在此引用
require('./style.less');


const shopModal = {

  
  confirm: function (options, callbackId) {
    const sender = this.sender
    let cancelTitle = options.cancelTitle || '取消';
    let okTitle = options.okTitle || '确定';
    let message = options.message || '标题';
    
    // test
    
  },

  
  toast: function (options) {
    modal.toast(options.message, options.duration)
  }

}

const meta = {
  shopModal: [{
    name: 'confirm',
    args: ['object', 'function']
  }, {
    name: 'toast',
    args: ['object']
  }]
}

export default {
  init: function (Weex) {
    Weex.registerApiModule('shopModal', shopModal, meta)
  }
}

tab切换

官方抽离出了相应的tab切换组件wxc-tabbar,但在H5端这个组件的表现形式与需求并不太符合,wxc-tabbar组件点击不同的tab,并不会重新刷新页面,而是在当前页面又实例化了一个weex对象,对于我们的业务场景来说,如果点击了第二个tab,在第二个tab中点击了一个a链接,返回后又会重新进入到第一个tab的界面。

我们想要的效果是点击两个tab,在H5端的展现形式跟点击两个a链接跳转一样,而在native端来回点击两个tab,不会重复渲染页面。针对这种业务场景,三端协定在基础module中实现一个loadUrl方法,H5端的实现方法就是跳转一个urlwindow.location.href = xxx,native端的逻辑在native端自行实现。

H5端获取服务端同步渲染的数据

因为历史原因,这个订单管理页面需要用到服务端同步渲染的一个数据字段,但是在weex中无法取到真实dom,即使通过在weex中手动判断web环境,也取不到document.querySelector方法。

console.log(document.querySelector)  // undefined
window.weex.init({
    jsonpCallback: JSONP_CALLBACK_NAME,
    appId: location.href,
    loader: loader,
    source: page,
    rootId: 'weex',
    // downgrade: ['root']  // 'root', 'list', 'scroller'
  })

在初始化weex实例的传参中也没有找到可以手动传入数据的入口,所以这里暂时的解决方案是在三端协议的基础moduleshopBase中实现一个getElement方法,这样就可以在we文件中取到web环境中的真实dom。

getElement: function (hook, callbackId) {
    const sender = this.sender;
    var el = document.querySelector(hook);
    sender.performCallback(callbackId, {
      el: el
    })
},

we文件中

shopBase.getElement('.j_IsOpenShop', function (json) {
    if (json.el.value === 'true') {
      self.isVisitor = false;
    }
    cb();
  })

html中的同步数据

native调试

weex官方提供了weex-devtool,其中weex bebug xx.we 命令可以将we文件编译成bundle并开启调试,这里要注意的是此处的编译是官方提供的,而很多情况下我们需要自己自定义webpack的编译方式,所以此处调试可以直接使用编译完的bundle文件。weex debug xx.js。若想在自己的app中调试weex页面,需要在ios和andriod app内编写相关代码,提供调试的支持。ios集成调试

文章中代码没有贴全,有兴趣的以及对native部分实现代码有兴趣的可以联系我微博达达的暹罗猫


本文对你有帮助?欢迎扫码加入前端学习小组微信群: