前端微服务化进阶3 - 多模块共享公共组件

前端微服务化之后,我们会面临一个问题: 模块之间重复代码不能复用的问题.

如果使用npm管理我们的重复代码,我们会多出维护npm包的成本. 在子模块更新npm包版本,也是一件很麻烦的事情. 在js文件体积上也没有任何的优化.

组件共享

今天我们就来聊一聊如何在多个模块中同时使用一个组件.

思路

在base模块管理公共组件,将组件封装成动态组件,这样在打包的时候我们就可以将该组件切割成单独文件了. 当其他的子模块需要这个组件的时候,向Base模块动态获取.

实践

动态组件的封装

为了让其他模块可以按需加载我们的公共组件,我们需要对已有的组件封装成动态组件.

我这里使用的是 umi/dynamic,

他是基于https://github.com/jamiebuilds/react-loadable 封装了一层. 有兴趣的小伙伴可以自行了解.

import React from 'react';
import dynamic from 'umi/dynamic';
import PageLoading from '@/components/PageLoading'

export const Demo = dynamic(import( `../Demo`), {loading: () => <PageLoading />})

export default Demo;

对外提供获取动态组件的方法

在加载Base模块的时候,我们可以在window下暴露一个调用该模块动态组件的方法

window.getDynamicComponent = async function(name) {
  let component = null;
  component = await import(`@/components/dynamic/${name}`);
  return component[name];
};

子模块调用公共组件

因为base模块提供了一个获取公共组件的全局方法, 我们就可以在任何模块任何需要调用公共组件的地方去是使用它了.

    // 获取组件
   let component =  await window.getDynamicComponent('Demo')

为了方便这种公共组件的使用,我们可以将这一方法封装成一个组件. 在调用公共组件,我们只需要声明就好了.

import React, { Component } from 'react';

// Matrix 在黑客帝国中是母体的意思
export default class Matrix extends Component {
  state = {
    DynamicComponent: null
  }

  static defaultProps = {
    $name: ""
  }

  async componentWillMount() {
    this.renderComponent()
  }

  componentWillReceiveProps(nextProps) {
    this.props = nextProps
    this.renderComponent()
  }

  async renderComponent() {
    const { $name } = this.props;
    try {
      if ($name) {
        const component = await window.getDynamicComponent($name)
        this.setState({
          DynamicComponent: component
        })
      }
    } catch (error) {
      this.setState({
        DynamicComponent: null
      })
      console.error(error)
    }
  }


  render() {
    const { DynamicComponent } = this.state;
    // 继承所有props
    return DynamicComponent && <DynamicComponent {...this.props} />;
  }
}

在实际页面中调用我们的公共组件

import React, { Component } from 'react';
import Matrix from '@/components/Matrix'

export default class Page extends Component {
  render() {
    return (
      <div>
        <Matrix name="Demo" />
      </div>
    );
  }
}

尾巴

实现这样的功能,其实非常简单. 但是因为如此,我们需要对公共组件的颗粒度要有一些把控. 不然我们可能会遇到一个页面会加载文件过多的问题,随之会影响页面加载速度. 当然我们也可以通过现有的网络技术去改善这种状况.

有了这一思路,我们或许可以在我们的子模块中不引用任何的其他组件, 依赖我们的base模块提供的公共组件.我们就可以完成页面的开发.

前端微服务化的可玩性非常的高.而且还有更多的玩法没有挖掘出来.

相关系列文章

https://alili.tech/tags/microfrontend/

本文链接:

https://alili.tech/archive/qh7x5i5szfh/