Weex原理之帶你去蹲坑

戀貓de小郭發表於2019-03-04

 本篇將節操滿滿的安利Weex(˶‾᷄ ⁻̫ ‾᷅˵),不一樣的角度推薦你入坑,官網有的我們不拖泥,這裡將給你補充官方沒有的,深入到蹲坑給你排憂解難,總會給你點驚喜,內容越後越幹,請緊張的往下看。

兩個字,講究

***** 本文配套,超完整 Weex 專案推薦 : GSYGithubAppWeex *****

一、簡介

 有對比才有傷害,說到Weex,難免讓人聯絡React Native。雖同為跨平臺移動端解決方案,擁JavaScript妄一統天下,單兩者的設計理念其實截然不同。

 這裡先介紹下兩者的差異,給徘徊在 React Native 和 Weex 之間的人,理解更適合哪些場景。

型別 React Native Weex
效能 較好 較弱
上手難度 稍高 容易
核心理念 React Vue
框架程度 較重 較輕
特點 適合開發整體App 適合單頁面
社群 豐富,Facebook維護 略殘念,目前託管apache
支援 Android、IOS Android、IOS、Web
適應性 原生開學習成本低 Web開發學習成本低
JS引擎 JSCore V8

 作為兩個框架的深度體驗者,個人總結出上面的對比,其中可以看出:

  • React Native更適合開發完整的App,因為它的效能較好,第三方外掛豐富,社群活躍並且維護較好,文件完整等(本篇主角是Weex好吧魂淡(#゚Д゚))。

  • Weex更適合開發單頁面整合,這也是阿里的業務特性。 當然Weex也可以開發完整的多頁面App,同時我也是這麼用過,不過效果對比React Native,顯然強差人意。

  • Weex勝在容易上手,基於Vue的設計模式,類MVVM的實現,也讓前端能更無縫的實現一些高效能的App業務。

  • Weex相容Android、IOS、Web三端,在單頁面的實現上,它有著React Native無法睥睨的先天優勢。

  • Weex的社群,個人覺得還是弱,資料不足,文件簡單,第三方支援太弱。和React Native一樣支援帶原生功能的外掛開發,但是,支援太少了,這也提高了後期的開發門檻。同時,一個小問題很容易讓入初學者,三過門而不入,作為一個釋出了兩年的框架,還是比較讓人吐槽的。

這就是,所謂的前言吧

二、原理

 這裡簡要說明下Weex在android下的分層以及原理。

 Weex主要包括三大部分:JS BridgeRenderDom,分別對應WXBridgeManagerWXRenderManagerWXDomManager 。通過WXSDKManager統一管理。其中JS BridgeDom執行在獨立的HandlerThread中,而Render執行在UI執行緒。JS Bridge主要用來和 JS 端實現進行雙向通訊,比如把js端的dom結構傳遞給Dom執行緒。Dom主要是用於負責dom的解析、對映、新增等等的操作,最後通知UI執行緒更新。而Render負責在UI執行緒中對dom實現渲染。

 如下圖,是生成dom,dom的解析,對映,新增,渲染的流程。

圖片來自網路

 如上可知,因為JS端執行於獨立的單執行緒中,所以為了保證執行的流暢性,一般需要避免在JS端執行耗時操作,比如:網路請求,圖片載入等,其實都是在原生端完成,js端執行的是發起一個請求和響應一個結果。同時因為原生端與JS端是通過JS Bridge通訊,所以也需要儘量避免大資料和頻繁的通訊,導致響應的延遲。

 原生端的dom的載入解析對映,也是效能的一大瓶頸。一般而言,Weex在Web端生成的,是通過webpack的webConfig打包成單頁面的index.web.js檔案;而在原生端,一般會通過webpack的weexEntry配置成多頁面形式:即每一個需要獨立的.vue的頁面,最終會被打包成一個.js檔案。所以開啟每個頁面時載入對應的js檔案,這很好的減小了需要載入的檔案大小,提高了dom的解析效率。最後,Weex預設打的js只包含業務js程式碼,基礎js庫已經被包含在weex sdk中,也使得體積會小很多。

三、入門

1、配置環境

 程式設計師就要從配置環開始,Weex 環境搭建 ,點選連結,只要你要一個穩定的網路,參考官網搭建環境,也就一杯茶的功夫,take it easy。配置好之後,weex create testProject建立一個專案High起來吧。

2、快速入門

 weex的入門還是比較簡單的,JavaScript、Vue瞭解下,即可預約的hello world。

 原生開發也許對vue接觸不多,跨界有時候很容易望而卻步,其實Vue本身,就是容易上手的框架,類似MVVM的模式(類似Android的DataBinding),很容讓人理解上手,簡單的說,你只關心資料,然後繫結到顯示的控制元件,就是這麼簡單。

 一般通過 Vue官網 教程,30分快速擼一發,之後你就直接入門Weex了,對,Weex做的最徹底的就是,你直接使用 vue 寫一個Web頁面,之後再順手編譯成了 ios 和 android 的原生頁面(儘管有些時候你需要在平臺適配上花費心思)。

請忽略那個this,真的( ̄. ̄)

 如上圖(請忽略那個this( ̄. ̄)),這就是一個極度簡化的,用Vue寫的Weex頁面。效果是從顯示Hello World ,一秒變I’m CarGuo,就是這麼自信。

 在<template>中排布需要渲染的控制元件,在<style>中指定控制元件的樣式(當然你也可以直接在<template>中),在<script>中寫資料獲取和處理邏輯等,是不是很簡單, Don’t be shy,Let's fuck it !

 因為需要支援三端,Weex在Vue的基礎上閹割了一些標籤、css樣式和事件,具體可見與 Web 平臺的差異

 其中,在Android和IOS上,<text> <image>等標籤,其實是被編譯為原生控制元件,這就是上面所說的dom解析,同時你也可以在原生端,自定義控制元件或者功能模組,然後註冊到weex中使用,實際上weex提供的基礎控制元件和功能模組並不多,但卻很容易擴充,具體可見 擴充原生端功能

(ps 也不知道阿里是怕做多錯多,還是懶)

 說到這裡,就需要說一說Weex的原生外掛開發支援,這也是官方文件比較沒整理好的原因,其實文件是有的:Weex外掛開發文件,如Android外掛大致流程就是:

  • weex plugin create命令建立外掛。
  • 在android目錄的library下通過@WeexComponent(控制元件)、@WeexModule(非UI功能)、@WeexAdapter(weex繼承功能擴充)實現第三方支援。
  • 將library釋出到maven (當然你也可以直接原始碼釋出到npm)
  • 配置根目錄的package.json然後釋出到npm

 由此可見,weex可以很方便的提供原生功能的擴充支援,但是由於社群較為薄弱,導致第三方外掛缺失,有(hen)些(duo)時候你可能不得不自己著手,開發原生端的功能支援,這就對於跨平臺開發而言,特別前端開發而言,就稍(te)顯(bie)不友好了。

目瞪狗帶

題外話 :說到跨平臺開發,也許你聽說過cordova這位老大哥,它曾是早期的跨平臺開發潮流,cordova提供豐富的原生外掛和打包功能:通過webview把前端頁面打包成一個App,通過外掛提供前端需要的原生介面,互動通過webview的js介面支援。為什麼說起它呢,是因為Weex中,你可以看到很多cordova的影子,類似weex platform add androidweex plugin add xxx都有些cordova的味道。如下圖,你如今依舊可以在Weex中找,尋找到cordova的存在感。

cordova殘留

3、其他推薦
Vuex 和 Vue-Router ,居家旅行必不可少。
  • Vuex類似Redux,如果你沒聽說Redux不要緊,也不要慫,簡單了說,Vuex就是單頁面下,幫你管理資料的框架。資料都存在Vuex的store中,你操作store更新資料,然後將store繫結到介面。它的用處在於可以在多個vue元件間,方便的同步資料,更新介面。

  • Vue-Router也是用於單頁面下,指定跳到某個頁面的管理工具,路由嘛,淺顯易懂。

iconfont :向量圖示,少不了iconfont,通過字型檔生成圖示,資源豐富,絕對值得推薦。
weex-ui: weex中難得的良心官方封裝庫。
eros :eros 不是框架,是基於 weex 封裝、面向前端的 vue 寫法的一整套 APP 開源解決方案,是由本木醫療大前端團隊經過大量實踐沉澱而出。。

四、深入填坑

1、ES6、ES7

  說到 Javascript ,ES6、ES7必須瞭解下。Weex中預設就有對其支援,但是對於async、await等,還需要如下一些簡單配置,然後 have fun 。

//命令列安裝
npm install --save-dev babel-plugin-transform-runtime

//然後在.babelrc檔案中加入
{
"presets": [
"es2015",
"stage-0"
],
"plugins": [ [
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]]
}
複製程式碼
2、多頁面

 Weex預設是單頁面效果,也就是Android中一個Activity的概念,而單頁面效果在原生上,跳轉一多效果就會web了。既然叫native,怎麼可能如此too young,所以這個時候,就需要修改預設的webpack,讓其支援naive多頁面了ı╮(╯▽╰)╭。

 首先,要知道Weex真正執行的是,通過entry.js作為入口檔案檔案,通過webpack,將.vue檔案打包成index.js進行使用。Look,多頁面的重點,就是將獨立頁面的.vue檔案,生成多個js檔案。

入口js

 如上圖,參考entry.js檔案,建立一個SecondPageEntry.js,作為SecondPage.vue的入口,用於webpack生成SecondPage.js頁面。

 什麼?webpack沒聽說過怎麼辦,No problem,你只需簡單的修改,一知半解完全可以勝任。如下圖,我們主要需要修改webpack.common.conf.js檔案,

webpack.common.conf.js

 可以看出,webpack.common.conf.js中,其實是區分了webConfig和weexConfig的不同打包方式。如下圖,其中weexEntry就是我們需要修改的地方,可以看到本來已經有index和entry.js存在了。

Weex原理之帶你去蹲坑

 最後我們需要通過navigator來實現跳轉,我們需要知道,要跳轉的js檔案在哪裡,如下程式碼演示,如何實現navigotor的native跳轉,完整相容三端跳轉請移步demo專案。

 //獲取當前js檔案所在完整路徑
 let bundleUrl = this.$getConfig().bundleUrl;
 bundleUrl = String(bundleUrl);
 let nativeBase;
 //android一般位於file://assets目錄下
 let isAndroidAssets = bundleUrl.indexOf('file://assets/') >= 0;
 //ios一般位於一般帶有file開頭,帶有WeexDemo.app
 let isiOSAssets = bundleUrl.indexOf('file:///') >= 0 && bundleUrl.indexOf('WeexDemo.app') > 0;
 if (isAndroidAssets) {
     nativeBase = 'file://assets/dist/';
 } else if (isiOSAssets) {
     nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1);
 } else {
    let host = 'localhost:8080';
    let matches = /\/\/([^\/]+?)\//.exec(bundleUrl);
    if (matches && matches.length >= 2) {
        host = matches[1];
    }
    nativeBase = 'http://' + host + '/index.html?page=./dist/';
 }
 return nativeBase;

複製程式碼
3、樣式sass\scss

sass,後期必不可少的利器。

 當你的weex專案不斷變大,一些樣式共享,公共顏色,大小尺寸等的管理,就是你需要面對的問題。

 這時候sass和scss就可以起到很大的作用。最大優點是,它可程式設計,支援定義變數,而且不像閹割後的css一樣,var()這種寫法無法在native下得到支援,這時候sass的效果絕對讓你回味無窮。

 使用sass也十分簡單,簡單配置下webpack,sass的語法也十分容易上手,只需十分鐘瞭解下就可以愉快的享用這塊糖了。

  • 先安裝sass依賴:
npm install node-sass;
npm install sass-loader; //依賴node-sass
複製程式碼
  • 之後webpack.common.conf.js中配置loader,如下圖,在兩個module處,增加紅框配置。

Weex原理之帶你去蹲坑

  • 最後用 import 引入的sass檔案進行載入,詳細可檢視demo工程。
//也可以 lang="scss"
<style lang="sass">
//匯入寫好的檔案
@import "./style.scss";
</style>
複製程式碼

五、蹲坑

 其實就是問題集錦,記錄一些開發過程中遇到的問題,相信你會喜歡:

  • 1、關於vue的<scrpit>標籤內,weex.requireModule(包括外掛weex-ui)中,在全域性獲取返回null的原因,是因為entry.js中的router物件,不能用import 和 export default,只能用require 和 module.exports 配合。

  • 2、es6一些語法問題,如async和await,可以用"babel-plugin-transform-runtime",在.babelrc中設定。

{
"presets": [
"es2015",
"stage-0"
],
"plugins": [ [
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]]
}
複製程式碼
  • 3、export default 和require混合使用的時候,會多一個default物件,比如this.$store.default.state這樣才對的問題。

  • 4、自定義的js檔案類中,不能使用全域性的weex.requireModule

  • 5、使用weex-ui的tabbar結合是,<list>必須有高度,或者overflow屬性為scroll才能滑動,而且overflow的位置必須是不會影響其他頁面位置。

  • 6、全屏預設height 1334 和 width 750,但是記得減去32大概高度的statusbar。

  • 7、list的loadmore,必須給list設定高度樣式,才能在web中正常觸發。

  • 8、text的</text>結束標籤換行,在debug下可能會出現樣式問題。

  • 9、生命週期在web中與android等不同,比如activated等。

  • 10、()=> {}對於this可能獲取存在的不同,儘量用function(){}。

  • 11、多頁即建立多個類似entry.js的入口檔案,在webpack下配置weex的開啟生成的js檔案,用於navigator跳轉,通過url傳值。

  • 12、android多頁面開啟失敗

android.os.FileUriExposedException問題:

在你的Application中新增:
if (Build.VERSION.SDK_INT>=18) {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();
}
複製程式碼

ActivityNotFoundException問題:

 <activity
         android:name=".xxxxxx"
         android:label="@string/app_name"
         android:screenOrientation="portrait"
         android:theme="@style/AppTheme.NoActionBar">
     <intent-filter>
         <action android:name="com.taobao.android.intent.action.WEEX"/>

         <category android:name="android.intent.category.DEFAULT"/>
         <category android:name="com.taobao.android.intent.category.WEEX"/>
         <action android:name="android.intent.action.VIEW"/>

         <data android:scheme="http"/>
         <data android:scheme="https"/>
         <data android:scheme="file"/>
         <data android:scheme="wxpage" />
     </intent-filter>
 </activity>
複製程式碼
  • 13、多頁面生成js時,import的時候,需要指定.vue字尾的。

  • 14、如果是webstorm,記得對.temp dist node_modules platforms幾個資料夾,右鍵設定excluded,避免一直indexing和硬碟資源消耗。

  • 15、@viewappear="onappear" @viewdisappear ="ondisappear" 類似onPause和onResume

  • 16、ios實時看log,可以先增加

-(void)redirectConsoleLog{
#ifdef DEBUG
    NSString *documentDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSLog(@"documentPath : %@",documentDir);

    //重定向NSLog
    NSString* logPath = [documentDir stringByAppendingPathComponent:@"console.log"];
    freopen([logPath fileSystemRepresentation], "a+", stderr);
#endif
}
複製程式碼
//呼叫
[self redirectConsoleLog];
複製程式碼

然後在Devices下,找到對應的模擬器號碼,在再Application下,搜尋console.log,跟蹤執行

tail -f
/Users/your name/Library/Developer/CoreSimulator/Devices/FDEACA11-D84E-4E8F-A6B8-26239559A928/data/Containers/Data/Application/9394D6CC-6B4A-4200-A13D-0CBE6F2BB67A/Documents/console.log
複製程式碼

最後

1、文章配套,超完整 Weex 專案瞭解下:github.com/CarGuo/GSYG…

2、react native相關文章

從Android到React Native開發(一、入門)

從Android到React Native開發(二、通訊與模組實現)

從Android到React Native開發(三、自定義原生控制元件支援)

從Android到React Native開發(四、打包流程和釋出為Maven庫)

還記得我嗎

相關文章