Weex 理解和記錄 - Web + iOS

又一個坑~~

最近研究了下React Native 與 Weex , 整理了份對比~ 最終選擇的阿里霸霸的Weex!

本文主要是記錄和分享我在實際開發Weex項目中的所見所學(2018年初), 僅是我個人的理解, 并是一定是完全正確的, 也希望有大神指點學習??

已經2019年啦,目前Weex已經更新到1.3.11, WeexSDK目前更新到0.20.1 ,Weex已不再屬于阿里, 貢獻給Apache去迭代維護了,最新Weex官網, 變化不小,依然在努力。Weex的開發可以說每個人有每個人的理解,項目的場景和開發的習慣也不一樣,以至于很多個人或者團隊在Weex基礎上又開源了很多不同的分支,比如Weex Plus、Weex Box、WEIUI等等,這些可以幫助開發者省去很多時間去理解和填坑,不過有利也有弊嘛。總之Weex的原理依然沒有變。

Weex項目界面展示:

涉及: 圖表, 列表, 菜單選擇
涉及: 圖片上傳, 表單提交
我對Weex的理解:

通過Weex,可以寫一套Vue代碼渲染在Native和Web兩端, 可以熱更新. 在Native端的效果近似原生. 可以這樣理解, 在Weex項目中, 你寫的Vue文件最終會編譯成兩種類型的文件夾,
1.通過啟動項目的命令: npm run serve (或者通過打包命令 npm run build:native), 可以時時編譯到項目目錄下的dist文件夾內 , 目錄結構跟src目錄下的結構一致, 會把.Vue文件編譯成.js文件這些.js文件是Native端Weex SDK加載所需要的文件, 每個Vue文件對應一個.js文件, 所以把這些js文件放在服務器上, Native端加載js地址就可以了, 達到熱更新效果~
2.通過命令: npm run build:prod 可以打包生成release文件夾, 該文件夾內是web環境訪問所需的.html文件, 每個Vue文件對應一個.html文件, 瀏覽器打開就是你寫的對應頁面了

Weex SDK是Native端所需要集成的, 用于將.js文件渲染到Native端的手機上, 官方Demo可以通過 weex add platform ios || weex add platform android 去下載, 看下代碼不難發現, SDK其實就是將.js文件(路徑)渲染成一個View, 然后放到當前視圖控制器中, Demo中有提供這個Controller, 接收一個Url, 這個Url可以是網絡的js路徑,也可以是本地的js路徑.所以關于頁面適配, 其實只要Native端適配當前視圖控制器的View就可以了

雖說支持Vue語法, 但在實際開發的過程中, 其實并不像開發Vue項目那樣隨意, 區別和限制要求還是挺多的. 說下最典型的頁面調整路由吧, Vue屬于單頁應用, 通過Router來管理頁面, 怎么跳轉都是一個頁面. 而Weex并不是單頁應用, 可以理解成Weex每個頁面都是一個新初始化的Vue頁面. 所以目前我還不知道Weex如何配置全局變量~

Weex比較適合做一些列表信息展示, 如果經常封裝組件的話, 開發起來會很快, 這是Weex的優勢, 但涉及到比較復雜的功能(地圖, 拍照攝像等)就需要費點心思了, 甚至需要Native端去支持, Weex提供了Module, Component 等橋梁, 可通過Weex向Native傳值和回調, Native可以注冊原生Component給Weex使用, 你也可以使用<Web>標簽來加載html用來顯示比較復雜的圖表功能(如上面雷達圖)

在開發人員上面, 雖說是Web開發, 但是我覺得最好有 iOS 或 Android 開發經驗, 這樣的話理解Weex運轉起來會非常得心應手, 也可以讓公司的移動端開發小伙伴一起參與Weex的學習~ , Weex不像React Native那樣有非常多的組件, 很多參考資料, 活躍的社區. Weex實際開發中所遇到的問題基本需要自己去思考, 官網上Weex的組件大多數都是很久之前的代碼了, 其實原理就是Native端寫好提供的, 跟你注冊Module給Native端他們實現是一樣的, 所以在選擇上面請多考慮. 這里推薦 Weex-Ui . 釘釘也有Weex-Ui官方群提供交流和學習!

