从零开始的微前端---qiankun篇(3)

1,573 阅读6分钟

前言

接着上一篇,上一篇讲了简单的qiankun实例,以及vue子应用的使用, 那么, 这一篇就实践一下react子应用的使用,子应用独立运行, 及子应用内路由跳转,一个步骤一个步骤实践

一.准备

npm i create-react-app -g // 先安装脚手架

2.创建项目

首先在主应用同级目录下, 创建react的子应用, 这里名字设置为qiankun-testchild2, 使用上方脚手架搭建
create-react-app qiankun-testchild2

image.png

3. 改造子应用, 上一篇中讲到了子应用的改造, 那么这个react项目也是类似的,

首先 , 将public中index.html中的根id改掉

image.png

4.然后就是修改webpack配置, 这里的话, react中, 在不ejec的情况下, 需要安装两个依赖来进行修改配置

npm install react-app-rewired customize-cra --save-dev

5.安装好了之后, 在子应用的根目录下, 创建一个config-overrides.js的文件, 这个文件用于配置webpack的基本配置, 基本配置的要点, 在上一个vue子应用中说过了, 这里就不细说了, 直接上代码


    module.exports = {
      webpack: (config) => {
        config.output.library = 'qiankun-testchild2'
        config.output.libraryTarget = 'umd'
        config.output.publicPath = 'http://localhost:9726'
        return config
      },
      devServer: (configFunction) => {
        // devServer要求返回一个函数
        return function(proxy, allowedHost) {
          const config = configFunction(proxy, allowedHost)
          // 以下端口修改直接写在.env文件中,默认会去这个文件中拿环境变量
          config.headers = {
            'Access-Control-Allow-Origin': '*'
          }
          return config
        }
      }
    }

6.再创建一个.env文件, 这个文件默认读取端口, 那么在这个文件中, 写好端口定义就行了,

 PORT=9726
  WDS_SOCKET_PORT=9726

7.写好webpack配置之后, 需要修改一下package.json, 因为使用了 react-app-rewired

image.png

     "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test --env=jsdom"
  },

8. 那么这些都配置好了之后, 就需要改造index.js了, 同样是需要shared文件夹,去保持获取主应用数据通信, 然后子应用的qinakun三个生命周期. 以及区分独立运行和子应用运行的判断, 直接上代码

  import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import actions from "../src/shared/action";
    // import reportWebVitals from './reportWebVitals';

    function render(props={}) {
      if (props) {
        // 注入 actions 实例
        actions.setActions(props);
      }
      const { container } = props;
      ReactDOM.render(<App />, container ? container.querySelector('#app3') : document.getElementById('app3'));
    }
    if (window.__POWERED_BY_QIANKUN__) {
      // eslint-disable-next-line no-undef
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }

    if(!window.__POWERED_BY_QIANKUN__) {
      console.log('123123')
      // 子应用独立执行时
      render()
    }
    export async function bootstrap(props) {
      console.log('react app bootstraped');
    }
    export async function mount(props) {
      render(props)
    }
    export async function unmount(props) {
      ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#app3') : document.getElementById('app3'));
    }

9. index.js中用到了action, 跟vue子应用一样, 可以直接把文件夹复制过来. 当然, 如果后续全局交互数据的类型不同的话, 内部还是需要单独设置某些修改获取属性方法的.

image.png

10. index.js修改完成后, 就需要修改app.js了, 那么, 在app.js中,同样在DOM完成阶段: componentDidMount, 去共享数据, 修改state的值,(react当中, state值不能直接修改, 需要使用this.setState),

关于react的使用及语法, 可以参见之前写的一篇:juejin.cn/post/684490…

代码如下:

image.png

    import './App.css';
    import actions from "./shared/action";
    import React, { Component } from 'react';
    class App extends Component {
      state = {
        msg: ''
      } 
      componentDidMount () {
        // 這裡共享數據, 加上判斷, 便於獨立啟動
        if (window.__POWERED_BY_QIANKUN__) {
          actions.onGlobalStateChange(state => {
            const { token } = state;
            // 未登录 - 返回主页
            this.setState({
              msg: token
            })
          }, true);
        }
      }
      render () {
        return (
          <div className="App">
            <header className="App-header">
              購物車系列 {this.state.msg}
            </header>
          </div>
        );
      }

    }

    export default App;

