WEEX-EROS | 集成並使用 bindingx

文章出處: bmfe.github.io/eros-docs/#…前端

前言

上篇文章 WEEX-EROS | 入門指南 中咱們已經從頭運行了 eros 來進行開發 app,不少同窗還不是很清楚 eros 到底能幹嗎,這裏在稍微解釋一下:vue

eros 是基於 weex 的二次封裝 app 開發解決方案,讓咱們能用 vue 語法來開發原生 iOS/Android 應用ios

eros 已經幫助了數家公司的前端開發者開發了本身的原生 iOS/Android 應用,並經過 weex 使其 app 具備 熱發佈 的能力,在 4 月份,eros 會發布插件系統,模擬器/真機熱刷新,全新官方 demo ,更全面的熱更新邏輯等,正式穩定下來。git

這篇文章中咱們主要來介紹:github

  • eros 中如何使用 weex-bindingx
  • 其餘 weex 純 native 項目中自行封裝 weex-bindingx 來優化 JS Bundle 大小

bindingx 由來

引用官方文檔上的介紹:express

  • 因爲 weex 底層使用的 JS-Native Bridge 具備自然的異步特性,這使得 JS 和 Native 之間的通訊會有固定的性能損耗,所以在一些複雜的交互場景中,JS 代碼很難以高幀率運行。舉個例子,若是咱們要實現 視圖隨手勢移動 的效果,那麼按照傳統的方式,須要在這個視圖上綁定 touch 或者 pan 事件,當手勢發生時, Native 會將手勢事件經過 Bridge 傳遞給 JS , 這產生了一次 Native 到 JS 的通訊。而 JS 在接收到事件後,須要根據手指移動的偏移量驅動界面變化,這又會產生一次 JS 到 Native 的通訊。與此同時,手勢回調事件觸發的頻率是很是高的,頻繁的通訊帶來的時間成本極可能致使界面沒法在16ms中完成繪製,進而產生卡頓npm

  • 事實上,不只僅是在 weex 上存在這種問題, React Native 等框架一樣存在相似的問題。拿 React Native Animated 組件爲例,爲了實現流暢的動畫效果,這個組件採用了聲明式的API,在 JS 端僅僅定義了輸入與輸出以及具體的 transform 行爲,而真正的動畫是經過 Native Driver 在 Native 層執行,這樣就避免了頻繁的通訊。然而,這個方案只能解決一部分問題,若是是有複雜交互操做的場景就不夠用了。另外,聲明式的方式可以定義的行爲很是有限,沒法知足更復雜的交互場景。bash

使用以前

請熟讀官方文檔:weex

weex native 項目的使用過程當中須要注意:app

1.傳入的元素須要多取一層 ref 屬性,假設咱們給一個元素上面 ref 屬性賦值爲 box ,則使用時候按照如下方式纔可:

this.$refs.box.ref
複製代碼

2.若是引用方式爲 requireModule('bindingx') 這種 weex 引入 module 的寫法,bind 方法按照文檔中的方式是無效的,須要作特殊處理,後面會說到。

3.ios 端引入的時候須要作下頁面手勢返回處理 (eros 中拓展了$router gesBack 屬性),防止右滑時候出現手勢衝突

eros 中使用

若是下載官方的 npm 包 weex-bindingx 直接使用時沒有問題的,但 eros 是主要 focus native,引入 weex-bindingx 在打出來一個簡單的 JS Bundle 就要 190+ kb,很明顯有些過於臃腫,在交互複雜的頁面,打出來的 JS Bundle 的大小就更加不可控。

進入 weex-bindingx 源碼發現,若是 native 直接使用 requireModule('bindingx') 引入,是須要改變 expression 爲對象,把填寫的表達式值傳入對象的 origin 屬性,而後傳入一個的 transformed 屬性,這個屬性是很長的 CallNative 指令字符串,能夠經過下載 npm 包 bindindx-parser 來自動生成。

