npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@microup/master

v1.0.0-main-1.0.0.20230823203256

Published

The Master package for the microup platform is the entry to the Micro-frontends code.

Downloads

1

Readme

master

master 包主要负责状态数据的存储,权限校验与重定向、顶层路由处理、多语言等工作。所有的 remote 模块都会经过 master 代码控制然后渲染。

目前未添加权限,多语言模块,如有需要可自行在master添加。

全局的事件触发,通用数据也都可以添加到 master 内,并且这些数据共享,可完成不同模块间数据通信。

store

masterStore 是通过 mobx 进行实现的状态数据池,它可以存储一些全局动态的状态数据,并且在所有 remote 模块中都可以访问到这些数据。

源码

masterStore 的核心代码如下,用于注册一个 Store

import React from 'react';
import {observer, useLocalObservable} from 'mobx-react';
import {runInAction} from 'mobx';
import { Provider } from 'mobx-react';

interface IIssue {
  // data
}

const useMainLocalStore = () => {
  const masterStore = useLocalObservable(() => ({
    data: {} as IIssue,
    get(key: string) {
      return masterStore.data[key]
    },
    set(key, value) {
      runInAction(function () {
        masterStore.data[key] = value
      })
    }
  }));
  return masterStore;
};

const MasterStoreProvider: React.FC<any> = observer((props) => {
  const {children} = props;
  const masterStore = useMainLocalStore();
  return <Provider masterStore={masterStore}>{children}</Provider>;
});

export {MasterStoreProvider};

包裹跟节点

import React from 'react';
……
import {MasterStoreProvider} from './store';
……
const InnerIndex = (props) => {
  const {match, AutoRouter} = props
  return (
    <MasterStoreProvider>
      ……
    </MasterStoreProvider>
  )
};

export default withRouter(InnerIndex);

使用

可以实现不同 remote 模块间数据通信,也可以使得一个模块触发另一个模块的视图渲染。

测试案例如下:

开启一个 app1 服务作为 host 模块,它可以以一下两种方式访问 remote 模块,一种访问其中 remote 模块注册的一个组件,第二种访问整个的 remote 模块,详情也可查看 app1 与 app2 包的代码。

app1 作为 host 模块

import React from 'react';
import {ExternalComponent} from '@microup/utils';
import {inject, observer} from 'mobx-react';

export default inject('masterStore')(observer((props) => {
  const {history, masterStore} = props
  return (
  <div>
    <button onClick={() => {
      masterStore.set('app2Test', !masterStore.get('app2Test'))
    }}>点击改变 app2Test 变量
    </button>
    <h5>引入 app2 externalize 组件</h5>
    <ExternalComponent
      system={{
        scope: 'app2', module: 'App2EC',
      }}
    />
    <h5>全量引入 app2 模块</h5>
    <div style={{borderLeft: "2px black solid",paddingLeft:'8px'}}>
      <ExternalComponent
        {...props}
        match={{...props.master, url: '/app1'}}
        system={{
          scope: 'app2', module: './app2',
        }}
      />
    </div>
  </div>
  )
}))

app2 externalize 组件

import React from 'react';
import {observer,inject} from 'mobx-react';

export default inject('masterStore')(observer(({match, masterStore}) => {
return (<div>app2-externalize-components | app2Test: 	{String(!!masterStore.get('app2Test'))}</div>);
}))
/* externalize: App2EC */

app2 根目录

import React from 'react';
import {Route, Switch} from 'react-router-dom';
import {inject} from 'mobx-react';
import {ErrorPage} from '@microup/master';

const Page1 = React.lazy(() => import('./routes/Page1'));
const Page2 = React.lazy(() => import('./routes/Page2'));

function Index({match, masterStore}) {
  return (
    <Switch>
      <Route path={`${match.url}/page1`} component={Page1}/>
      <Route path={`${match.url}/page2`} component={Page2}/>
      <Route path={`${match.url}`} component={Page1}/>
      <Route path="*" component={ErrorPage}/>
    </Switch>
  );
}

export default inject('masterStore')(Index);

app2 page1

import React from 'react';
import {inject, observer} from 'mobx-react';

export default inject('masterStore')(observer((props) => {
  const {match, masterStore} = props
  return (<div>
    app2 page1 | app2Test: {String(!!masterStore.get('app2Test'))}
  </div>)
}));

master router

moaster router 控制这整个项目的跟路由,路由优先判断是否是/error_page或者/undefined进行全局的错误路由拦截,过滤后的路由会转入 AutoRouter,在 AutoRouter 组件中会处理一级路由并分发到对应 remote 模块

master.jsx

const InnerIndex = (props) => {
  const {match, AutoRouter} = props
  return (
    <MasterStoreProvider>
      <Switch>
        <Route exact path={`${match.url}${match.url === '/' ? '' : '/'}error_page`} component={ErrorPage}/>
        <Route exact path={`${match.url}${match.url === '/' ? '' : '/'}undefined`} component={Empty}/>
        <Route path={match.url} component={AutoRouter}/>
      </Switch>
    </MasterStoreProvider>
  )
};

AutoRouter 组件位置在 routes.nunjucks.jsx 编译后在 tmp 目录中的 router.index.js 中,tmp 详情参考 boot 包文档。

一级路由处理

在 AutoRouter 中执行了这样代码,其作用是优先匹配 config.js 中设置的 routes 中的路由,如果匹配成功,则访问本地路由组件。

routes 作用是一级路由与对应组件位置的描述

# 只是本地开发
routes: {
  a1: './src/exampleMaster/App1', # 路由为 http://192.168.20.133:9091/#/a1/
  a2: './src/exampleMaster/App2' # 路由为 http://192.168.20.133:9091/#/a2/
}
const AutoRouter = () => (
  <Suspense fallback={<Loading/>}>
    <CacheSwitch>
      {'{{ routes }}'}
      <CacheRoute path="*">
        <ExternalRoute />
      </CacheRoute>
    </CacheSwitch>
  </Suspense>
);

如果没有匹配成功,则执行 <ExternalRoute /> 组件,匹配对应 remote 模块。

<ExternalRoute /> 组件中,根据.env中配置的STATIC_URL作为基地址,后边拼接 url 地址中的一级路由与/importManifest.js生成一个新的地址,类似http://192.168.20.101:9095/a1/importManifest.js

然后创建一个新的script标签src 填入上边生成的地址。远程拉取 importManifest.js 代码,实现 remote 模块间连接。(实现方案详情参考动态远程容器

通常我们会有很多 remote 模块,在 host 模块访问多个 remote 模块时,我们不想在代码中把所有地址都写出来( 类似 STATIC_URL-a1,STATIC_URL-a2 这样,会比较麻烦)。这时我可以创建一个独立的 nginx 帮我们代理。

例如所有模块都访问 http://192.168.20.101:9095/a1/importManifest.js ,而 192.168.20.133:9095 中设置 nginx 做对应的反向代理

# STATIC_URL 对应的 nginx
location /a1/ { # app1 remote 模块
	proxy_pass http://192.168.20.101:9091/;
}
location /a2/ { # app2 remote 模块
	proxy_pass http://192.168.20.123:9092/;
}

这样我们访问 http://192.168.20.101:9095/a1/importManifest.js 他会根据一级路由 a1 判断访问 http://192.168.20.101:9091/a1/importManifest.js 然后拿到 js 静态文件做对应渲染。