用 ReactXP

加速跨平台開發

黃 胤翔

Ben

  • Web 全端開發
  • 趨勢科技
  • CTJS Conf Staff
  • React Native Taiwan
  • https://github.com/hinxcode

Slides Link

slides.com/hinx/reactxp

OUTLINE

  1. Introduction
  2. Why We Need ReactXP
  3. Demo
  4. Dive into ReactXP
  5. Other Solutions
  6. Conclusion

25 mins

身為一個

Web Developer

一個會 React

的前端工程師

Learn once,

write anywhere

View maps directly to the native view equivalent on whatever platform React Native is running on, whether that is a UIView, <div>, android.view, etc.

https://facebook.github.io/react-native/docs/view.html

Is it possible compatible with Web?

Android

iOS

Web

Non-Platform

Specific

A Library For Building Cross-Platform Apps

ReactXP

ReactXP

X-Platform

https://microsoft.github.io/reactxp/docs/faq.html

ReactXP builds upon React Native. ReactXP’s components and APIs are inspired by React Native.

AppRegistry

import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('MyApp', () => App);

https://github.com/Microsoft/reactxp/blob/master/samples/hello-world-js/src/index.js

https://github.com/Microsoft/reactxp/blob/master/src/native-common/App.ts

import React from 'react';
import RX from 'reactxp';
import App from './App';

RX.initialize(true, true);
RX.UserInterface(<App />);
import RN from 'react-native';

...

initialize(debug: boolean, development: boolean) {
    super.initialize(debug, development);
    window['rxdebug'] = debug;
    RN.AppRegistry.registerComponent('RXApp', () => RootView);
}

...

API

Props

Styles

Attributes

Animation Interface

Components

Android

iOS

Web

React Native

ReactXP

React Native

ReactXP

Android

iOS

Web

React

Webpack

Platform Support

  • iOS (React Native)

  • Android (React Native)

  • Web (React)

  • Desktop (Electron)

Browser Support

  • Chrome

  • IE (9 and newer)

  • Edge

  • Firefox

  • Safari

  • Other HTML5 browsers

DEMO

https://github.com/hinxcode/coscup-schedule-app

iOS App

Android App

Mobile Web

Desktop Web

Live Demo

跨三平台  成功!

ReactXP Overview

https://github.com/Microsoft/reactxp

https://microsoft.github.io/reactxp

Repo

Docs

Use ReactXP

Without TS ?

Components

  • ActivityIndicator
  • Button
  • GestureView
  • Image
  • Link
  • Navigator
  • Picker
  • ScrollView
  • Text
  • TextInput
  • View
  • WebView

ActivityIndicator

https://github.com/Microsoft/reactxp/blob/master/src/native-common/ActivityIndicator.tsx

return (
    <RN.ActivityIndicator
        animating={ this.state.isVisible }
        color={ this.state.isVisible ? this.props.color : 'transparent' }
        size={ size }
    />
);

GestureView

  • Tapping

  • Double-tapping

  • Panning

  • Pinching

Navigator

  • Stack

  • cardStyle

  • transitionCompleted()

  • renderScene()

Picker

  • items

  • onValueChange

Styles

const myViewStyle = RX.Styles.createViewStyle({
    backgroundColor: '#fff'
});


<RX.View style={myViewStyle} />

createXXXStyle()

/*
    { backgroundColor: '#fff' } <-- ruleSet
*/

createViewStyle(ruleSet: Types.ViewStyle, cacheStyle: boolean = true): Types.ViewStyleRuleSet {

    return this._adaptStyles(ruleSet, cacheStyle);
}

https://github.com/Microsoft/reactxp/blob/master/src/native-common/Styles.ts#L71

Types

export interface ViewStyle extends ViewAndImageCommonStyle {
    borderStyle?: 'solid' | 'dotted' | 'dashed' | 'none';
    ...
}

export interface ViewAndImageCommonStyle extends FlexboxStyle, TransformStyle {
    borderWidth?: number;
    borderColor?: string;
    borderRadius?: number;
    ...
}

