Ionic - Change import library based on IsDebugMode

2019-07-27 23:26发布

问题:

Can I use IsDebugMode to switch out import libraries?

Can I do:

if (IsDebugMode) {
    import { SQLiteMock, SQLiteObject } from '../mocks/SQLiteMock';
}
else {
    import { SQLite, SQLiteDatabaseConfig, SQLiteObject } from '@ionic-native/sqlite';
}

回答1:

No, unfortunately you can't conditionally import libraries. What you can do though is import both libraries, inject both in your constructor and then conditionally using isDevMode(), (there exists no isDebugMode()) you can use whatever you prefer for each case.

This is not a very good solution though, as it means you will load both libs in memory and since you inject them in the constructor the treeshaking that happens at build time will not ommit them.

If this is done sparingly it might even not make a difference though (negatively). I suggest you benchmark Memory size at runtime using dev tools and if there is significant gain to outweigh the manual nature of the cleaner approach, then just replace the Mock class in the import when you are done developing the feature using that Mock.



回答2:

Even though some plugins may work when running the app on the browser (when using ionic serve), I like to inject a mock version of the plugin, to avoid warnings in the console (or errors being thrown because of cordova.js not being found).

The easiest way I found to do this is by using factories, to decide which implementation of the plugin should be injected. I'll show you a demo using the Keyboard cordova plugin just to keep this short, but the same can be done with any cordova plugin (even if it's not part of Ionic Native).

In my providers folder, I have a custom folder named plugins and inside of that folder I've added a file with the name keyboard.provider.ts. Inside of that file, I've defined the following:

// Ionic
import { Platform } from 'ionic-angular';

// RxJS
import { Observable } from 'rxjs/Observable';

// Ionic native
import { Keyboard } from '@ionic-native/keyboard';

// Browser implementation
export class BrowserKeyboardProvider extends Keyboard {

    public hideKeyboardAccessoryBar(hide: boolean): void {
        console.log(`[Keyboard Browser]: hideKeyboardAccessoryBar set to ${hide}`);
    }

    public show(): void {
        console.log(`[Keyboard Browser]: show`);
    }

    public close(): void {
        console.log(`[Keyboard Browser]: close`);
    }

    public disableScroll(disable: boolean): void {
        console.log(`[Keyboard Browser]: disableScroll set to ${disable}`);
    }

    public onKeyboardShow(): Observable<any> {
        console.log(`[Keyboard Browser]: onKeyboardShow`);
        return Observable.of(true);
    }

    public onKeyboardHide(): Observable<any> {
        console.log(`[Keyboard Browser]: onKeyboardHide`);
        return Observable.of(true);
    }

}

// Mobile implementation
export class MobileKeyboardProvider extends Keyboard { }

// --------------------------------------------------------
// Keyboard factory
//    parameters: dependencies of the target service
//    returns: instance of the service (for real devices or the browser)
// --------------------------------------------------------
export function keyboardFactory(platform: Platform) {
    return platform.is('cordova') ? new MobileKeyboardProvider() : new BrowserKeyboardProvider();
}

// keyboardProvider: used to import the service in the NgModule declaration
export let keyboardProvider =
    {
        provide: Keyboard,
        useFactory: keyboardFactory,
        deps: [Platform]
    };

Like you can see there, I'm just creating two classes that extend the Keyboard class, and in the browser implementation I just write a few things in the console, and return a mocked response. You can do whatever you want inside of those methods.

To know what methods you should mock, you can inspect the index.d.ts file of that plugin.

The mobile implementation does not define anything, so the method from the super class (the Keyboard plugin) will be used.

The most important part of the code is the last past:

export function keyboardFactory(platform: Platform) {
    return platform.is('cordova') ? new MobileKeyboardProvider() : new BrowserKeyboardProvider();
}

// keyboardProvider: used to import the service in the NgModule declaration
export let keyboardProvider =
    {
        provide: Keyboard,
        useFactory: keyboardFactory,
        deps: [Platform]
    };

The keyboardFactory gets an instance of the Platform and we use that instance to check if te app is being executed in the browser, or in a cordova device. Based on that, we return the browser implementation, or the mobile one.

The keyboardProvider just tells angular that it should use the keyboardFactory factory to create instances of the Keyboard class, and that the factory depends on an instance of the Platform to work properly.

Now the only thing left to be done, is to tell the main module (the one in the app.module.tsfile) to use our custom provider.

// Cordova plugins providers
import { keyboardProvider } from '../providers/plugins/keyboard.provider';

@NgModule({
    declarations: [
        MyApp,
        // ...
    ],
    imports: [
        // ...
    ],
    bootstrap: [IonicApp],
    entryComponents: [
        //...
    ],
    providers: [
        // Cordova plugins
        keyboardProvider,  // <-- Use the provider instead of just Keyboard

        // ...
        { provide: ErrorHandler, useClass: IonicErrorHandler }
    ]
})
export class AppModule { }