11. 在componentDidMount 阶段加上判断的原因就是, 如果它是独立启动,那么就不会注册actions, 就避免了独立启动时找不到actions的问题,

12. 然后呢, 子应用弄好之后, 就需要到主应用去注册一下, 就是在主应用的main.js中apps数组中,添加上这个子应用就好了, 之后的子应用也是一样的,基本的东西就不再做解释了.

image.png

    
    let apps = [
      {
        name: "qiankun-testchild1",
        entry: '//localhost:9725', 
        container: "#subapp",
        activeRule: "/qiankun-testchild1",
        props: {
          name: "kuitos"
        }
      },
      {
        name: "qiankun-testchild2",
        entry: '//localhost:9726', 
        container: "#subapp",
        activeRule: "/qiankun-testchild2",
        props: {
          name: "kuitos"
        }
      }
    ]

13. 然后可以启动看下组合效果和单独启动效果.

image.png

image.png

image.png

image.png

14. 那么到这里, 基本上就简单的集成好了vue和react项目了, 独立运行呢, 刚才也做到了, 就是把注册action的过程做一个判断, 可以理解为跟主应用有关联的数据部分做个独立运行的场景判断. 那么接下来就实践一下, 如何在子应用中进行路由跳转

1. 子应用内路由跳转,以vue子应用为例

子应用内的路由跳转, 依旧可以使用router的方式进行跳转,先创建router-view容器

image.png

然后, 添加两个子页面

image.png

image.png

再在router文件中注册访问路径, 这里需要处理一下重复跳转的问题

image.png

可以看到上图中, 三个红框部分, 第一个红框处理重复跳转的问题, 第二个红框注册路由, 第三个红框中, base特别注意, 可以改成当前子应用路由,那么上面的路由就不用加子应用的路由了,但是子应用间跳转会出现base路径的问题, 好, 下面运行图.

image.png

image.png

image.png

2. 子应用间跳转,

那么这个就用react子应用跳转到vue子应用, 首先, 需要在app中包裹BrowserRouter

image.png

然后, 在router4以上中, 要使用history跳转, 需要createBrowserHistory, 用法如下, history不用安装

image.png

    import './App.css';
    import actions from "./shared/action";
    import { withRouter } from "react-router-dom";
    import {createBrowserHistory} from "history";
    import React, { Component } from 'react';
    class App extends Component {
      state = {
        msg: ''
      } 
      componentDidMount () {
        // 這裡共享數據, 加上判斷, 便於獨立啟動
        if (window.__POWERED_BY_QIANKUN__) {
          actions.onGlobalStateChange(state => {
            const { token } = state;
            // 未登录 - 返回主页
            this.setState({
              msg: token
            })
          }, true);
        }
      }
      toChild1() {
       let history =  createBrowserHistory()
        // console.log(this.history)
        history.push('/qiankun-testchild1/test1')
        // history.pushState(null, '/qiankun-testchild1/test1', '/qiankun-testchild1/test1');
      }
      render () {
        return (
          <div className="App">
            <header className="App-header">
              購物車系列 {this.state.msg}
              <button onClick={this.toChild1}>點擊我跳轉</button>  
            </header>
          </div>
        );
      }

    }

    export default withRouter(App);

好了, 现在可以尝试下子应用间的跳转, 效果如下图

image.png

image.png

好, 可以看出, 路径正确了, 但是没有加载出来下一级, 那么, 修改一下,vue子应用的生命周期,这个是因为没有注销

image.png 把注释替换一下

image.png

好了, 再来看下效果

image.png

image.png vue内部跳转, 正常 image.png 从vue跳转到react, 正常 image.png 从react跳转到vue, 正常 image.png 再次点击标签跳转在线商品主页, 正常 image.png

15. 好了, 以上的话, 就基本创建了两个集成子应用, 并且把子应用内和子应用间跳转的问题解决了. 下一篇, 会把这两个子应用内容完善掉, 因为更多的子应用添加集成的步骤都是类似的, 所以,之后的子应用就不作解释了. 主要分析实践应用通信这一块.

16, 最后附上项目链接: github.com/hejiyun/qia…

结语

欲上琼楼邀满月,回首故园已是秋. 各位, 望归来依旧是少年!