適合前端Vue開發童鞋的跨平臺Weex

基於 Vue 技術棧的你若是須要選用一種移動端跨平臺框架,是 Weex?React-Native?仍是Flutter? 無疑,相對於後二者,由於你如今已有比較熟練的 Vue 基礎,若是在其餘條件一致的狀況,Weex 無疑是最佳選擇;可是 Weex 真的適合在實際項目中進行移動端跨平臺開發嗎?Weex 的開發效率、Weex 的質量是否知足需求?前端

1、開發環境

在這個 Weex app 開發中,個人開發環境相關配置以下:vue

工具名稱 版本號
Node.js 8.2.1
Npm 5.3.0
Android Studio 3.2
Weex 2.0.0-beta.17
JDK 1.8
Weex-ui 0.6.14

2、Weex 介紹

2.一、Weex 理念

「Write once, run everywhere」, Weex 的定義就像是:寫個 vue 前端,順便幫你編譯成性能還不錯的 apk 和 ipa(固然,現實有時很骨感)。基於 Vue 設計模式,支持 web、android、ios 三端,原生端一樣經過中間層轉化,將控件和操做轉化爲原生邏輯來提升用戶體驗。 在 weex 中,主要包括三大部分:JS Bridge、Render、Dom,分別對應WXBridgeManager、WXRenderManager、WXDomManager,三部分經過 WXSDKManager 統一管理。其中 JS Bridge 和 Dom 都運行在獨立的 HandlerThread 中,而 Render 運行在 UI 線程。 JS Bridge 主要用來和 JS 端實現進行雙向通訊,好比把 JS 端的 dom 結構傳遞給 Dom 線程。Dom 主要是用於負責 dom 的解析、映射、添加等等的操做,最後通知 UI 線程更新,而 Render 負責在 UI 線程中對 dom 實現渲染。
Weex 全部的標籤也不是真實控件,JS 代碼中所生成存的 dom,最後都是由 Native 端解析,再獲得對應的 Native控件渲染,如 Android 中標籤對應 WXTextView 控件。 Weex 中文件默認爲 .vue ,而 vue 文件是被沒法直接運行的,因此 vue 會被編譯成 .js 格式的文件,Weex SDK會負責加載渲染這個 js 文件。Weex 能夠作到跨三端的原理在於:在開發過程當中,代碼模式、編譯過程、模板組件、數據綁定、生命週期等上層語法是一致的。不一樣的是在 JS Framework 層的最後,web 平臺和 Native 平臺,對 Virtual DOM 執行的解析方法是有區別的。java

2.2 建立 Weex 項目

Weex 提供了一個命令行工具 weex-toolkit 來幫助開發者使用 Weex,它能夠用來快速建立一個空項目、初始化 iOS 和 Android 開發環境、調試、安裝插件等操做。node

咱們能夠經過如下步驟建立一個基礎的 Weex 項目:
(1)安裝 weex-toolkit 工具react

npm install weex-toolkit -g

(2)建立新項目android

weex create weex_project

(3)安裝項目依賴ios

cd weex_project
npm install

(4)啓動項目git

npm start

項目啓動完畢,瀏覽器窗口會自動打開項目首頁,以下圖所示:
在這裏插入圖片描述
(5)添加 原生Android 平臺github

weex platform add android

(6)運行下面的命令,能夠在模擬器或真實設備上啓動 Android 應用:web

weex run android

2.三、運行Weex項目

2.3.一、啓動服務端應用

(1)進入目錄 weex_project/backend/,安裝服務端應用所須要的插件包:

$ npm install

(2)啓動服務端應用

$ npm run start

2.3.二、啓動 Weex 應用

(1)若是你還沒安裝 weex 工具,能夠運行如下命令進行安裝:

$ npm install -g weex-toolkit

(2)安裝項目須要的插件包:

$ npm install

(3)啓動項目:

$ npm run start

3、Weex 經常使用的 VSCode 插件