因而 eros 作了很簡單的二次封裝,集成進 widget 中,直接經過 this 調用便可。

下面咱們來實現一個官方的 demo:

首先編寫模板和樣式:

<template>
  <div class="container" >
    <div class="border">
      <div :ref="'my'" class="box" @touchstart="ontouchstart">
        <div class="head">
          <div class="avatar"></div>
          <text class="username">HACKER</text>
        </div>
        <div class="content">
          <text class="desc">Google announced a new version of Nearby Connections for fully offline.high bandwidth peer to peer device communications.</text>
        </div>
        <div class="footer">
          <text class="action">SHARE</text>
          <text class="action" style="color:#7C4DFF">EXPLORE</text>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
  .container {
    flex: 1;
    background-color:#eeeeee;
    
  }
  .border{
    height:1000px;
    padding-left:35px;
    padding-right:35px;
    padding-top:100px;
  }
  .box {
    width: 680px;
    height: 450px;
    background-color:#651FFF;
  }
  .head {
    background-color:#651FFF;
    width:680px;
    height:120px;
    flex-direction:row;
    align-items:center;
  }
  .content{
    width:680px;
    height:240px;
    background-color:#651FFF;
    padding-left:24px;
    padding-top:24px;
    padding-right:24px;
  }
  .footer {
    width:680px;
    height:90px;
    background-color: #fff;
    align-items:center;
    justify-content:flex-end;
    padding-right:25px;
    flex-direction:row
  }
  .action {
    font-size:35;
    padding-right:20px;
  }
  
  .desc {
    font-size:32;
    color:#fff;
    padding-left:24px;
  }
  
  
  .avatar {
    width:96px;
    height:96px;
    border-radius:48px;
    background-color:#CDDC39;
    margin-left:36px;
    margin-right:48px;
  }
  
  .username {
    color:#fff;
    font-size:32;
  }
  
</style>

複製代碼

編寫 js 邏輯:

