【问题标题】:Split react render methods into a separate npm package将 react 渲染方法拆分为单独的 npm 包
【发布时间】:2019-01-10 13:02:03
【问题描述】:

我们有一个用 React 开发的前端应用程序。 现在我们还想制作一个 react-native 应用程序,但要尽可能重用当前应用程序。

我已经看到在这些情况下经常使用 react-native-web,但是,我们已经开发了我们的应用程序,仅使用 react 并将我们当前的所有组件替换为 react-native-web 组件会花费太多时间(也因为我们使用的是antd组件库)。

我的想法是将所有渲染方法移动到另一个 npm 包(以及需要重写以使用 react-native 的其他部分)并从主包中导入它们。

我想像这样的文件夹结构:

    • 核心 -- 将包含所有可重用的代码
    • web -- 将包含所有带有 antd 组件的渲染方法
    • native -- 将包含所有带有原生组件的渲染方法

通过这种方式,Web 和原生包将各自拥有自己的 npm 启动和构建脚本。 如何在核心包中并基于平台(在本例中为 Web 或本机)从正确的包中导入渲染部分?

我想我可以使用 lerna 库来分离包和 commonjs 或 requirejs 之类的东西,然后动态导入正确的渲染元素。

您能推荐一些其他有用的工具吗? 或者整个方法是错误的,还有另一种方法可以实现吗?

也就是说,我们如何实现从web包到核心包的依赖注入?

想象一下,我们在核心包中有一个组件 Home:

class Home extends Component {
..
    render() {
        // somehow import required 'view'
        // example: import('path_to_web_package_from_some_global_variable' + '/HomeRender.js');
        // There would be a HomeRender.js in 'web' package and another one in 'native' package
    }
..
}

【问题讨论】:

  • 我如何才能在核心包中并基于平台(在本例中为 Web 或原生)从正确的包中导入渲染部分? - 你最好不应该,因为这不允许对未使用的平台进行树摇动。这些是特定于平台的包,应该依赖于core,而不是相反。考虑提供一个简短的示例,这些包是什么以及它们如何协同工作。
  • @estus 摇树未使用平台是什么意思?我只想导入特定于“web”或“native”的代码。我添加了一个小样本,说明它的外观
  • 为什么不直接将组件移动到web/native 都可以访问的文件夹中并从那里导入它们?
  • 我只想导入特定于 'web' 或 'native' 的代码 - 你不能这样做。此外,动态import() 是异步的,它将强制组件异步,这不是一件好事。我会根据我对您的包裹外观的理解尝试给出答案。

标签: javascript reactjs react-native


【解决方案1】:

发布的包应该按照传统方式构建为 ES5,可选择使用 ES6 导出和导入(ESM 构建)以在模块化环境中更有效地使用它。

ES6 导入和导出是静态的。它们不能被交换,至少在构建工具中没有一些模块篡改。虽然动态 import()a proposal 并且不是 ES6 的一部分。这将阻止有效的 tree-shaking,这将导致 webnative 被包含在一个包中,即使其中一个未使用。

应该使用 DI 的一些变体来使 core 与渲染器无关。

其中一种方法是在core 中允许某种插件系统:

import { configureRenderer } from '@foo/core';
import * as renderers from '@foo/web';

configureRenderer(renderers);

这在某些情况下可能会起作用,但它不能很好地与组件一起使用,因为这会阻止 renderers 被摇树并限制 core 使用 native 或 web. This results inweb@ 987654333@nativeor web 在这种情况下可能会起作用,因为平台是互斥的,但总的来说它不是一个好的设计解决方案。

更好的方法是以相反的方式实现这一点。 core 没有依赖关系,包含通用抽象,而webnative 是具体实现。这可以通过类组件的 OOP 继承来解决,并且有很多可能用函数式方法实现 DI,适合这种情况的一种是 render prop(因此得名):

// @foo/core
export class Home extends Component {
    ...
    render() {
        const {render, ...props} = this.props;
        return render(props);
    }
}

// @foo/web
import { Home as HomeWithoutRenderer } from '@foo/core';

const rendererHOC = (Comp, renderer) => props => <Comp render={renderer} ...props />;

export const Home = rendererHOC(HomeWithoutRenderer, props => {
  // platform-specific view
});

【讨论】:

  • 谢谢!这真的很有帮助 :) 它现在正在工作,唯一需要改变的是新的 Home 导出:而不是 'export const Home = rendererHoc(..)' 有 'export const Home = () => rendererHoc( ..)'
  • 很高兴它成功了。有一个错字,rendererHOC 是作为真正的 HOC 的,所以其他的 props 可以传递给它。我更新了。
猜你喜欢
  • 2022-08-17
  • 2018-02-07
  • 2020-12-27
  • 2016-02-07
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
相关资源
最近更新 更多