Weex爲VSCode提供了一些經常使用的插件,能夠提升開發效率:

  • weex-new-project - 用於在 VSCode 中建立Weex項目;
  • weex-lang - 用於在 VSCode 中對最新的 Weex 語法進行支持;
  • weex-doctor - 用於檢查 iOS 和 Android 本地開發環境;
  • weex-debugger - 用於在 VSCode 中啓動Weex調試工具;
  • weex-run - 用於在熱更新模式下啓動 Android 及 iOS 工程;

3.一、weex-run

可使用截圖的步驟來安裝 weex-run插件,能夠自行搜索如何安裝VSCode插件。
在這裏插入圖片描述
(2)啓動 Android 項目

在這裏插入圖片描述
啓動成功控制檯會輸出一堆日誌,以下圖。
在這裏插入圖片描述
Weex自帶熱更新功能,接下來,咱們查看 Android 項目的熱更新。

3.二、weex-debugger

(1)安裝 weex-debugger 插件,安裝過程和安裝weex-run插件相似。
(2)ctrl + shift + p 彈出命令輸入框,以下圖所示輸入:weex debug,而後網頁會出現第 2 張截圖的二維碼:
在這裏插入圖片描述
在這裏插入圖片描述
(3)用手機的 Weex Playground App 的二維碼進行掃描,出現如下調試頁面(必定必定要注意,手機連的 WiFi 和 你開發本地網絡在同一局域網)。
在這裏插入圖片描述
(4)再用手機的 Weex Playground App 的二維碼掃描 Weex 應用的二維碼,調試頁面就會變成對應的 Weex 應用的調試頁面,以下圖所示。
在這裏插入圖片描述

4、Weex 項目實戰

4.一、項目目錄路徑

下面經過一個Weex 項目來講明Weex的一些基礎,項目目錄結構以下:
在這裏插入圖片描述

4.二、功能模塊設計

考慮到更好的體驗 Weex 和 H5 在開發效率、功能性能、用戶體驗等方面的差別性,咱們對功能模塊進行精心設計,主要基於咱們現有的實際項目的業務進行開發,並結合移動端特有的特性。
相關的模塊功能設計以下圖所示,其中紅色標註部分表示,受限於開發資源、Weex 生態方面緣由,咱們暫時還沒完成所有功能的開發。
在這裏插入圖片描述

4.三、功能界面展現

下面是Weex示例項目截取一些功能界面展現,以下圖:
在這裏插入圖片描述

4.四、重要功能介紹

除了一些常規的功能開發外,如下介紹的幾個功能在 Weex 官網中並無詳細介紹或者根本沒有介紹,咱們在開發過程當中踩了很多坑,所以將踩坑經驗進行彙總,幫助你們避免踩坑:

(1)登陸 token 認證
(2)圖片選擇/上傳功能
(3)websocket 功能實現
(4)手機物理鍵返回上一級功能
(5)Android 如何顯示本地圖片

4.4.一、token 認證功能

(1)token 認證介紹

在 Web 領域基於 token 的身份驗證隨處可見。在大多數使用 Web API 的互聯網公司中,tokens 是多用戶下處理認證的最佳方式。token 具備如下特性:

  • 無狀態、可擴展
  • 支持移動設備
  • 跨程序調用
  • 安全

基於 token 的身份驗證的過程以下:

  • 用戶經過用戶名和密碼發送請求。
  • 服務端程序驗證。
  • 程序返回一個簽名的 token 給客戶端。
  • 客戶端儲存 token,而且每次用於每次發送請求。
  • 服務端驗證 token 並返回數據。

(2)weex 和 express 之間實現 token 認證

express 服務端主要使用 express-jwt 插件,express-jwt 是 nodejs 的一箇中間件,內部對 jsonwebtoken 進行封裝使用。express-jwt 會驗證指定 http 請求的 jsonwebtoken 的有效性,若是有效就將 jsonwebtoken 的值設置到 req.user 裏面,而後跳轉到相應的 router。

如下是服務端 express 的代碼邏輯,代碼以下:

