【quickhybrid】API多平臺支撐的實現

来源:http://www.cnblogs.com/dailc/archive/2017/12/25/8107881.html
-Advertisement-
Play Games

前言 在框架規劃時,就有提到過這個框架的一些常用功能需要支持 環境下的調用,也就是需要實現API的多平臺支撐 為什麼要多平臺支撐?核心仍然是復用代碼,比如在微信下,在釘釘下,在quick容器下, 如果沒有多平臺支撐,那麼 只能用於quick容器下,釘釘和微信下就得分別用其它代碼實現, 代碼復用率低, ...


前言

在框架規劃時,就有提到過這個框架的一些常用功能需要支持H5環境下的調用,也就是需要實現API的多平臺支撐

為什麼要多平臺支撐?核心仍然是復用代碼,比如在微信下,在釘釘下,在quick容器下,
如果沒有多平臺支撐,那麼quick.ui.alert只能用於quick容器下,釘釘和微信下就得分別用其它代碼實現,
代碼復用率低,如果實現了多平臺支撐。那麼三個平臺中同一個功能的代碼則是一樣的。

什麼樣的多平臺支撐

當然了,本框架實現的多平臺支撐和一般框架的有點區別。

一般的框架中支持多個平臺更多的是一個polyfill,譬如

// 假設以前不支持h5
const oldToast = quick.ui.toast;

quick.ui.toast = function(...) {
    if (os.h5) {
        // 做一些h5中做的
        ...
    } else {
        oldToast(...);
    }
};

這就是墊片實現,如果是新的環境,用新的實現,否則用老的實現

而__本框架中的多平臺實現是直接內置到了框架核心中__,也就是說框架本身就支持多平臺API的設置

