閱讀179 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Weex布局尺寸通用適配方案的研究

問題

weex為前端賦予了強大的跨端開發能力和較一致的良好的用戶體驗。weex一般是與native進行協作開發:

  1. 同app內不同頁麵用兩者分別開發,統一串聯
  2. 同頁麵兩者協作開發,比如native提供組件,weex使用

由於native布局一般采用定高不定寬的方式,以求不同尺寸的屏幕可以得到基本一致的布局體驗,然而weex布局采用基於750的等比例縮放,這種不一致導致的問題:

  1. 同一元素在不同屏幕的尺寸展現不一致,大屏手機尺寸偏大,小屏手機尺寸偏小
  2. 與native尺寸偏差較多

字號的差異也比較明顯。

默認依據ip6尺寸布局,慘不忍睹的現場直擊...

  • 紅框部分的滑動組件是native的,而背景容器是weex布局的,由於weex等比例縮放,而native使用pt,導致iphone 5s native組件溢出,而iphone 6p組件相比於容器過小。

數據流圖

  • 下麵是iphone 6p下native列表和weex列表字號對比

思考及分析

weex目前的尺寸處理方式:

無視屏幕分辨率和尺寸,虛擬寬度統一為750,布局尺寸以750為基準按比例縮放。

那麼使用weex原生尺寸定義想實現native的體驗“在不同尺寸手機上同樣元素的尺寸基本一致”是不太可能的。

不過weex在Js運行時提供了屏幕的物理分辨率和像素比例,那麼運行時可不可以用js動態算一下呢?

我們定義weex的原生尺寸為vp(virtual pixel),視覺稿的尺寸或native的尺寸為ns(native size)。

期待的轉換規則為

vp = translateRule(dp)

這樣用戶隻需要按native的方式寫布局尺寸,就可以映射為weex的尺寸,被weex布局邏輯動態解析,從而達到我們的目的。

既然我們要模擬Native的尺寸適配,首先要知己知彼,簡單了解了一下native的適配方法

安卓尺寸適配

安卓提供了一個萬能的適配單位:dp,dp屏蔽了屏幕大小和分辨率,提供了一致體驗,dp和物理像素的換算關係如下:

dp = px / (dpi / 160)

欲轉換先取dpi,可惜weex沒有dpi,但提供了一個比例scale,可用來計算dpi

scale = dpi / 160

換算可得

vp = dp * 750 / ( deviceWidth / scale )

IOS尺寸適配

類似dp的單位是著色器(似乎等同於pt?)

pt = px / scale

換算可得

vp = pt * 750 / ( deviceWidth / scale )

通過分析我們發現,安卓和IOS的適配規則殊途同歸,可統一轉換規則為:

    function translateRule(dp) {
        return dp * 750 / ( deviceWidth / scale );
    }

到這裏似乎大功告成,隻要在寫樣式時祭出我們的法寶:計算屬性,並動態賦到模板節點的style屬性上即可。然而這種方式讓開發同學想起了前端茹毛飲血的上古時代,而今前端顯然已經沐浴在現代化的春風裏很久了。如果在寫樣式的時候需要寫固定尺寸時,能像native一樣直接寫pt/dp這樣的單位那肯定是極好的。

掃了一下經過weex-loader編譯的代碼,與vue-loader編譯的區別主要是在style的處理上,前者將style編譯為map形式,並掛載到Vue實例上,所以我們在兩個位置可以拿到編譯後的style map

  1. vue實例生命周期內
  2. vue導出的組件對象上

由此有兩個插入自動處理尺寸邏輯的方案:

  1. 運行時。於上述2的位置,掃描vue實例的style map,過濾出自定義單位和值並替換為weex識別的尺寸
  2. 編譯時。通過webpack loader對vue文件進行預處理,掃描自定義單位並於1位置注入運行時處理尺寸的邏輯

分析

方案一:

  1. 性能較差,需要遍曆每個頁麵整個組件樹的style map
  2. vue實例拿到的style map,已被weex-loader過濾掉自定義單位,變成了人畜無害的數字。。。
  3. 需要js代碼處理,一看就不是weex的親兒子單位

結論:放棄

方案二:

  1. 每個組件單獨注入修改尺寸的代碼,免去了遍曆組件樹的開銷
  2. 在weex-loader的樣式分析之前解析,可以拿到原始樣式代碼
  3. 純編譯時純工具處理,使用自定義單位如同使用原生單位一樣方便,weex開發者毫無感知

結論:靠譜

實現

主流程

  • 識別style裏的自定義單位/函數,抽取單位前的數值
  • 根據規則生成尺寸處理邏輯的代碼
  • 注入到vue實例的beforeCreate函數裏

樣式識別支持less和css

單位一般用於單參數映射規則,多參數映射規則可寫成函數

規則文件

基於以下原因,需要獨立的、用戶自定義的、vue實例裏可以方便引用的規則文件

  • 可能需要多個單位,因此需要一個規則集合
  • 規則需要支持自定義,方便使用者修改和擴展
  • 用戶可能也需要手動調用某個單位的計算規則,比如實現動畫

loader實現內部提供了一個默認的規則文件,支持用戶自定義規則文件去覆蓋和擴展默認規則

默認規則

單位/函數 描述
dp 模擬安卓dp單位, 兼容ios,與native布局尺寸相同
mq media query
vw 相對於屏幕寬度的百分比
vh 相對於屏幕高度的百分比



數據流圖如下:

數據流圖

效果

無痛使用

    .class-name {
        /*
            自定義單位/處理函數dp, vw
            多個參數可用函數形式,單參數建議用單位
        */
        width: 100dp;
        height: 200vw;
        font-size: dp(24, 750); //可直接用24dp,這裏僅做例子
    }

UI效果

  • 不同尺寸屏幕native組件和weex容器完美契合(貼圖不是實際屏幕大小,所以5s布局尺寸看著比較大,僅做頁麵內native和weex的尺寸比較)

  • weex與native文字尺寸基本一致(由於weex安卓使用小數字號有問題,所以計算後的字號做了四舍五入取整,且字體也差異,所以效果還是稍微有點差異,但對比做適配前已經好很多了)

擴展

因為實現原理是編譯時解析自定義樣式函數,並插入運行時js代碼。因此除了用於計算尺寸外,還可以計算其他依賴環境(比如屏幕尺寸、設備類型等)的樣式,成為一個通用的weex css運行時處理工具。

當然更期待著weex原生支持各種布局單位,這才是康莊大道。

包地址:https://web.npm.alibaba-inc.com/package/@ali/weex-css-runtime-loader

最後更新:2017-07-19 20:03:08

  上一篇:go  記 node_modules 版本鎖定方案的落地
  下一篇:go  存儲--盤古,阿裏雲飛天分布式存儲係統設計深度解析