美文网首页
实战微服务,从使用qiankun到自写微服务

实战微服务,从使用qiankun到自写微服务

作者: 码代码的小公举 | 来源:发表于2022-06-14 09:33 被阅读0次

我本地写的文档,大部分内容我直截了当的截图了。

image.png
image.png
image.png
image.png
image.png
image.png

我根据报错提示进行了调整:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { isInIcestark, setLibraryName } from '@ice/stark-app';

import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

let w_root;
export function mount(props) {
  w_root = ReactDOM.createRoot(props.container);
  w_root.render(<App {...props.customProps} />);
}
export function unmount(props) {
  w_root.unmount();
}
setLibraryName('microApp');

const root = ReactDOM.createRoot(document.getElementById('root'));
if (!isInIcestark()) {
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
}

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
import { useEffect } from 'react';

import './App.css';

function App() {
  useEffect(() => {
    console.log('useEffect');
    return () => {
      console.log('unmout');
    }
  })
  return (
    <div className="App">
      我是create-react-app的独立项目
    </div>
  );
}

export default App;

子应用的生命周期感知:顺利在进入的时候打印到“useEffect”, 离开的时候打印了"unmout", 完成将普通的create-react-app 项目成为子应用。

性能优化 性能优化 | icestark
image.png
image.png
image.png
第二步:主应用接入
  1. yarn add qiankun

  2. index.js 文件 (配置路由)

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { BrowserRouter } from "react-router-dom";
    
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    const root = ReactDOM.createRoot(document.getElementById('root_main'));
    root.render(
      <React.StrictMode>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </React.StrictMode>
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();
    
    
  1. 主应用App.js文件(入口文件):

    import { registerMicroApps, start } from 'qiankun';
    import { Routes, Route, Link } from "react-router-dom";
    import { useEffect } from 'react';
    
    import './App.css';
    
    
    function App() {
      useEffect(() => {
        registerMicroApps([
          {
            name: 'child', // app name registered
            entry: '//localhost:7001',
            container: '#Child',
            activeRule: 'child'
          }, {
            name: 'about', // app name registered
            entry: '//localhost:7001',
            container: '#about',
            activeRule: 'about'
          },
        ]);
        start();
      }, [])
      return (
        <div className="App">
          <div style={{ background: '#577dc3', padding: 10, color: '#fff' }}>
            headers
          </div>
          <div style={{ display: 'flex' }}>
            <div style={{ padding: 10, borderRight: '1px solid #eee' }}>
              <Link to="/main">main</Link>
              <br /><br />
              <Link to="/child">child</Link>
              <br /><br />
              <Link to="/about">about</Link>
              <br /><br />
              <Link to="/">/</Link>
            </div>
            <div style={{ flex: 1 }}>
              <Routes>
                <Route path="/" element={<div>enter</div>} />
                <Route path="main" element={<div>main</div>} />
                <Route path="child" element={<div id="Child"></div>} />
                <Route path="about" element={<div id="about"></div>} />
              </Routes>
            </div>
          </div>
        </div>
      );
    }
    
    export default App;
    App.css  样式文件
    
    .App {
      width: 100vw;
      height: 100vh;
      overflow: hidden;
    }
    
  2. 上一步中,只有1,3是跟逻辑相关的,2. 4都是强调了第一步的改动,到此主应用就完成了。

第三步:子应用接入
  1. yarn eject

  2. 修改配置:webpack.config.js文件


    image.png
    image.png
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom";

import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';


/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap() {
  console.log('react app bootstraped');
}

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
let root_child
export async function mount(props) {
  root_child = ReactDOM.createRoot(props.container ? props.container.querySelector('#root') : document.getElementById('root'));
  root_child.render(
    <React.StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </React.StrictMode>
  );
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  console.log(props.container.querySelector('#root'))
    // unmountComponentAtNode会报错,注释掉后不影响使用
  // ReactDOM.unmountComponentAtNode(
  //   props.container ? props.container.querySelector('#root') : document.getElementById('root'),
  // );
}

/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props) {
  console.log('update props', props);
}

if (!window.__POWERED_BY_QIANKUN__) {
  // 不是qiankun过来的就正常展示
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(
    <React.StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </React.StrictMode>
  );
}

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
  • 这里重点注意:官网给的是

    export async function mount(props) {
      ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
    }
    

这个是旧版本的写法,新版本的react需要ReactDOM.createRoo,然后render来作为渲染方式,不然会报错。


image.png
image.png
image.png
image.png
image.png
image.png

这里我只是简单实现了在主应用中插入子应用,效果如上图。

思路
  1. 请求子应用url地址,得到入口js文件,这里也需要配置跨域哦
  2. 子应用向window注册wei_mount事件(我目前没有想到其他更好的办法)
  3. 主应用修改window._IS_WS_WEI,表示当前为微服务
  4. 主应用写上根模块id="child"
主应用实现代码

这里注意bundle.js的入口路径不同项目也许有差别,可以做个微调。

app.js文件

import { useState } from 'react';
import { useEffect } from 'react';
import './App.css';

window._IS_WS_WEI_ = 'wangshi的demo';
let a;
const runScript = async (url) => {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = resolve;
    script.onerror = reject;
    const firstScript = document.getElementsByTagName('script')[0];
    firstScript.parentNode.insertBefore(script, firstScript);
  });
};

function App() {
  useEffect(() => {
    asyncCall();
  }, [])
  async function asyncCall() {
    if (a) {
      return;
    }
    a = true;
    await runScript('http://localhost:3001/static/js/bundle.js')
    window.wei_mount({ container: document.getElementById('child') })
  }
  return (
    <div className="main_App">
      我是主
      <div id="child"></div>
    </div>
  );
}

export default App;

入口文件路径查找: 我是这样确认我的入口位置的


image.png
image.png image.png

Why Not Iframe

为什么不用 iframe,这几乎是所有微前端方案第一个会被 challenge 的问题。但是大部分微前端方案又不约而同放弃了 iframe 方案,自然是有原因的,并不是为了 "炫技" 或者刻意追求 "特立独行"。

如果不考虑体验问题,iframe 几乎是最完美的微前端解决方案了。

iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。

1url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
2UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
3全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
4慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

其中有的问题比较好解决(问题1),有的问题我们可以睁一只眼闭一只眼(问题4),但有的问题我们则很难解决(问题3)甚至无法解决(问题2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题, 最终导致我们舍弃了 iframe 方案。

-----摘抄于qiankun

相关文章

网友评论

      本文标题:实战微服务,从使用qiankun到自写微服务

      本文链接:https://www.haomeiwen.com/subject/odoomrtx.html