preface

In the framework planning, it was mentioned that some common functions of the framework need to support calls in the H5 environment, that is, multi-platform support of the API needs to be implemented

Why multiple platforms? The core is still reuse code, such as in wechat, in Dingpin, in quick container, if there is no multi-platform support, then quick.ui. Alert can only be used in quick container, dingpin and wechat will have to use other code respectively, code reuse rate is low, if the realization of multi-platform support. The code for the same function on all three platforms is the same.

What kind of multi-platform support

Of course, this framework implements multi-platform support and the general framework is a bit different.

A framework that supports multiple platforms is more of a polyfill, for example

// Assume that h5 was not previously supported
const oldToast = quick.ui.toast;

quick.ui.toast = function(.) {
    if (os.h5) {
        // Do something h5 does. }else {
        oldToast(...);
    }
};
Copy the code

This is the shim implementation, if it’s a new environment, use the new implementation, otherwise use the old implementation

The multi-platform implementation of this framework is directly built into the framework core __, that is, the framework itself supports multi-platform API Settings

quick.extendModule('ui'[{namespace: 'toast'.os: ['h5'].defaultParams: {
        message: ' ', }, runCode(... rest) {// Define the practices in the H5 environment. }},... ] ; quick.extendModule('ui'[{namespace: 'toast'.os: ['quick'].defaultParams: {
        message: ' ', }, runCode(... rest) {// Define the actions in the Quick environment. }},... ] ;Copy the code

When defining apis within the framework, instead of quick. UI. Alert = XXX directly, a specific API is used to define the implementation separately for an environment

Furthermore, each API defined in the framework is implemented in a Quick, H5 environment.

The core of multi-platform support

As can be seen from the above introduction, multi-platform support is mainly the implementation of the front end, and the native API, which can only be counted as the implementation of one environment

The core is to override set and GET based on: Object.defineProperty

Object.defineProperty(apiParent, apiName, {
    configurable: true.enumerable: true.get: function proxyGetter() {
        // We need to return the corresponding content according to different environment. },set: function proxySetter() {
        // You can be prompted not to modify the API}});Copy the code

The framework of multi-platform implementation code can refer to the source code, here is not redundant, below will introduce how to simply implement a multi-platform support API

Implement a multi-platform support API

Let’s presuppose the final result:

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

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

quick.ui.alert = 11; // Tip: Quick API is not allowed to be modified
Copy the code

So what should be done to achieve the above requirements?

Write a prototype

The simplest is to assume that these implementations already exist and return them directly based on 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() {
        // We need to return the corresponding content according to different environment
        if (quick.os.quick) {
            return alertQuick;
        } else {
            returnalertH5; }},set: function proxySetter() {
        // You can be prompted not to modify the API
        alert('Quick API is not allowed to change'); }});Copy the code

So, the result of this call is

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

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

quick.ui.alert = 11; // Tip: Quick API is not allowed to be modified
Copy the code

Although the effect is the same as preset, but obviously needs to be improved

Add ways to extend the API

Extensions are defined as follows

const quick = {};

quick.os = {
    quick: false};Proxyapi [namespace][OS] */ proxyAPI [namespace][OS
const proxysApis = {};
// All supported environments
const supportOsArray = ['quick'.'h5'];

function getCurrProxyApiOs(currOs) {
    for (let i = 0, len = supportOsArray.length; i < len; i += 1) {
        if (currOs[supportOsArray[i]]) {
            returnsupportOsArray[i]; }}// The default is h5
    return 'h5';
}

// Get quick.ui.alert
function getModuleApiParentByNameSpace(module, namespace) {
    let apiParent = module;
    // Only the parent of the namespace, if only XXX, there is no parent
    const parentNamespaceArray = / [.] /.test(namespace) ? namespace.replace(/ [.] [^.] + $/.' ').split('. ') : [];

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

    return apiParent;
}

function proxyApiNamespace(apiParent, apiName, finalNameSpace) {
    // Proxy API, Proxy apiName from apiParent to Proxy execution
    Object.defineProperty(apiParent, apiName, {
        configurable: true.enumerable: true.get: function proxyGetter() {
            // Make sure the function you get is executable
            const nameSpaceApi = proxysApis[finalNameSpace];

            // Get the current environment, get the corresponding environment proxy object
            return nameSpaceApi[getCurrProxyApiOs(quick.os)] || nameSpaceApi.h5;
        },
        set: function proxySetter() {
            alert('Quick API is not allowed to change'); }}); }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);
    // The final namespace contains modules
    const finalNameSpace = moduleName + '. ' + apiNamespace;
    // if only XXX, select XXX; if aa.bb, select bb
    const apiName = / [.] /.test(apiNamespace) ? api.namespace.match(/ [.] [^.] + $/) [0].substr(1) : apiNamespace;

    // Use proxysApis[finalNameSpace] instead of apiParent[apiName]
    if(! proxysApis[finalNameSpace]) {// If you do not already have a namespace for the proxy API, you only need to set up the proxy once
        proxyApiNamespace(apiParent, apiName, finalNameSpace);
    }

    // A new API proxy replaces the contents of the previous API namespace
    const apiRuncode = api.runCode;
    const oldProxyNamespace = proxysApis[finalNameSpace] || {};

    proxysApis[finalNameSpace] = {};

    supportOsArray.forEach((osTmp) = >{
        if(api.os && api.os.indexOf(osTmp) ! = =- 1) {
            // If the OS exists and is valid, redefine it
            proxysApis[finalNameSpace][osTmp] = apiRuncode;
        } else if (oldProxyNamespace[osTmp]) {
            // Otherwise, use the old version of the agentproxysApis[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;
Copy the code

To add some complexity to the code above, there is a pool that manages all proxy calls uniformly and then updates the proxies for the environment each time

Based on the above approach, the API for the environment can be extended as follows

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); }}]);Copy the code

The final call is as follows (as expected)

quick.os.quick = true;
quick.ui.alert('hello'); // quick-hello
quick.os.quick = false;
quick.ui.alert('hello'); // h5-hello
quick.ui.alert = 11; // Tip: Quick API is not allowed to be modified
Copy the code

While this kind of extension may seem complicated for just one or two apis, it has its advantages when there are many apis, especially when batch preprocessing is required (default parameters, Promise support, etc.)

Application of multi-platform support in Quick

The Quick Hybrid framework supports quick and H5 environments by default, and the core code is listed above (of course, there are some internal proxies, default parameter handling, etc., which is slightly more complicated).

Based on this core, the framework definition and API definition can then be packaged separately

quick.js
quick.h5.js
Copy the code

In addition, if you add a new environment (such as DD), you just need to add another environment extension package, which is written in the same way, so that it is easy to maintain

Back to root

  • How to implement a Hybrid framework

The source code

Github implements the framework

quickhybrid/quickhybrid