Weex-Ui官方釘釘群

Weex Apache 官方社區交流釘釘群

如何創建工程請仔細閱讀Weex官網要好好看看哦

下面就來細說下工程里的一些內容了

搜索關鍵詞:
工程運行
全局方法的配置
頁面跳轉
降級方案
網絡請求stream
圖片加載
關于獲取標簽對象
動態修改標簽樣式
自定義字體樣式
跨域相關
打包部署
交互相關
JS緩存機制
CSS樣式不支持記錄
標簽樣式不支持記錄
點擊事件失效
<list>列表中的<input>顯示錯誤
運行 weex add platform ios 出現錯誤

工程運行

weex create xxxx 后先看下最新的效果吧~

cd 項目目錄下 npm start啟動項目,瀏覽器中可以看到:

Hello

可以看到左側是內容在手機中顯示的樣式, 右側二維碼是提供weex官方軟件"weexplayground"(各大應用市場下載)掃碼在手機中查看的 (需要連接同一個無線網哦~,因為服務是部署在本地的,只有同一ip地址才能訪問), 所以網上的weex Demo基本都有二維碼~ 這樣可以在真機上查看效果.(在實際開發中我基本不用官方APP去看在手機上的效果, 因為開發過程我需要跟Native端交互,會用到Module, 而官方APP中是不會注冊這個Module 的, 所以看不到效果. 我在開發中首先是看在瀏覽器的效果, 瀏覽器中沒有問題我再打包在手機上看效果)

可以說preview.html其實就是用來看下在手機尺寸中展示的效果的, 在項目中web/preview.html中可以找到, 這里是Web環境并非Native環境呦,

<iframe id="preview" src="/" frameborder="0"></iframe>

還記得之前地址的后綴么?page=index.js這個index.js就是src下對應index.vue編譯出的結果,所以這個加載的結果就是src目錄下index.vue中的內容~ 如果需要查看其它頁面樣式直接改成src目錄下的相對地址就可以了~

全局方法的配置

我目前的處理方式是把所有全局方法寫在一個.js文件里,其他頁面手動導入這個js文件中的方法

// src/mixins/index.js
export default {
  methods: {
     $push () {...}
  }
}

其他頁面引入這個js文件中的方法:

import globalMethods from '../mixins/index.js'
export default {
  mixins: [globalMethods],
  created () {
  }
}

頁面跳轉

在Weex中使用Vue 這篇文章中有介紹如何使用和使用注意事項, 需要好好看一下!
其中關于使用 Vuex 和 vue-router ,感覺官方其實并不推薦使用vue-router了, 更推薦使用 navigator 來管理頁面實例

按官方的例子使用navigation進行跳轉

methods: {
      jump (e) {
        navigator.push({
          url: '/view_one/index_two',
          animated: 'true'
        })
      }
    }

發現在Web端url是可以寫相對地址的,跳轉沒有問題.
但是在Native端,頁面加載是失敗的,應該是因為Native端加載的是js文件, 所以Native端url需要寫完整的js路徑

這里重點說下我在跳轉方面的處理

曾經iOS , WeexSDK是可以看到源碼的, 現在不行了哈, 我看了下navigator的寫法,其實就是使用系統的跳轉, 把傳過來的Url給Controller去渲染,這樣的話Native端當然需要完整的js路徑咯, 所以我建議原生上面的跳轉最好自定義, 這樣的話導航欄的隱藏/顯示/標題等等都可以自己控制, 很簡單, 注冊個Module, 定義一個方法就可以了

// iOS代碼
@implementation WXNavigationModule

WX_EXPORT_METHOD_SYNC(@selector(push:))
WX_EXPORT_METHOD_SYNC(@selector(pop))

/**
 頁面跳轉
 
 @param params 參數
 */
- (void)push:(NSDictionary *)params {
    if ([params isKindOfClass:[NSDictionary class]]) {
        NSString *h5Url = params[@"h5Url"];
        NSString *fallbackUrl = params[@"fallbackUrl"];
        [BLRouterService.shared gotoWeexWithH5Url:h5Url fallbackUrl:fallbackUrl barHidden:[params[@"barHidden"] boolValue]];
    }
}

