1. Background

Hippy provides a Navigator component for page navigation and jumping.

However, the Navigator component has major limitations. It is implemented by starting a new Instance of Hippy. Under 2.0, instances may not be able to communicate with each other.

The functions of @hippy/ React and @hippy/ react-Web Navigator components are relatively lacking, and neither of them has a good function to realize page hopping. There are also differences in functionality between the two ends, making it impossible to achieve native and Web isomorphism

Here’s how the Navigator component in @hippy/react and @hippy/ react-Web is implemented

1.1 @hippy/reactRouting implementation

In the Navigator component, render displays by instantiating a Hippy instance while listening for Android’s back key

// https://github.com/Tencent/Hippy/blob/312d0d963cac2d8cf60ff97ddd554a01e575cea0/packages/hippy-react/src/components/navig ator.tsx#L125
constructor(props: NavigatorProps) {
    super(props);
    const { initialRoute } = props;
    if (initialRoute && initialRoute.component) {
      const hippy = new Hippy({
        appName: initialRoute.routeName,
        entryPage: initialRoute.component,
      });

      hippy.regist();

      this.routeList[initialRoute.routeName] = true;
    }
    this.handleAndroidBack = this.handleAndroidBack.bind(this);
}
Copy the code

The page returns by calling native callUIFunction

public pop(option: { animated: boolean }) {
    if (this.stack.size > 1) {
      const options = [option];
      this.stack.pop();
      callUIFunction(this.instance, 'pop', options); }}Copy the code

In this way, the two pages cannot communicate with each other or transfer data

1.2 @hippy/react-webRouting implementation

In contrast to @hippy/react, the Navigator component in @hippy/react- Web has no corresponding implementation function

//https://github.com/Tencent/Hippy/blob/master/packages/hippy-react-web/src/components/navigator.tsx
/* eslint-disable class-methods-use-this */

import React from 'react';
import { formatWebStyle } from '.. /adapters/transfer';

/** * Simply router component for switch in multiple Hippy page. * @noInheritDoc */
class Navigator extends React.Component {
  pop() {
    // TODO
  }

  push() {
    // TODO
  }

  render() {
    const { style } = this.props;
    const newProps = Object.assign({}, this.props, {
      style: formatWebStyle(style),
    });
    return (
      <div {. newProps} / >
    );
  }
}

export default Navigator;
Copy the code

2. Hippy project routing implementation

React-router is used to manage multiple pages and switch between Hippy native and Web pages

2.1 Hippy Router Selection

In React, react-Router is used for page switching, supporting multi-page development. There is also a native version of React -router-native

React-router-native is the native version of the React-Router, but it is based on the more complete Navigator component in React-Native. After analysis and implementation, react-router-native cannot be used directly in Hippy

MemoryRouter in React-Router, a route based on a pure JS implementation, does not need to rely on urls, which makes it applicable to native applications

The following is a description of MemoryRouter

A <Router> that keeps the historyOf your "URL"in memory (does not read or write to the address bar). Useful in tests and non-browser environments like React Native.
Copy the code

Therefore, react-Router can support switching between native and Web pages at the same time for multi-page development

2.1 React-Router used in Hippy

  1. throughPlatform.OSDetermine the current platform
  2. Used in native projectsMemoryRouter, used in the WebHashRouter
  3. throughreact-routerSwitch between multiple pages

Here’s how to use the React-router in Hippy

import React, { Component } from 'react';
import {
  StyleSheet,
  View,
} from '@hippy/react';
import {
  MemoryRouter,
  HashRouter,
  Route,
} from "react-router-dom";

import routes from './route';
import { ISWEB } from './utils';


export default class App extends Component {

  render() {
    const Router = ISWEB ? HashRouter : MemoryRouter;
    return (
      <View>
        <Router>
          {
            routes.map(item => {
              return (
                <Route key={item.path} exact path={` ${item.path} `} ><item.component meta={item.meta| | {}} / ></Route>); })}</Router>
      </View>); }}Copy the code