var expressJWT = require('express-jwt');
// token 設置
app.use(expressJWT({
  secret: CONSTANT.SECRET_KEY
}).unless({
  // 除了如下配置的地址,其餘的URL都須要驗證
  path: ['/getToken', /^\/public\/.*/, /^\/user_disk\/.*/]
}));

// 登陸時,須要進行用戶密碼認證,相應路由跳轉到下面一步
app.use('/getToken', tokenRouter);

// 當用戶密碼正確時,咱們進行 token 設置
data: {
  token: jsonWebToken.sign({
    uid: obj.uid
  }, CONSTANT.SECRET_KEY, {
    expiresIn: 60 * 60 * 1
  }),
}

對應的Weex的代碼以下:

// Weex 登陸邏輯
login () {
  let param = {
    uid: this.uid,
    password: this.password
  };
  let options = {
    url: '/getToken',
    method: 'POST',
    body: JSON.stringify(param)
  };
  let vm = this;
  api.fetch(options, function (ret) {
    if (ret.ok && ret.data.code === 0) {
      // 前端能夠獲取到服務端返回的 token ,並將其做爲全局變量  
      global.token = 'Bearer ' + ret.data.data.token;
      vm.$router.push('/tabIndex');
    } else {
      modal.toast({
        message: '用戶認證失敗!',
        duration: 1
      });
    }
  });
}

// Weex 的每次請求,頭部都帶上 token
initOptions.headers['Authorization'] = global.token;

通過以上代碼邏輯處理後,咱們查看 Weex 向服務端發送的請求頭部,都攜帶了 token,以下圖所示。這樣服務端 express 處理這個請求時,就能夠經過解析 token 獲取到對應的用戶 id ,從而容許其對服務端的數據訪問。
在這裏插入圖片描述

4.4.二、圖片選擇/上傳功能

(1)存在問題

很遺憾,Weex 居然沒有提供文件選擇/上傳的模塊,對於前端開發者來講無疑晴天霹靂,那我不是要手動去寫 Android 的 java 代碼,通過反覆查找,真的沒有文件選擇/上傳模塊,因而咱們只能本身去寫 Java 代碼去實現 Android 端圖片選擇以及上傳功能。固然,也可使用一些第三方的插件。

(2)實現 Android 原生的圖片選擇/上傳功能

在 weex_projectplatformsandroidappsrcmainjavacomweexappextend 目錄下新建 圖片上傳 模塊的類 WXAlbumModule ,其繼承 WXModule ,其主要兩個方法爲 choosePhoto 和 onActivityResult ,其中 choosePhoto 用於給 Weex 前端來調用,當 Weex 前端須要選擇相冊中的圖片時,Weex 前端就調用 choosePhoto 方法;onActivityResult 是用戶選擇好相冊中的圖片後,會相應觸發該事件,並將用戶選擇的相片以參數形式傳入 onActivityResult ,從而咱們能夠在 onActivityResult 中進行圖片的上傳邏輯,圖片上傳完成後,Android 端會在回調事件中通知前端,圖片放置在服務端的目錄路徑,前端能夠對應進行圖片顯示等操做。關鍵代碼邏輯以下,若是若是對 Java 徹底一無所知的同窗能夠先不看,懂 java 代碼的建議結合項目代碼來看,會更清晰。

例如,下面是Android端封裝的