/**
 頁面返回
 */
- (void)pop {
    dispatch_async(dispatch_get_main_queue(), ^{
        [BLRouterService.shared.navigationController popViewControllerAnimated:true];
    });
}

Web環境我依然使用Weex自帶的navigator, 所以我根據平臺做了區分:

// js 代碼
// push操作 barHidden代表是否隱藏導航欄 默認隱藏
    $push (path, barHidden) {
      if (this.$isNativePlateform() && weex.supports && weex.supports('@module/navigation.push')) {
        weex.requireModule('navigation').push({
          h5Url: this.$getPushPath(true, path),
          fallbackUrl: this.$getPushPath(false, path),
          barHidden: barHidden,
          animated: true
        })
      } else {
        weex.requireModule('navigator').push({
          url: this.$getPushPath(false, path),
          animated: 'true'
        })
      }
    },
  /**
     * 是否native
     */
    $isNativePlateform () {
      var platformType = this.$getPlatformType()
      return (platformType === 'android' || platformType === 'iOS')
    },
    // 獲取平臺類型
    $getPlatformType () {
      if (weex.config.env.platform === 'iOS') {
        return 'iOS'
      } else if (weex.config.env.platform === 'android') {
        return 'android'
      } else if (weex.config.env.platform === 'Web') {
        return 'web'
      } else {
        return 'undefine'
      }
    },

還有一點需要注意的就是這個跳轉路徑的拼接:

  1. Native端加載的是.js文件, 這個文件在本地的dist目錄下, 相對views的文件
  2. Web端加載的是.html文件 本地測試的時候,通過控制臺的接口請求可以看到加載的其實是頁面相對views的路徑.html
http://192.168.1.49:12581/views/etc/home.html

所以使用跳轉的時候傳的path最好是相對views文件的相對路徑,比如這樣

 this.$push('views/order/order-detail?applyId=' + this.dataItem.applyId)

這樣的話拿到這個path后處理下路徑和參數, 通過平臺判斷否拼上/dist, 末尾是.js 還是 .html 等, 就這可以完美在Web和Native端跳轉了

降級方案

所謂的降級方案可以理解成如果發現Native端的SDK無法展示或者展示出現問題, 則使用Web去展示. 也就是說當Native端SDK在渲染頁面時發生了錯誤回調, 我們就需要寫一個WebView去展示該頁面對應的H5地址了, WebView加載的可不是js了, 而是頁面對應的.html (Weex寫出的頁面打包好后,最終會在release文件夾內生成對應的.html文件用于Web端展示,加載的地址可以由Web傳給Native端記錄下來, 也可以根據服務端部署的位置自己調整).
iOS的Native端錯誤回調如下:

    _instance.onFailed = ^(NSError *error) {
#if DEBUG
        if ([[error domain] isEqualToString:@"1"]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSMutableString *errMsg=[NSMutableString new];
                [errMsg appendFormat:@"ErrorType:%@\n",[error domain]];
                [errMsg appendFormat:@"ErrorCode:%ld\n",(long)[error code]];
                [errMsg appendFormat:@"ErrorInfo:%@\n", [error userInfo]];
                
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"render failed" message:errMsg delegate:weakSelf cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
                [alertView show];
            });
        }
#endif
    };

也就是說這里我們需要手動將創建一個WebView加載到當前控制器去展示了,Weex渲染的View就可以移除了.

請注意: 降級情況下雖然也在Native端, 但這時候注冊Module 不會再起作用了,
這時候想要交互就屬于純html與WebView的交互了, Native端去給WebView注冊JS吧

通過版本號手動降級控制:

官方推薦使用模塊降級, 也就是說用第三方plugin去判斷.
群里的大神推薦使用webpack-plugin-downgrade 這個插件,
按照它的意思我在weex/configs/webpack.common.conf.js 這里添加plugin 并使用, 請注意 new DowngradePlugin({}) 一定要放在第一位,不然會報錯