export interface FlexboxStyle {
    ...
}

https://github.com/Microsoft/reactxp/blob/master/src/common/Types.ts

createViewStyle(ruleSet: Types.ViewStyle, cacheStyle: boolean = true): Types.ViewStyleRuleSet {

    return this._adaptStyles(ruleSet, cacheStyle);
}

_adaptStyles()

private _adaptStyles<S extends Types.ViewAndImageCommonStyle>(def: S, cacheStyle: boolean): Types.StyleRuleSet<S> {
    let adaptedRuleSet = def as ReactNativeViewAndImageCommonStyle<S>;

    ...

    // Convert text styling
    let textStyle = adaptedRuleSet as Types.TextStyle;
    if (textStyle.font) {
        if (textStyle.font.fontFamily !== undefined) {
            textStyle.fontFamily = textStyle.font.fontFamily;
        }
        if (textStyle.font.fontWeight !== undefined) {
            textStyle.fontWeight = textStyle.font.fontWeight;
        }
        if (textStyle.font.fontStyle !== undefined) {
            textStyle.fontStyle = textStyle.font.fontStyle;
        }
        delete textStyle.font;
    }

    if (def.flex !== undefined) {
        var flexValue = def.flex;
        delete adaptedRuleSet.flex;
        if (flexValue > 0) {
            // p 1 auto
            adaptedRuleSet.flexGrow = flexValue;
            adaptedRuleSet.flexShrink = 1;
        } else if (flexValue < 0) {
            // 0 -n auto
            adaptedRuleSet.flexGrow = 0;
            adaptedRuleSet.flexShrink = -flexValue;
        } else {
            // 0 0 auto
            adaptedRuleSet.flexGrow = 0;
            adaptedRuleSet.flexShrink = 0;
        }
    }

    ...

   return adaptedRuleSet;
}

https://github.com/Microsoft/reactxp/blob/master/src/native-common/Styles.ts#L135

cacheStyle

let dynamicViewStyle = RX.Styles.createViewStyle({
    backgroundColor: userColor
}, false);
createViewStyle(ruleSet: Types.ViewStyle, cacheStyle: boolean = true): Types.ViewStyleRuleSet {

    return this._adaptStyles(ruleSet, cacheStyle);
}

Animations

Animated Value

constructor(props) {
    ...

    this._translationValue = new RX.Animated.Value(-100);
}

Animated Styles

constructor(props) {
    ...

    this._animatedStyle = RX.Styles.createAnimatedTextStyle({
        transform: [{
            translateY: this._translationValue
        }]
    });
}

render() {
    <RX.Animated.Text style={ this._animatedStyle }>
        Hello World
    </RX.Animated.Text>
}

Timing Animations

constructor(props) {
    ...

    this._translationValue = new RX.Animated.Value(-100);
}

componentDidMount() {
    let animation = RX.Animated.timing(this._translationValue, {
            toValue: 0,
            easing: RX.Animated.Easing.OutBack(),
            duration: 500
        }
    );

    animation.start();
}

Composite Animations

let compositeAnimation = RX.Animated.parallel([
    RX.Animated.timing(animatedScaleValue,
        { toValue: 0.0, duration: 250, easing: RX.Animated.Easing.InOut() }
    ),
    RX.Animated.timing(animatedOpacityValue,
        { toValue: 1.1, duration 250, easing: RX.Animated.Easing.Linear() }
    )
]);

compositeAnimation.start();

Sometimes it’s useful to execute multiple animations in parallel or in sequence.

import RN = require('react-native');

...

export var Animated = {
    ...
    delay: RN.Animated.delay,
    parallel: RN.Animated.parallel,
    sequence: RN.Animated.sequence
};

https://github.com/Microsoft/reactxp/blob/master/src/native-common/Animated.tsx#L194

Composite Animations