quick.extendModule('ui', [{
    namespace: 'toast',
    os: ['h5'],
    defaultParams: {
        message: '',
    },
    runCode(...rest) {
        // 定義h5環境中的做法
        ...
    },
}, ...];

quick.extendModule('ui', [{
    namespace: 'toast',
    os: ['quick'],
    defaultParams: {
        message: '',
    },
    runCode(...rest) {
        // 定義quick環境中的做法
        ...
    },
}, ...];

在框架內部定義API時,不再是直接的quick.ui.alert = xxx,而是通過特定的API單獨給某個環境下定義實現

而且,框架中的定義,每一個API都是有quickh5環境下的實現的。

多平臺支撐的核心

從上述的介紹中也可以看到,多平臺支撐主要是前端的實現,與原生API,原生API在這裡面只能算一個環境下的實現

核心就是基於:Object.defineProperty,重寫set和get

Object.defineProperty(apiParent, apiName, {
    configurable: true,
    enumerable: true,
    get: function proxyGetter() {
        // 需要根據不同的環境,返回對應下的內容
        ...
    },
    set: function proxySetter() {
        // 可以提示禁止修改API
    },
});

本框架中的多平臺實現代碼可以參考源碼,這裡不贅述,下文中會介紹如何簡單的實現一個多平臺支撐API

實現一個多平臺支撐API

我們先預設最終的結果:

quick.os.quick = true;
quick.ui.alert('hello'); // quick-hello

quick.os.quick = false;
quick.ui.alert('hello'); // h5-hello

quick.ui.alert = 11; // 提示:不允許修改quick API

那麼要達到上述的要求,應該如何做呢?

寫一個雛形

最簡單的,先假設這些實現都已經存在,然後直接基於defineProperty返回

function alertH5(message) {
    alert('h5-' + message);
}
function alertQuick(message) {
    alert('quick-' + message);
}
const quick = {};

quick.ui = {};
quick.os = {
    quick: false,
};

Object.defineProperty(quick.ui, 'alert', {
    configurable: true,
    enumerable: true,
    get: function proxyGetter() {
        // 需要根據不同的環境,返回對應下的內容
        if (quick.os.quick) {
            return alertQuick;
        } else {
            return alertH5;
        }
    },
    set: function proxySetter() {
        // 可以提示禁止修改API
        alert('不允許修改quick API');
    },
});

那麼,它的調用結果是

quick.os.quick = true;
quick.ui.alert('hello'); // quick-hello

quick.os.quick = false;
quick.ui.alert('hello'); // h5-hello

quick.ui.alert = 11; // 提示:不允許修改quick API

雖然效果和預設的一樣,但是很明顯還需優化完善

增加拓展API的方法

拓展方式的定義如下

const quick = {};

quick.os = {
    quick: false,
};
/**
 * 存放所有的代理 api對象
 * 每一個命名空間下的每一個os都可以執行
 * proxyapi[namespace][os]
 */
const proxysApis = {};
// 支持的所有環境
const supportOsArray = ['quick', 'h5'];

function getCurrProxyApiOs(currOs) {
    for (let i = 0, len = supportOsArray.length; i < len; i += 1) {
        if (currOs[supportOsArray[i]]) {
            return supportOsArray[i];
        }
    }

    // 預設是h5
    return 'h5';
}

// 如獲取quick.ui.alert
function getModuleApiParentByNameSpace(module, namespace) {
    let apiParent = module;
    // 只取命名空間的父級,如果僅僅是xxx,是沒有父級的
    const parentNamespaceArray = /[.]/.test(namespace) ? namespace.replace(/[.][^.]+$/, '').split('.') : [];

    parentNamespaceArray.forEach((item) = >{
        apiParent[item] = apiParent[item] || {};
        apiParent = apiParent[item];
    });

    return apiParent;
}

function proxyApiNamespace(apiParent, apiName, finalNameSpace) {
    // 代理API,將apiParent里的apiName代理到Proxy執行
    Object.defineProperty(apiParent, apiName, {
        configurable: true,
        enumerable: true,
        get: function proxyGetter() {
            // 確保get得到的函數一定是能執行的
            const nameSpaceApi = proxysApis[finalNameSpace];

            // 得到當前是哪一個環境,獲得對應環境下的代理對象
            return nameSpaceApi[getCurrProxyApiOs(quick.os)] || nameSpaceApi.h5;
        },
        set: function proxySetter() {
            alert('不允許修改quick API');
        },
    });
}

function extendApi(moduleName, apiParam) {
    if (!apiParam || !apiParam.namespace) {
        return;
    }

    if (!quick[moduleName]) {
        quick[moduleName] = {};
    }

    const api = apiParam;
    const modlue = quick[moduleName];
    const apiNamespace = api.namespace;
    const apiParent = getModuleApiParentByNameSpace(modlue, apiNamespace);
    // 最終的命名空間是包含模塊的
    const finalNameSpace = moduleName + '.' + apiNamespace;
    // 如果僅僅是xxx,直接取xxx,如果aa.bb,取bb
    const apiName = /[.]/.test(apiNamespace) ? api.namespace.match(/[.][^.]+$/)[0].substr(1) : apiNamespace;

    // 這裡防止觸發代理,就不用apiParent[apiName]了,而是用proxysApis[finalNameSpace]
    if (!proxysApis[finalNameSpace]) {
        // 如果還沒有代理這個API的命名空間,代理之,只需要設置一次代理即可
        proxyApiNamespace(apiParent, apiName, finalNameSpace);
    }

    // 一個新的API代理,會替換以前API命名空間中對應的內容
    const apiRuncode = api.runCode;
    const oldProxyNamespace = proxysApis[finalNameSpace] || {};

    proxysApis[finalNameSpace] = {};

    supportOsArray.forEach((osTmp) = >{
        if (api.os && api.os.indexOf(osTmp) !== -1) {
            // 如果存在這個os,並且合法,重新定義
            proxysApis[finalNameSpace][osTmp] = apiRuncode;
        } else if (oldProxyNamespace[osTmp]) {
            // 否則仍然使用老版本的代理
            proxysApis[finalNameSpace][osTmp] = oldProxyNamespace[osTmp];
        }
    });
}

function extendModule(moduleName, apis) {
    if (!apis || !Array.isArray(apis)) {
        return;
    }
    if (!quick[moduleName]) {
        quick[moduleName] = [];
    }
    for (let i = 0, len = apis.length; i < len; i += 1) {
        extendApi(moduleName, apis[i]);
    }

}

quick.extendModule = extendModule;

上述代碼中增加了些複雜度,有一個統一管理所有代理調用的池,然後每次會更新對於環境下的代理

基於上述的方式可以如下拓展對於環境下的API

quick.extendModule('ui', [{
    namespace: 'alert',
    os: ['h5'],
    defaultParams: {
        message: '',
    },
    runCode(message) {
        alert('h5-' + message);
    },
}]);

quick.extendModule('ui', [{
    namespace: 'alert',
    os: ['quick'],
    defaultParams: {
        message: '',
    },
    runCode(message) {
        alert('quick-' + message);
    },
}]);

最終的調用如下(結果和預期一致)

quick.os.quick = true;
quick.ui.alert('hello'); // quick-hello
quick.os.quick = false;
quick.ui.alert('hello'); // h5-hello
quick.ui.alert = 11; // 提示:不允許修改quick API

雖然就一兩個API來說,這類拓展方式看起來很複雜,但是當API一多,特別是還需批量預處理時(如預設參數,Promise支持等),它的優勢就出來了

多平臺支撐在quick中的應用

quick hybrid框架中,預設支持quickh5有種環境,核心代碼就是上述列舉的(當然,內部增加了一些代理,預設參數處理等,會稍微複雜一點)。

基於這個核心,然後可以將框架的定義和API定義分開打包

quick.js
quick.h5.js

這樣,最終看起來h5下的API定義就是一個拓展包,是沒有它也不會影響quick環境下的使用,而且,如果增加一個新的環境(比如dd),
只需要再新增另一個環境的拓展包而已,各種寫法都是一樣的,這樣便於了統一維護

返回根目錄

源碼

github上這個框架的實現

quickhybrid/quickhybrid


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文轉載來自標點符的《資料庫中Schema(模式)概念的理解》,原文地址: http://www.biaodianfu.com/database-schema.html 在學習資料庫時,會遇到一個讓人迷糊的Schema的概念。實際上,schema就是資料庫對象的集合,這個集合包含了各種對象如:表、視 ...
  • ...
  • win10 64位 mysql安裝過程出現status顯示failed ...
  • MySQL中使用group_concat()函數長度限制問題,怎麼修改!!! ...
  • 1.1 索引的介紹 索引是對資料庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問資料庫表中的特定信息。如果想按特定職員的姓來查找他或她,則與在表中搜索所有的行相比,索引有助於更快地獲取信息。 索引的一個主要目的就是加快檢索表中數據的方法,亦即能協助信息搜索者儘快的找到符合限制條件的記錄ID ...
  • 自動佈局是一項強大的功能,它允許開發者創建一個單一的用戶界面,它會自動調整屏幕大小,方向和本地化,Xcode5中的編輯界面的自動佈局功能已經大大增強了。當約束缺失或錯誤配置時,界面生成器可以修複佈局。 參考資料:《iOS7開發快速入門》 ...
  • 轉載請註明原文鏈接:http://www.cnblogs.com/yanyojun/p/8099523.html 代碼已上傳到github:https://github.com/YanYoJun/FragmentTabHostDemo 上一篇有講過使用ViewPager來實現標簽卡效果的。這一篇講一 ...
  • Android遠程桌面助手理論上相容Android4.4至Android8.1之間所有的原生安卓系統,其他第三方ROM,如MIUI、Flyme、EMUI和Smartisan OS等也都陸續測試過,目前尚未發現相容性問題。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...