const plugins = [
  new DowngradePlugin({
    condition: {
      // Any condition is matched will be downgraded.
      ios: {
        osVersion: '>1.0',
        appVersion: '>1.0.0',
        weexVersion: '>1',
        deviceModel: ['iPhone5,1']
      },
      android: {
        osVersion: '>1.0',
        // Check condition with multiple app.
        // The `MY_APP_A` and `MY_APP_B` is WXEnvironment's appName param.
        appVersion: {
          MY_APP_A: '>1.0.0',
          MY_APP_B: '>3.0.0'
        },
        weexVersion: '>1',
        deviceModel: ['G-2PW2100']
      }
    }
  }),
  /*
   * Plugin: BannerPlugin
   * Description: Adds a banner to the top of each generated chunk.
   * See: https://webpack.js.org/plugins/banner-plugin/
   */
  new webpack.BannerPlugin({
    banner: '// { "framework": "Vue"} \n',
    raw: true,
    exclude: 'Vue'
  })
]

OK, 只要滿足condition中的判斷條件, 當加載頁面時就會對應回調_instance.onFailed失敗方法~

iOS中設置appName 和 appVersion 很方便 官網說的很清楚~
Android比較"耐人尋味", 最終在群里詢問大神找到了答案:


Android配置

網絡請求stream

現在stream并不屬于weex自帶的網絡請求庫了, 屬于一種plugin, 在entry.js中的注釋部分有說明,所以需要 npm i weex-vue-stream --save , 然后在entry.js中導入和添加:

import stream from 'weex-vue-stream'
// install the plugins.
weex.install(stream)

圖片加載

方法應該有很多,網上也有一些
《Weex 加載 .xcassets 中的圖片資源》
《可能是史上最全的weex踩坑攻略》- 故事六: 圖片加載
《weex android iOS 加載本地圖片》

官方推薦的圖片加載的方法WXImgLoaderDefaultImpl ,這里如果要替換SDWebImage版本或者方法請注意返回的對象要實現<WXImageOperationProtocol>方法, 不然會閃退, 具體可參考文章之前遇到的問題即解決.

因為要考慮到三端統一與熱更新, 我就把所以圖片放在服務上了 ,這就不存在加載iOS / Android 本地的圖片了, 所以圖片的路徑也要是完整的路徑 (所有圖片路徑我也得做處理) ,當然這樣做也有不好的地方, 加載慢什么的, 因為Native端會做圖片緩存, 所以就沒怎么考慮了

關于獲取標簽對象

官方推薦使用ref

<div ref='container'></div>

const dom = weex.requireModule('dom')
  mounted () {
    this.$nextTick(() => {
      var _this = this
      setTimeout(function () {
        dom.getComponentRect(_this.$refs.container, containerOption => {
            // getComponentRect 官方使用時沒有加延遲 但這個方法有時候我發現獲取的size都是0,所以遇到這樣的情況可以加個延遲,
        })
      }, 300)
    })
  },

動態修改標簽樣式

網上找的一種方法~

const animation = weex.requireModule('animation')
mounted () {
  this.$nextTick(() => {
    var _target = this.$refs.target
    animation.transition(_target, {
      styles: {
        backgroundColor: 'red'
      },
      duration: 0, // 變換用時,可設置為0
      timingFunction: 'ease', // 變換的動畫,同理css3變換
      delay: 0 // 延時執行
    })
  })
}

如果需要重新渲染,我使用的方法是vue動態style

v-bind:style="{ height: viewHeight + 'px'}"

animation動態修改標簽樣式在手機上不支持修改坐標(fixed: top,left,right,bottom) ,需要使用:style, 而且dom.getComponentRect這個方法寫在Created() 方法里在手機上獲取不到dom, 需要價格setTimeout延遲獲取

自定義字體樣式

字體具體我沒實現過,群里朋友分享的


官方addRule介紹

跨域相關

weex默認是支持跨域的 —— 可參見這篇文章
但是在電腦瀏覽器上會出現跨域問題,在手機或者模擬器上不會~
如果想在電腦瀏覽器中實現跨域方法,可以用 weex工程自帶的跨域插件去設置~
在config.js 中 添加如下配置, 重啟服務即可降/api自動代理成http://testapi.demo.cn

