# React 项目改造

# 一、改造前准备

  1. 首先保证项目已经接入node

  2. 子服务在权限中心注册,在配置中心能看到子服务 http://test.yx.mail.netease.com/micro-fed/mf-admin/#/service-list

# 二、web端改造

  1. 修改 web/scripts/build/build.js
const argv = require('yargs').argv;
const sharkrScripts = require('@sharkr/scripts');
const sw = new sharkrScripts();
const webpackMicroConfig = sw.getBuildMicroConfig(argv.target);
const webpackConfig = sw.getBuildConfig(argv.target);

sw.runBuild({
    webpackConfig: webpackConfig, 
    target: argv.target, 
    callback: () => {
        sw.runBuildMicroFed({
            webpackConfig: webpackMicroConfig,
            target: argv.target,
        });
    } 
});
  1. 改造web/src/pages/app.tsx.将Content组件中的内容抽取到一个独立的组件appRoute.tsx中。

appRoute.tsx

import React from 'react';
import { Route, Redirect, Switch } from 'react-router-dom';
import { ModuleAList, ModuleADetail } from './moduleA';
import { Loading } from './components/loading';
import { ModuleAEdit } from './moduleA/edit';

const AppRoute: React.FC = () => (
    <React.Fragment>
        <Loading />
        <Switch>
            <Route
                key="/moduleA/list"
                path="/moduleA/list"
                component={ModuleAList}
            />
            <Route
                key="/moduleA/detail"
                path="/moduleA/detail/:id"
                component={ModuleADetail}
            />
            <Route
                key="/moduleA/edit"
                path="/moduleA/edit/:id"
                component={ModuleAEdit}
            />
            <Redirect key="/first" path="/first" to="/moduleA/list" exact />
            <Redirect key="/" path="/" to="/first/" exact />
        </Switch>
    </React.Fragment>
);

export {AppRoute};

app.tsx

/* eslint-disable no-console */
import React from 'react';
import { Menu, message } from 'antd';
import { HashRouter as Router } from 'react-router-dom';
import { SharkRLayout, SharkRMenuProps } from '@sharkr/components';
import { AppConfig } from '@shark/core';
import { getUserInfo } from '../services';
import { AppRoute } from './appRoute';

import './app.scss';

AppConfig.configure({
    contextPath: '/react-template-full',
    productCode: 'react-template',
});

const { Header, Container, Sider, Content } = SharkRLayout;
const { Logo: HeaderLogo, Left, Right, Menu: HeaderMenu, DropDown: HeaderDropDown } = Header;
const { Menu: SideMenu } = Sider;

export const App: React.FC = () => {
    const userMenu = (
        <Menu>
            <Menu.Item>退出</Menu.Item>
        </Menu>
    );
    const headerMenu: SharkRMenuProps = {
        data: [
            {
                name: '一级菜单1',
                link: {
                    href: '#/first',
                },
            },
            {
                name: '一级菜单2',
                onItemClick: e => {
                    // 自定义回调事件
                    console.log(e);
                    message.info(`自定义回调事件, 选中${e.item.name}`);
                },
            },
        ],
    };
    const siderMenu: SharkRMenuProps = {
        data: [
            {
                name: '模块A',
                children: [
                    {
                        name: '列表页',
                        link: { href: '#/moduleA/list' },
                    },
                ],
            },
            {
                name: '模块B',
                children: [
                    {
                        name: '外部链接',
                        link: { href: 'https://yx.mail.netease.com/shark', target: '_blank' },
                    },
                ],
            },
            {
                name: '自定义',
                onItemClick: e => {
                    // 自定义回调事件
                    console.log(e);
                    message.info(`自定义回调事件, 选中${e.item.name}`);
                },
            },
        ],
    };
    return (
        <Router>
            <SharkRLayout>
                <Header>
                    <HeaderLogo product="严选B端模板(Normal)" />
                    <Left>
                        <HeaderMenu {...headerMenu} />
                    </Left>
                    <Right>
                        <HeaderDropDown overlay={userMenu} title={getUserInfo().name} />
                    </Right>
                </Header>
                <Container>
                    <Sider>
                        <SideMenu {...siderMenu} />
                    </Sider>
                    <Content>
                        <AppRoute />
                    </Content>
                </Container>
            </SharkRLayout>
        </Router>
    );
};

  1. 如果项目中用到操作权限控制、左侧导航权限控制、路由守卫,请使用@eagler/authorizion (opens new window)

# 三、server端

  1. 安装两个中间件:@tiger/tiger-extract-header和@tiger/microconfig
// 用于获取请求头部信息
npm i @tiger/tiger-extract-header
// 用于获取微应用资源配置文件
npm i @tiger/microconfig
  1. 将中间件加入server/src/middlewares/global.middleware.ts中
import tigerExtractHeader from '@tiger/tiger-extract-header';
import microconfig from '@tiger/microconfig';

 // 允许代理模式
app.proxy = true;
// 从header中提取productCode;取值方式:ctx.state.headerData.productCode
app.use(tigerExtractHeader()());

// 省略
...

// openid
app.use(tigerOpenid({})({
    include: [`${contextPath}/`, `${contextPath}/*.html`, `${contextPath}/xhr/(.*)`]
}));
//微应用config
app.use(microconfig({
    contextPath: config.contextPath,
    webPath: config.webAppPath
}));

  1. 如果项目中用到操作权限控制、左侧导航权限控制、路由守卫,请使用@eagler/authorizion (opens new window)
// 对应的node端需要安装的包 
npm i @eagler/authorizion-node
// 将AuthenticationModule添加到modules中
// modules/index.ts

import { BaseModule, TgModule } from '@tiger/boot';
import { AuthenticationModule } from '@eagler/authorizion-node';
import { config } from '../config';

@TgModule({
    prefix: config.contextPath,
    imports: [AuthenticationModule],
    controllers: []
})
export class AppModule extends BaseModule { }

  1. 子应用去除@tiger/security和@tiger/permission中间件,由主应用承担csrf认证与权限认证。

  2. ./server/src/conf.base.ts

修改productCode,serviceCode,productId都为子应用的serviceCode

const productCode = 'mf-admin'; // 使用serviceCode作为productCode,用于权限中心
const serviceCode = 'mf-admin';// 
const productId = 'mf-admin';// 服务没有id,用code代替

然后在服务管理页面,设置该服务自己的用户、角色、权限,并添加【关联产品】

http://test.yx.mail.netease.com/micro-fed/mf-admin/#/service-list

  1. 由于@tiger/openid会解析cookie并往ctx中写入用户信息,而后续的接口可能会使用ctx中的用户信息。所以目前@tiger/openid包还不能去掉。日后如果@tiger/openid拆分成 【认证包】 和 【解析包】,那么就可以去除@tiger/openid