3. The hippy-React three-terminal isomorphic router is used

3.1 the use ofreact-routerExisting problems

The React-Router can solve multiple page hops in Hippy at a certain level, but it has some problems

  1. Native switching has no animation and the experience is the same as on the Web
  2. Unable to use the React-router-transition animation
  3. Native return operation, directly back to closehippyproject
  4. LinkThe process of using the need to pass incomponent. The reason is thatLinkComponent defaultaThe labelAnd thehippyDo not supportaThe label
// How to use Link in hippy
import { View } from '@hippy/react';

<Link to="/about" component={View}>About</Link>

Copy the code

3.2 Compatibility with page switching

Page switching in A Hippy project In addition to page switching in a Hippy project, there is also interaction with a client or browser

To switch from a Hippy page to a native page on the client, the client needs to provide the support for forwarding to a pseudo protocol. In a Web environment, basic browser capabilities are required. Therefore, compatibility processing is required

There are three main scenarios for page switching in hippy projects

scenario handling
hippyWithin the project react-router
hippy– > native Native pseudo protocol support
hippy– > web page window.locationorwindow.open

3.2.1 Compatibility with page switching

  • The principle of analysis

The react-router uses the Context to pass functions to jump routes, such as goback and push, to the component

When a component needs to use the React-Router function, it uses the withRouter high-order component to inject the route redirect function into the component

// withRouter usage
// https://reacttraining.com/react-router/web/api/withRouter
import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
class ShowTheLocation extends React.Component {
  static propTypes = { // Declare propTypes to get the router method
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  };
}

const ShowTheLocationWithRouter = withRouter(ShowTheLocation);
Copy the code

Implementation principle of withRouter

// https://github.com/ReactTraining/react-router/blob/402ecabdc94e5aeb657c593d8af200625a09cdfe/packages/react-router/module s/withRouter.js#L11
<RouterContext.Consumer>
    {context => {
      return (
        <Component
          {. remainingProps}
          {. context}
          ref={wrappedComponentRef}
        />
      );
    }}
 </RouterContext.Consumer>
Copy the code

WithRouter’s source code analysis shows that context contains all of the router’s methods for components to use, so you can personalize the context layer according to different platforms

  • The solution

Hijacking the context by implementing withRouter’s logic

import { Platform } from '@hippy/react';
import { withHippyHistory } from './history.hippy';
import { withWebHistory } from './history.web';

const ISWEB = Platform.OS === 'web';

const wrapper = ISWEB ? withWebHistory : withHippyHistory
  return (
    <Component
      {. remainingProps}
      {. wrapper(context)}
      ref={wrappedComponentRef}
    />
);
Copy the code
  1. In the terminal, override the route jump function to call the natively provided jump method
// history.hippy.js
import { callNativeWithPromise } from "@hippy/react";
import { parsePath } from './util';
const createHook = (history, ) = > {
	function push (path, state) {}
	function replace () {}
	function go () {}
	function goBack () {}
	function goForward () {}
    function open ({ hippyUrl }) {
		return callNativeWithPromise('TgclubJsModule'.'callNativeAction', hippyUrl)
	}
	return { push, go, goBack, goForward, replace, open }
}

export function withHippyHistory (context) {
	const { history } = context;
	return {
	  ...context,
	  history: {... history, ... createHook(history) } } }Copy the code
  1. Provide additionalopenFunction to jump to pages that are not part of the project, so that there is no awareness at the business layer.
// history.web.js
const createHook = (history) = > {
    function open ({ webUrl, config }) {
		if (webUrl) {
			window.open(webUrl); }}return { open }
}
export function withWebHistory (context) {
	const { history } = context;
	return {
		...context,
		history: {... history, ... createHook(history) } } }Copy the code

Through the transformation of functions in context, unified page switching call method of different platforms. Inside the Wrapper is a special processing for the platform

4. Legacy issues

  1. Page switching animation
  2. hippyThe in-project page jump ADAPTS the system to return to the previous page action
  3. replaceOperation requires terminal cooperation and maintenance of page routing stack