@JSMethod(uiThread = true)
// 給 Weex 前端調用,當用戶點擊時,調用該函數
public void choosePhoto(String param, JSCallback callback) {
    if (ContextCompat.checkSelfPermission(mWXSDKInstance.getContext(),
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions((WXPageActivity) mWXSDKInstance.getContext(),
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                CAMERA_REQUEST_CODE);
    } else {
        choosePhoto();
    }
    try{
        JSONObject jsonObject = new JSONObject(param);
        this.type = (String)jsonObject.get("type");
        this.path = (String)jsonObject.get("path");
        this.url = (String)jsonObject.get("url");
        this.token = (String)jsonObject.get("token");
    }catch (JSONException e){
        e.printStackTrace();
    }
    this.callback = callback;
}

選擇完成後,系統會返回圖片的信息,此時就能夠進行上傳操做,以下所示:

@Override
// 用戶選擇好相冊中的圖片後,會相應觸發該事件,並將用戶選擇的相片以參數形式傳入
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == WXPageActivity.RESULT_OK) {
        switch (requestCode) {
            case CAMERA_REQUEST_CODE: {
                try {
                    Uri selectedImage = data.getData();
                    String[] filePathColumns = {MediaStore.Images.Media.DATA};
                    Cursor c = mWXSDKInstance.getContext().getContentResolver().query(selectedImage, filePathColumns, null, null, null);
                    c.moveToFirst();
                    int columnIndex = c.getColumnIndex(filePathColumns[0]);
                    String picturePath = c.getString(columnIndex);
                    c.close();

                    //上傳的文件
                    File file = new File(picturePath);
                    // 普通參數
                    HashMap<String , String> params = new HashMap<>();
                    params.put("path", this.path);
                    uploadForm(params, "file", file, "", this.url);

                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

實現好以上選擇圖片和上傳圖片的代碼邏輯後,咱們須要在 weex_projectplatformsandroidappsrcmainjavaWXApplication.java 中進行模塊的註冊,代碼邏輯以下:

WXSDKEngine.registerModule("wxalbum", WXAlbumModule.class);

而後,Weex 前端調用註冊的原生模塊便可,以下所示:

const WXAlbum = weex.requireModule('wxalbum');

upload () {
  let path = 'public/upload/';
  let vm = this;
  storage.getItem('token', event => {
    let param = {
      type: 'image/jpeg', // 選擇的數據類型
      path: path,
      url: CONSTANT.SERVER_URL + '/users/upload',
      token: event.data
    };
    WXAlbum.choosePhoto(JSON.stringify(param), ret => {
      let obj = JSON.parse(ret);
      vm.imgPath = '/' + path + obj.file[0].originalFilename;
      modal.alert({
        message: vm.imgPath,
        okTitle: '確認'
      }, function () {
        console.log('alert callback')
      })
    });
  })
},

4.4.三、WebSocket 功能實現

(1)存在問題

Weex 官網的 webSocket 章節特地標註如下警告字眼:
h5 提供 WebSockets 的 protocol 默認實現,iOS 和 Android 須要自定義實現,Android 可參考:

  • DefaultWebSocketAdapter.java
  • DefaultWebSocketAdapterFactory.java

好吧,根本沒有封裝 WebSocket 功能,那我就按官網給的參考來實現吧,因而,我點擊前面兩個參考連接,連接打開的頁面根本不存在,報 404(官網出現這種問題,實在不該該啊)。網上谷歌搜索一圈,沒有發現相似的問題,仍是主要查看了這個給的 url 以及結合阿里將 weex 貢獻給 Apache 維護這個事情,猜想是否是 Weex 捐給 Apache 維護,github 的庫目錄更改,可是官網對應的 url 地址沒有作修改。通過查找,確實是這個問題,在舊庫中如下目錄找到官網提的:DefaultWebSocketAdapter.java 和 DefaultWebSocketAdapterFactor.java :
github.com/alibaba/wee…

(2)手動實現 WebSocket 功能

咱們 在 weex_projectplatformsandroidappsrcmainjavacomweexappadapter 目錄底下建立 Websocket 的實現類 DefaultWebSocketAdapter.java 和工廠建立類 DefaultWebSocketAdapterFactory.java ,關鍵邏輯代碼以下:

// 該類主要實現 Websocket 的鏈接、發送消息、接收消息、關閉等函數或事件
public class DefaultWebSocketAdapter implements IWebSocketAdapter {
  @Override
  public void connect(){...}
  @Override
  public void send(String data) {...}
  @Override
  public void close(int code, String reason) {...}
  @Override
  public void destroy() {...}
  ...  
}

而後,爲該類主要爲建立 Websocket 對象的工廠類:

// 該類主要爲建立 Websocket 對象的工廠類
public class DefaultWebSocketAdapterFactory implements IWebSocketAdapterFactory {
    @Override
    public IWebSocketAdapter createWebSocketAdapter() {
        return new DefaultWebSocketAdapter();
    }
}

接下來,在 weex_projectplatformsandroidappsrcmainjavacomweexappWXApplication.java 中初始化 Websocket ,以下所示:

WXSDKEngine.initialize(this,
        new InitConfig.Builder().setImgAdapter(new ImageAdapter()).                        setWebSocketAdapterFactory(new DefaultWebSocketAdapterFactory()).build()
);

而後,在 Weex 的前端中導入Websocket模塊,就可使用 Websocket,相關代碼以下:

const ws = weex.requireModule('webSocket');

ws.WebSocket(CONSTANT.SOCKET_WS, '');
// 須要注意 web 端的寫法和 android 端的寫法不同
// android 的 onxx 事件是一個方法,須要傳入一個JSCallback的值,
if (weex.config.env.platform === 'Web') {
  ws.onmessage = this.socketMessage;
} else {
  ws.onmessage(this.socketMessage);
}

4.4.四、點擊手機物理鍵返回上一級功能

(1)存在問題

咱們開發的 Weex app,若是在 app 的哪一個界面,點擊手機的返回上一級物理鍵,都會致使 app 退出,好吧,Weex 也沒有提供對應的事件處理,咱們不得不本身再去寫安卓的 java 代碼去向 Weex 的 Web 端拋出這個事件。

(2)重寫手機物理鍵返回上一級的處理邏輯

正常交互邏輯:當處於主界面時,返回上一級物理鍵會進行提示「再點擊一次退出」,若是不是處於主界面時,會返回上一級頁面。

首先,咱們在 weex_projectplatformsandroidappsrcmainjavacomweexappWXPageActivity.java 中添加監聽點擊手機物理鍵的事件,以下所示:

public void onBackPressed(){
    Map<String,Object> params=new HashMap<>();
    params.put("name","msg");
    mInstance.fireGlobalEventCallback("androidback",params);
  }

在 Weex 的 vue 入口文件中,監聽 androidback 事件,當接收到該事件時,進行相應的邏輯處理,代碼以下所示:

listenAndroidBack () {
  let vm = this;
  globalEvent.addEventListener('androidback', function (e) {
    if (vm.$route.name === 'tabIndex' || vm.$route.name === 'loginPage') {
      if (vm.exitFlag) {
        weex.requireModule('wxclose').closeApp();
      } else {
        modal.toast({
          message: '再點一次退出',
          duration: 1
        });
        vm.exitFlag = true;
        vm.clearExitFlag();
      }
    } else {
      vm.$router.go(-1);
    }
  });
},

4.4.五、Android 顯示本地圖片

(1)存在問題

Weex 官網中 image 圖片組件顯示項目目錄下圖片,src 地址直接寫成相對路徑,以下所示;可是這種寫法存在問題,它只支持 web 端的顯示,在 Android 端是沒法顯示的,找不到對應圖片。

<image ref="poster" src="path/to/image.png"></image>

(2)Android/IOS 端顯示本地圖片

Weex 沒有在將 vue 編譯成 Android 組件時,對應將圖片放置到 Android 對應的目錄下,因此咱們只好本身將圖片手動再放置一份,其中 Android 端須要額外將圖片放在 /platforms/android/app/src/main/res/drawable-xxhdpi ,IOS 放入xcode 底下的 /Source/images/下 ,而後咱們在代碼邏輯中,根據環境判斷如今是 Web 環境、Android 環境或者 IOS 環境,再對應的獲取對應目錄下的圖片(暫時只能作到這種程度了...),以下代碼所示:

const ICON_URL = {
  Web: `${WEB_IMAGE_URL}`,
   android: `local:///${pureName}`,
   iOS: `local:///filePng/${pureName}${suffixName}`
}
return ICON_URL[CUR_RUN_PLATFORM];

5、編譯 Android apk

Android apk 打包分 debug 版和 release 版,一般所說的打包指生成 release 版的 apk,release 版的 apk 會比debug 版的小,release 版的還會進行混淆和用本身的 keystore 簽名,以防止別人反編譯後從新打包替換你的應用。 下面咱們主要介紹如何在 Android Studio 中對 weex 項目進行打包。

5.一、Android 平臺目錄

Android Studio 打開 Android 工程,目錄爲:weex 項目 /platforms/android 。

5.二、常規的 AS 打包分爲兩種

一種是沒有 「.jks」 文件的打包
一種是有 「.jks」 文件的打包
注:.jks」 文件 相似 apk 身份證;

5.三、沒有 「 .jks 」 文件的打包

(1)打包步驟以下截圖:
在這裏插入圖片描述
在這裏插入圖片描述
(2)咱們點擊選擇 【Create new】建立jks
在這裏插入圖片描述
(3)填寫 key 的相關信息
在這裏插入圖片描述
(4)點擊 OK 以後,能夠看到以下信息已被自動填充,並點擊打包便可。
在這裏插入圖片描述
在這裏插入圖片描述
(5)等待打包完成後,就能夠查看打包好的 apk 文件
在這裏插入圖片描述

6、Weex 開發總結

6.一、官網常常沒法訪問

Weex 官網常常出現沒法訪問的狀況,頻率大概一週至少一次;這就很影響開發效率了,由於在開發過程當中須要常常查看官網的寫法、說明等,若是訪問不了,則會形成必定程度的開發 block。

6.二、官網文檔粗糙

Weex 官網的文檔比較粗糙,若是沒有比較好的前端和移動端原生開發知識儲備的話,看官網的文檔就很吃力了,官網不少講解寫的很是簡單,都默認你同時熟練前端和移動端原生開發,並且同時有較好前端和移動端原生開發人員應該在業界還比較少吧。

6.三、生態貧瘠

Weex 生態是真的貧瘠,除了阿里本身出產的組件庫 weex-ui 外,其它的相關插件幾乎找不到,有也是少於100個 star 的,例如我在項目開始前設計的一些功能:拍照、圖片選擇上傳、語音錄入、通信、定位、文件預覽等等移動端的特有功能,都沒有插件,都須要本身去寫 Android 的原生代碼,那這時就失去了利用框架提升開發效率的意義;生態跟 react-native 差的真不是一丁半點,而是根本不是一個量級。

6.四、是否兩個 Weex 版本

結合上一點,坊間傳聞:Weex 存在兩個版本,一個版本是阿里內部使用的,一個是非阿里內部使用;這個傳言無從驗證,可是結合第2點說的 Weex 生態貧瘠,我卻無心在瀏覽器搜索中,發現了一系列常見功能的插件封裝:weex.apache.org/zh/biz-comp… ,截圖以下,可是這些插件並無提供出來使用,存在 Weex 官網中,可是卻沒有訪問入口。若是這些插件功能能提供使用,無疑將很大程度豐富 Weex 的生態。
在這裏插入圖片描述

6.五、三端兼容性很差

Weex 號稱 「一次撰寫,多端運行」,可是存在不少兼容性問題,好比咱們在 Web 端調試開完後一個功能模塊,可是在 Android 端一運行,就各類跑不通,各類兼容性問題;這種問題致使,咱們後期根本不敢在 Web 環境開發,例如:咱們這個項目是想開發個 Android 的 app,咱們最終都直接在 Android 環境下開發,這種效率確定就沒有在 Web 環境開發效率高。

6.六、Vue 支持度不夠

Weex 默認集成 Vue 框架,並且主打 Vue 受衆,可是 Weex 對 Vue 的支持度還不夠,除了官網上提到的那些 vue 特性不支持外,還有不少特性沒有被列出,例如:vuex 等。

參考:
1,Weex實戰項目連接
2,《WEEX跨平臺開發實戰》出版啦
3,Weex開發之地圖篇
4,Weex Eros快速入門
5,WEEX環境搭建與入門
6,Weex開發之WEEX-EROS開發踩坑
7,移動跨平臺技術方案總結

相關文章
相關標籤/搜索