<script>
  export default {
    data () {
      return {
        x: 0,
        isInAnimation: false,
        opacity:1,
        gesToken:0
      }
    },
    methods: {
      ontouchstart (event) {
        // 若是在執行動畫,就不觸發
        if(this.isInAnimation) return 
        // 解綁動畫
        if(this.gesToken != 0) {
          this.$bindingx.unbind({
            eventType:'pan',
            token:this.gesToken
          })
          this.gesToken = 0
          // return
        }
        // 找到元素 注意多了一個.ref
        let boxRef = this.$refs.box.ref
        let gesTokenObj = this.$bindingx.bind({
          anchor:boxRef,
          eventType:'pan',
          props: [
              {element:boxRef, property:'transform.translateX',expression:"x+0"}, // 這裏是表達式,須要有運算符號,不能只寫x
              {element:boxRef, property:'opacity',expression: "1-abs(x)/600"}]   // 咱們這裏指望 opacity 不管在拖動盒子向左向右的時候 都有漸隱 全部取絕對值 移動到絕對 600px 時候爲全隱
        }, (e) => {
          if(e.state === 'end') {
            // 拖動結束事件 記錄當前座標 和 透明度
             this.x += e.deltaX
             this.opacity = 1-Math.abs(e.deltaX)/600
            //  開始進行下一步 回彈仍是滑出的 動畫
             this.bindTiming()
          }
        })
        // 記錄下取消的token 屢次 ontouchstart 時要帶着 token 把上次的解綁
        this.gesToken = gesTokenObj.token
      },
      dismissCallback() {
        this.$notice.toast({
            message: '您已經刪除了小卡片。'
        })
      },
      bindTiming() {
        // 開始執行動畫
        this.isInAnimation = true

	      let boxRef = this.$refs.box.ref
        // 改變的 x 座標,最終的 x 座標,最終的透明值,位移 x 原點的表達式
        let changed_x, final_x, final_opacity, translate_x_origin
        // 經過一個變量來判斷是否已經滑出
        let shouldDismiss = false

        // 生成表達式邏輯
        if(this.x>=-750/2 && this.x<=750/2) {
        // weex 設置寬度默認都是750px 往左拖動或者往右拖動盒子一半之內時
          shouldDismiss = false   // 標記爲不消失
          final_x = 0             // 回到原點
          changed_x = 0-this.x    // 計算出須要位置的值
          final_opacity = 1       // 透明度變回 1
          translate_x_origin = `easeOutElastic(t,${this.x},${changed_x},1000)` // 運動曲線爲easeOutElastic 生成位移到原點的表達式 1s內執行
        } else if(this.x < -750/2) {
          // 往左拖動盒子超過一半時
          shouldDismiss = true   // 標記爲消失
          final_x = -750         // 徹底把盒子位移到左邊屏外面
          changed_x = -750-this.x// 計算出須要位置的值
          final_opacity = 0     // 透明度變回 0
          translate_x_origin = `easeOutExpo(t,${this.x},${changed_x},1000)` // 運動曲線爲easeOutExpo 生成位移到 -750px 表達式 1s內執行
        } else {
          // 往右拖動盒子超過一半時
          shouldDismiss = true   // 標記爲消失
          final_x = 750          // 徹底把盒子位移到右邊邊屏外面
          changed_x = 750-this.x // 計算出須要位置的值
          final_opacity = 0      // 透明度變回 0
          translate_x_origin = `easeOutExpo(t,${this.x},${changed_x},1000)` // 運動曲線爲easeOutExpo 生成位移到 750px 表達式 1s內執行
        }
       
      //  運動曲線爲linear 計算出透明度表達式 1s內執行
       let opacity_origin = `linear(t,${this.opacity},${final_opacity - this.opacity},1000)`
	     let result = this.$bindingx.bind({
          eventType:'timing',       // 結束的時候是沒有任何監聽的 用 timing 來作定時的動畫
          exitExpression:"t>1000",  // 當時間超過 10000ms 結束動畫
          props: [
              {element:boxRef, property:'transform.translateX',expression:translate_x_origin},
              {element:boxRef, property:'opacity',expression:opacity_origin}
            ]
          
        },(e) => {
            if(e.state === 'end' || e.state === 'exit') {
              // reset x
              this.x = final_x
              this.opacity= final_opacity
              this.isInAnimation = false
              
              shouldDismiss && this.dismissCallback()
                
            }
        })
      }
    }
  }
</script>
複製代碼

能夠看到,只須要調用 $bindingx 便可。

weex native 中使用

在非 eros 的 weex native 項目中,咱們能夠重寫 bind 方法來保持與官方使用一致,下載 npm 包, lodashbindingx-parser 來進行改造:

// bindingx.js
import { parse } from 'bindingx-parser'
import _cloneDeep from 'lodash/cloneDeep'

const WeexBinding = weex.requireModule('bindingx')
const BindingxFunction = WeexBinding.bind

let _WeexBinding = _cloneDeep(WeexBinding)

// 重寫 bind 方法
_WeexBinding.bind = (options, callback) => {
    if (!options) {
        throw new Error('should pass options for binding')
    }

    options.exitExpression = formatExpression(options.exitExpression)

    if (options.props) {
        options.props.forEach((prop) => {
            prop.expression = formatExpression(prop.expression)
        })
    }

    return BindingxFunction(options, options && options.eventType === 'timing' ? fixCallback(callback) : callback)
}

module.export =  _WeexBinding
複製代碼

在業務中使用:

var bindingx = require('bindingx')
複製代碼

這樣在打包以後非壓縮狀態下體積能減小到 34kb 左右。而 eros js bundle 的大小會更小,已經把這部分重寫邏輯放入了 widget,經過 appboard.js 來內置到客戶端執行。

如今,盡情使用 bindingx 吧!

相關文章

相關文章
相關標籤/搜索