APIs

  • Accessibility
  • Alert
  • App
  • Clipboard
  • Input
  • International
  • Linking
  • Location
  • Modal
  • Network
  • Platform
  • Popup
  • StatusBar
  • Storage
  • UserInterface
  • UserPresence

App

/* Methods */
initialize(debug: boolean, development: boolean): void;

getActivationState(): AppActivationState;

...

https://microsoft.github.io/reactxp/docs/apis/app.html

import React from 'react';
import RX from 'reactxp';
import App from './App';

RX.App.initialize(true, true);
RX.UserInterface.setMainView(<App />);

UserInterface

/* Methods */
setMainView(element: React.ReactElement<any>): void;

useCustomScrollbars(enable: boolean): void;

isHighPixelDensityScreen(): boolean;

getContentSizeMultiplier(): SyncTasks.Promise<number>;

getMaxContentSizeMultiplier(): SyncTasks.Promise<number>;

setMaxContentSizeMultiplier(maxContentSizeMultiplier: number): void;

dismissKeyboard(): void;

isNavigatingWithKeyboard(): boolean;

...

https://microsoft.github.io/reactxp/docs/apis/userinterface.html

Linking

/* Types */
interface SmsInfo {
    phoneNumber?: string;
    body?: string;
}

interface EmailInfo {
    to?: string[];
    cc?: string[];
    bcc?: string[];
    subject?: string;
    body?: string;
}

interface LinkingErrorInfo {
    code: LinkingError;
    url: string;
    error?: string;
}

enum LinkingErrorCode {
    NoAppFound = 0,
    UnexpectedFailure = 1,
    Blocked = 2,
    InitialUrlNotFound = 3
}

/* Methods */
openUrl(url: string): SyncTasks.Promise<void>;

launchSms(smsData: SmsInfo): SyncTasks.Promise<void>;

launchEmail(emailData: EmailInfo): SyncTasks.Promise<void>;

...

https://microsoft.github.io/reactxp/docs/apis/linking.html

Network

/* Types */
enum DeviceNetworkType {
    Unknown,
    None,
    Wifi,
    Mobile2G,
    Mobile3G,
    Mobile4G
}

/* Methods */
isConnected(): SyncTasks.Promise<boolean>;

getType(): SyncTasks.Promise<DeviceNetworkType>;

/* Events */
connectivityChangedEvent: SubscribableEvent<(isConnected: boolean) => void>;

https://microsoft.github.io/reactxp/docs/apis/network.html

Platform

/* Types */
type PlatformType = 'web' | 'ios' | 'android' | 'windows';

/* Methods */
getType(): Types.PlatformType;

https://microsoft.github.io/reactxp/docs/apis/platform.html

UserPresence

/* Methods */
isUserPresent(): boolean;

/* Events */
userPresenceChangedEvent: SubscribableEvent<
    (isPresent: boolean) => void>();

https://microsoft.github.io/reactxp/docs/apis/userpresence.html

Extensions

  • ImageSvg

  • Video

  • VirtualListView

Primitive Extensions

import RXVideoPlayer from 'reactxp-videoplayer';

class MyVideoPanel extends RX.Component<null, null> {
    render() {
        return (
            <RXVideoPlayer
                source={ this.props.source }
                showControls={ true }
                onProgress={ this._onVideoProgress }
                onEnded={ this._onVideoEnded }
            />
        );
    }
}

Higher-level

Component

Contain no platform-specific code but build upon the lower-level primitives

Other Solutions

react-web

https://github.com/taobaofed/react-web

react-primitives

https://github.com/lelandrichardson/react-primitives

react-native-web

https://github.com/necolas/react-native-web

ReactXP Project Generator

https://github.com/react-native-training/create-xp-app

https://www.facebook.com/groups/reactnativetw

References

  • https://microsoft.github.io/reactxp/
  • https://facebook.github.io/react-native/
  • https://blog.souche.com/react-native-source-code-analysis/
  • http://www.infoq.com/cn/articles/react-native-web

Thanks for Listening

ReactXP

By hinx

ReactXP

2017 COSCUP

  • 1,458