hippy-react 三端同构 — 路由

1. 背景介绍

Hippy 提供了 Navigator 组件,用于页面导航、跳转。

但是 Navigator组件有比较大的局限性, 该组件通过启动一个新的 Hippy 实例实现,在 2.0 下实例之间可能无法互相通信,iOS 上也必须作为根节点包裹所有子组件,使用有很大限制。

@hippy/react 以及 @hippy/react-web 中的 Navigator 组件功能相对比较欠缺,两端都没有比较好的实现页面跳转的功能。两端的功能也存在着差异,导致无法实现原生和web的同构

以下是 @hippy/react@hippy/react-web 中的 Navigator 组件的实现方式

1.1 @hippy/react 路由实现

Navigator 组件中,通过实例化一个 Hippy 实例进行渲染展示,同时对 Android 的回退键进行监听

// https://github.com/Tencent/Hippy/blob/312d0d963cac2d8cf60ff97ddd554a01e575cea0/packages/hippy-react/src/components/navigator.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);

}

通过调用原生 callUIFunction 执行页面返回

public pop(option: { animated: boolean }) {

    if (this.stack.size > 1) {

      const options = [option];

      this.stack.pop();

      callUIFunction(this.instance, 'pop', options);

    }

}

这种方式,两个页面之间无法进行数据互通,也无法传递数据

1.2 @hippy/react-web 路由实现

相比于 @hippy/react@hippy/react-web 中的 Navigator 组件则没有对应的实现功能

//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;

2. hippy项目路由实现

使用 react-router 来管理多页面,实现 Hippy 原生和web的多页面切换

2.1 hippy router选择

react 中,主要是由 react-router 来进行页面切换,支持多页面开发。同时也有native版的 react-router-native

react-router-nativereact-router 的native版本,但是其基于 react-native 中比较完善的 Navigator 组件。经过分析和实现,无法在 Hippy 中直接使用 react-router-native

react-router 中的 MemoryRouter,基于纯js实现的路由,不需要依赖于 URL,这使得其可以应用在native

下面这个是关于 MemoryRouter 描述

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

因此使用 react-router 可以同时支持原生和web页面切换,进行多页面开发

2.1 hippy中react-router使用

  1. 通过 Platform.OS 对当前平台进行判断
  2. 在原生项目中使用 MemoryRouter, 在web中使用 HashRouter
  3. 通过 react-router 对多页面进行切换

以下是 hippyreact-router 的使用方式

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>

    );

  }

}

3. hippy-react三端同构router使用

3.1 使用 react-router 存在的问题

react-router 能够在一定层度上解决 hippy 中多页面跳转的功能,是也存在一些问题

  1. 原生切换没有动画,体验与web的一样
  2. 无法使用 react-router-transition 动画
  3. 原生的返回操作,直接回关闭 hippy 项目
  4. Link 的使用过程中,需要传入 component。 原因是 Link 组件默认使用 a 标签,而 hippy 中不支持 a 标签
// hippy 中 Link的使用方式

import { View } from '@hippy/react';



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

3.2 页面切换兼容

hippy 项目中页面切换除了项目中的页面切换,还存在着与客户端或者浏览器的交互

hippy 页面切换到客户端原生页面,需要客户端提供伪协议跳转支持。在web环境下,需要使用浏览器基础能力。因此需要进行兼容处理

hippy 项目中的页面切换主要有一下三种场景

场景 | 处理方式

---|---

hippy 项目内 | react-router

hippy -> 原生 | 原生伪协议支持

hippy -> web页面 | window.location 或者 window.open

3.2.1 页面切换兼容

* **原理分析**

react-router 通过 Context 将跳转路由的函数, 如 goback, push,传递给组件

当组件需要使用到 react-router 功能时,通过 withRouter 高阶组件,向组件注入路由跳转函数

// withRouter 使用方式

// https://reacttraining.com/react-router/web/api/withRoute

import React from "react";

import PropTypes from "prop-types";

import { withRouter } from "react-router";

class ShowTheLocation extends React.Component {

  static propTypes = { // 声明propTypes,从而获取router方法

    match: PropTypes.object.isRequired,

    location: PropTypes.object.isRequired,

    history: PropTypes.object.isRequired

  };

}



const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

withRouter 实现原理

// https://github.com/ReactTraining/react-router/blob/402ecabdc94e5aeb657c593d8af200625a09cdfe/packages/react-router/modules/withRouter.js#L11

<RouterContext.Consumer>

    {context => {

      return (

        <Component

          {...remainingProps}

          {...context}

          ref={wrappedComponentRef}

        />

      );

    }}

 </RouterContext.Consumer>

withRouter 的源码分析来看,其中 context 包含了 router 所有的方式,提供给组件使用,因此可以在 context 这一层,按照不同的平台,进行个性化处理

* **解决方案**

通过实现 withRouter 的逻辑,在 context 进行劫持处理

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}

    />

);
  1. 在终端中,重写路由跳转函数,调用原生提供的跳转方法
// 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)

        }

    }

}
  1. 提供额外的 open 函数,用于跳转到非本项目的页面,使得在对业务层无感知。
// 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)

        }

    }

}

通过对 context 中函数的改造,统一不同平台的页面切换调用方式。而在 wrapper 里面针对平台进行特殊处理

4. 遗留问题

  1. 页面切换动画
  2. hippy 项目内页面跳转适配系统返回上一页动作
  3. replace 操作需要终端配合,维护页面路由栈
本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
hippy-react 三端同构 — 路由
但是 Navigator组件有比较大的局限性, 该组件通过启动一个新的 Hippy 实例实现,在 2.0 下实例之间可能无法互相通信,iOS 上也必须作为根节点...
<<上一篇
下一篇>>