应用间通信

icestark 将应用进行了拆分(框架应用和子应用),拆分之后,不同之间就会有数据交换的场景。在 icestark 体系下,通信可以分为两类:子应用和框架应用之间的通信;不同子应用之间的通信。icestark 拆分后的应用在运行时,共享 locationCookieLocalStoragewindow 等资源。因此应用间的通信,都可以基于这些实现。

子应用和框架应用之间的通信

这类数据交换的场景很多,这里简单通过一些场景的实现方案进行说明。

通过共享的

  1. // 子应用 A 中的代码
  2. import React from 'react';
  3. import { Link } from 'react-router-dom';
  4. const App = () => (
  5. <div>
  6. <Link to="/A/home">
  7. 跳往 home 页面
  8. </Link>
  9. </div>
  10. );
  1. // 框架应用中的代码
  2. import React from 'react';
  3. import { AppRouter, AppRoute } from '@ice/stark';
  4. class App extends React.Component {
  5. state = {
  6. showFooter: true,
  7. };
  8. onRouteChange = (pathname) => {
  9. const { showFooter } = this.state;
  10. // 通过监听 pathname,判断是否为 /A/home,控制 footer 的显示隐藏
  11. if (showFooter && pathname === '/A/home') {
  12. this.setState({ showFooter: false });
  13. } else if (!showFooter && pathname !== '/A/home') {
  14. this.setState({ showFooter: true });
  15. }
  16. }
  17. render() {
  18. const { showFooter, language } = this.state;
  19. return (
  20. <div>
  21. <div className="header">this is common header</div>
  22. <AppRouter onRouteChange={this.onRouteChange} >
  23. <AppRoute
  24. path={['/', '/home', '/about']}
  25. basename="/"
  26. exact
  27. title="Index"
  28. url={[
  29. '//g.alicdn.com/icestark-demo/child/0.1.2/js/index.js',
  30. '//g.alicdn.com/icestark-demo/child/0.1.2/css/index.css'
  31. ]}
  32. />
  33. </AppRouter>
  34. {showFooter ? <div className="footer">this is common footer</div> : null}
  35. </div>
  36. );
  37. }
  38. }

AppRouter 提供的 onRouteChange 支持从框架应用中监听子应用切换 pathnamequery 的能力。

在子应用中触发

  1. // 子应用 A 中的代码
  2. import React from 'react';
  3. import { Button } from '@alifd/next';
  4. class App extends React.Component {
  5. handleClick = () => {
  6. window.postMessage({ refreshMessage: true }, '*');
  7. };
  8. render() {
  9. const { showFooter, messageNumber } = this.state;
  10. return (
  11. <div>
  12. <Button onClick={this.handleClick}>通知框架应用更新信息</Button>
  13. </div>
  14. );
  15. }
  16. }
  1. // 框架页中的代码
  2. import React from 'react';
  3. import axios from 'axios';
  4. import { AppRouter, AppRoute } from '@ice/stark';
  5. class App extends React.Component {
  6. state = {
  7. messageCount: 0,
  8. };
  9. componentDidMount() {
  10. window.addEventListener('message', (e) => {
  11. const { origin, data } = e;
  12. // 设置白名单,提高安全性
  13. const isSaveHost = host.indexOf('http://localhost') > -1 ? true : origin !== host;
  14. if (!isSaveHost) return;
  15. if (data.refreshMessage) {
  16. this.requestMessage();
  17. }
  18. });
  19. }
  20. requestMessage = () => {
  21. axios({ headers: {}, method: 'GET', url: '/app/message', params: {}, timeout: 30000 }).then(res => {
  22. // res 格式如下:
  23. // {
  24. // success: true,
  25. // model: 5
  26. // }
  27. const { success= false, model } = res;
  28. if (success) {
  29. this.setState({ messageCount: Number(model) });
  30. }
  31. })
  32. }
  33. render() {
  34. const { messageCount } = this.state;
  35. return (
  36. <div>
  37. <div className="header">you have {messageCount} message!</div>
  38. <AppRouter>
  39. <AppRoute
  40. path={['/', '/home', '/about']}
  41. basename="/"
  42. exact
  43. title="Index"
  44. url={[
  45. '//g.alicdn.com/icestark-demo/child/0.1.2/js/index.js',
  46. '//g.alicdn.com/icestark-demo/child/0.1.2/css/index.css'
  47. ]}
  48. />
  49. <AppRoute
  50. path="/user"
  51. basename="/user"
  52. title="User"
  53. url={[
  54. '//g.alicdn.com/icestark-demo/child2/0.1.2/js/index.js',
  55. '//g.alicdn.com/icestark-demo/child2/0.1.2/css/index.css'
  56. ]}
  57. />
  58. </AppRouter>
  59. </div>
  60. );
  61. }
  62. }

不同子应用之间的通信

子应用之间需要数据交换的场景也很多,通信有两种方式:

通过公共数据存放途径比如

示例代码

  1. // 子应用 A 中的代码
  2. import React from 'react';
  3. import { Button } from '@alife/next';
  4. class App extends React.Component {
  5. constructor(props) {
  6. super(props);
  7. this.state = {
  8. count: (window.__app__ && window.__app__.count) || 0,
  9. };
  10. }
  11. add = () => {
  12. this.setState({ count: this.state.count + 1 }, () => {
  13. if (window.__app__) {
  14. window.__app__.count = this.state.count;
  15. } else {
  16. window.__app__ = {};
  17. window.__app__.count = this.state.count;
  18. }
  19. });
  20. }
  21. render() {
  22. const { count } = this.state;
  23. return (
  24. <div>
  25. <Button type="normal" onClick={this.add}>Add</Button>
  26. <p>This is in App A, Current count is {count}.</p>
  27. </div>
  28. );
  29. }
  30. }
  1. // 子应用 B 中的代码
  2. import React from 'react';
  3. import { Button } from '@alife/next';
  4. class App extends React.Component {
  5. constructor(props) {
  6. super(props);
  7. this.state = {
  8. count: (window.__app__ && window.__app__.count) || 0,
  9. };
  10. }
  11. add = () => {
  12. this.setState({ count: this.state.count + 1 }, () => {
  13. if (window.__app__) {
  14. window.__app__.count = this.state.count;
  15. } else {
  16. window.__app__ = {};
  17. window.__app__.count = this.state.count;
  18. }
  19. });
  20. }
  21. render() {
  22. const { count } = this.state;
  23. return (
  24. <div>
  25. <Button type="normal" onClick={this.add}>Add</Button>
  26. <p>This is in App B, Current count is {count}.</p>
  27. </div>
  28. );
  29. }
  30. }

通过框架应用作为媒介,将共享数据存放在框架应用中,这就将子应用之间的通信转化成了子应用和框架应用的通信

两种通信数据流转过程如图所示数据流转