proxyTable: {
      '/api': {
        target: 'http://testapi.demo.cn',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    },

打包部署

打包Web頁面:
npm run build:prod 是打包到生產環境的命令, 這里需要注意下,默認是會產生巨大的.map文件的, 一般部署是不需要的,所以我們需要配置下打包時不編譯.map文件

config.js

webpack.prod.conf.js

其實也就是在配置文件中搜索下map關鍵字 對應的看下是否是相關控制,然后取消調就可以了

成功打包后會在目錄下生成release文件:


release

可以看到對應的vue文件最終會生成一個html, 我們只要訪問這個html就可以看到內容了, 所以部署的時候只要將release/web文件夾部署到服務器上即可~

打包Native頁面:
npm run build:native后會在根目錄下生成dist文件, 這個文件中會對應生成.js文件,一般將整個dist目錄放在服務器中,Native端加載即可

打包圖片:
哈哈,目前還不知道圖片如何打包, 只是把圖片手動拖到服務器中, 還在研究...

交互相關

Weex 到 Weex 之間:
如果是父子頁面關系: emit(),refs
如果是跨頁面: BroadcastChannel

Weex 到 Native:
Module

Native 到 Weex:
在頁面加載時通過Native端render渲染時傳入option參數, 或者globalEvent 還可以由Weex發起然后使用CallBack回調

下面主要記錄下Weex中Web展示頁面和Weex原生展示的頁面之間的交互,怎么理解呢,場景如下:

因為要顯示echart表,所以在native端我是這樣處理的, 我單獨用HTML寫了一個展示echart頁面, 這個頁面就是web環境了, 我在其他頁面用<web>去加載這個HTML, 現在遇到的問題就是傳參和參數監聽.而目前唯一的橋梁就是<web>展示的src的URL, 我通過使用錨點http://baidu.com#params進行參數傳遞并且不會刷新<web>, 這個方法在iOS是沒有問題的,但是在Android上遇到了修改錨點<web>依然被刷新(因為src檢測到URL變化了).
解決方案: Android去單獨判斷, 加載完URL后,這樣改變url:

<web src="javascript:window.changeHighLightText('xxx')">

然后在對應的HTML頁面用window注入這個方法,即可達到傳值效果.bingo~

javascript: 的方法在很多瀏覽器中都被禁止, 因為涉及到安全性,但是在Android上目前依然是可行的, 沒辦法的辦法了, 但這種方法并不能保證所有Android都使用, 也許有些系統會禁止這種方法~ (iOS使用這種是行不通的)

postMessage 向當前 Web 頁面發送數據 不過該方法目前僅支持Android

這里有個方法可以參考 ,通過<web>加載的h5頁面向weex頁面發送消息

JS緩存機制

參閱該文章

CSS樣式不支持記錄:

1.padding margin 不支持縮寫 ,類似這種,

padding: 0 20px 30px 0;

但是如果四個值一樣可以,類似這種

margin: 10px;

2.不支持百分比

3.display布局僅支持relative | absolute | fixed | sticky 可參考這偏文章

  1. border 樣式差異官方說明
border 目前不支持類似這樣 border: 1 solid #ff0000; 的組合寫法。

5.不支持display:none, 可以使用 v-if 代替,或者用 opacity:0; 來模擬
值得注意的是,當opacity的只小于等于0.01時,native控件便會消失,但視圖會被渲染,占位空間還在,但用戶無法進行交互操作,點擊時會發生點透效果。

6.陰影(box-shadow), 僅支持iOS 不支持Android (吐血...)

標簽樣式不支持記錄

  1. 支持 v-if , 不支持 v-show , 有些標簽不支持 v-else v-else-if (比如text標簽不支持,但div標簽支持)

點擊事件失效

官方明確了一些不支持@click='function()'方式的標簽

自定義的組件需要

<customComponent @click.native='customClick()'></customComponent>

<list>列表中的<input>顯示錯誤

在<list>列表里, 如果<cell>標簽中存在<input>輸入框, 當列表滾動時,如果<input>框滑出列表再滑回來會出現無法看到值的情況, 這是<list>列表復用時產生的Bug, 解決方法: 下面的評論中我也有給出我的建議

運行 weex add platform ios 出現錯誤

error: unknown option `--telemetry'

請參考這篇文章評論

繼續更新....

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容