Skip to content

子系统接入控制台框架指南

jsers edited this page Nov 3, 2020 · 5 revisions

目的

目的是要确保控制台的框架在各个系统下是一致的,用户在切换系统的时候感觉是在一个平台。

微前端

1、包装入口
基于 single-spa 需要 single-spa 包装入口文件,具体参考 single-spa 文档 domElementGetter: () -> document.getElementById('ecmc-layout-container')

ecmc-layout-container 是所有子系统 renderTo 的元素

2、配置 output

  • output.filename = '[name]-[chunkhash].js' // 系统入口文件必须是 systemIdent-xxxxxx.js 格式
  • output.publicPath = /${systemIdent}/
  • output.library = systemIdent
  • output.libraryTarget = 'amd'

3、添加 module rule

webpackConfig.module.rules.unshift({
    parser: { system: false },
});

3、配置 publicPath
本地环境和独立部署的线上环境,这里的值须为服务部署对应的绝对路径
线上环境和 uic 等部署在一个环境,则这里为 /${子系统标识}/ 格式的相对路径

4、输出 manifest 文件
受限于目前的设计 manifest 需要有下面的约束
使用 stats-webpack-plugin 生成统计信息,文件名必须是 manifest.json

// 参考代码
webpackConfig.plugins.push(
  new StatsPlugin(
    manifestName: 'manifest.json',
    {
        chunkModules: false,
        source: true,
        chunks: false,
        modules: false,
        assets: true,
        children: false,
        exclude: [/node_modules/]
    }
  )
)

系统注册

<feRepoRoot>/layout-web/static/systemsConfig.json

运行开发环境

1、注册系统
2、先运行布局框架(layout-web)
3、运行子系统

子系统独立运行

"无头" 模式下没有认证登录相关逻辑,可以在 webpack devServer 里加入以下代码,在运行开发环境阶段获取登录状态。

devServer: {
  ...
  before: async (app, server) => {
    try {
      const response = await axios.post(`${http://n9e.rdb}/api/uic/auth/login`, {
        username: 'xxx',
        password: '***',
        is_ldap: 0,
      });
      server.headers = server.headers || {};
      server.headers['set-cookie'] = response.headers['set-cookie'];
    } catch (e) {
      console.log(e);
    }
  },
}

隔离 & 约束

1、子系统维护自身的路由,需要配置好 basepath
2、禁止声明全局变量
3、禁止定义没有 namespace 的 classname
4、禁止修改基础标签样式
5、很多框架会有 normalize.css 需要特殊处理包裹下

消息通知

目前涉及的状态不会很多,暂时采用 postMessage 方式
一些初始化的数据诸如权限点等需要在 componentDidMount 阶段才能监听到
Function Component 使用 useLayoutEffect 来代替 componentDidMount

当前语言

type language = 'zh' | 'en';
window.addEventListener('message', (event) => {
  const { data } = event;
  if (_.isPlainObject(data) && data.type === 'language') {
    setLanguage(data.value);
  }
}, false);

租户、项目

interface tenantProject = {
  tenant: {
    id: number,
    ident: string,
  },
  project: {
    id: number,
    ident: string,
  }
};
window.addEventListener('message', (event) => {
  const { data } = event;
  if (_.isPlainObject(data) && data.type === 'tenantProject') {
    setTenantProject(data.value);
  }
}, false);

权限点

type permissionPoint = {
  [index: string]: true,
};
window.addEventListener('message', (event) => {
  const { data } = event;
  if (_.isPlainObject(data) && data.type === 'permissionPoint') {
    setPermissionPoint(data.value);
  }
}, false);

处理租户、项目公共选择器是否显示

1、如果子系统全站不需要则在 app.tsx 里发送消息
2、如果子系统部分页面不需要则在该页面里发送消息

需要注意:当发送隐藏选择器消息后需要在组件卸载的时候重新发送显示选择器消息

// class component 方式参考下面
componentDidMount() {
  window.postMessage({
    type: 'tenantProjectVisible',
    value: false,
  }, window.location.origin);
}
componentWillUnmount() {
  window.postMessage({
    type: 'tenantProjectVisible',
    value: true,
  }, window.location.origin);
}
// function component 方式参考下面
useEffect(() => {
  window.postMessage({
    type: 'tenantProjectVisible',
    value: false,
  }, window.location.origin);

  return () => {
    window.postMessage({
      type: 'tenantProjectVisible',
      value: true,
    }, window.location.origin);
  }
}, [])