设置环境变量

  • 我们完成了子应用页面的显示,但是由于我们之前设置的window.__MICRO_WEB__从来没有改变过,进入子应用时,会重复进行渲染,我们可以看下子应用设置的逻辑 ```javascript function render() { instance = createApp(App); instance .use(router) .mount(‘#app’); } // 一直会执行到这里面 if (!window.MICRO_WEB) { render(); } export async function bootstrap() { console.log(‘vue3.0 app bootstrap’); }

export async function mount(app) { setMain(app) render(); }

export async function unmount(ctx) { instance.unmount(); instance = null; const { container } = ctx if (container) { document.querySelector(container).innerHTML = ‘’ } }

  1. - 除了一直执行`render`方法,子应用的声明周期我们也没有出发到,所以我们要在加载子应用页面的时候,设置`__MICRO_WEB__``true`
  2. ```javascript
  3. export const loadHtml = async (app) => {
  4. window.__MICRO_WEB__ = true;
  5. }
  • 我们设置__MICRO_WEB__true后,子应用不再调用render方法,需要执行mount声明周期来触发执行render,我们需要处理我们子应用声明周期的执行

    获取子应用生命周期

  • 我们通过执行js脚本,来获取到子应用声明周期

  • main\micro\sandbox\index.js

    1. // 2. 运行js文件,获取子应用生命周期
    2. // const lifeCycle = performScript(script, app.name);
    3. const lifeCycle = performScriptForEval(script, app.name);
    4. console.log('lifeCycle', lifeCycle);
  • performScriptForEval执行内容

    1. const scriptText =
    2. `() => {
    3. ${script}
    4. return window[${appName}]
    5. }`;
    6. return eval(scriptText);

    image.png

  • 我们获取到的内容是js脚本文件,并不是我们想要的module,这是因为当前返回的window,并不是我们的window对象

  • 我们通过call,将指针指向window,并传入window参数

    1. const scriptText =
    2. `() => {
    3. ${script}
    4. return window[${appName}]
    5. }`;
    6. return eval(scriptText).call(window,window);
  • 上面这种方式执行的时候获取不到子应用全局变量,具体原因没找到

  • 最终写法都可以获取到子应用声明周期 ``javascript // 执行应用的 js 内容 new Function 篇 export const performScript = (script, appName) => { const scriptText = ${script} return window[${appName}] ` return new Function(scriptText).call(window, window);

}

// 执行应用中的 js 内容 eval篇 export const performScriptForEval = (script, appName) => { // 我们在子应用打包配置中设置了library,全局变量名 // 我们通过执行window.appName 来获取子应用声明周期 const scriptText = (() => () => { try { ${script} return window['${appName}'] } catch (err) { console.error('runScript error:' + err); } })() return (() => eval(scriptText))().call(window, window); }

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/243804/1657875076554-146b24a1-846c-4d93-87a7-ae807ea8d9a3.png#clientId=u71de78f4-5877-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=149&id=u5ffb39ce&margin=%5Bobject%20Object%5D&name=image.png&originHeight=164&originWidth=542&originalType=binary&ratio=1&rotation=0&showTitle=false&size=14833&status=done&style=none&taskId=u262a10a2-2bd8-4198-9ebc-d8f3755e56a&title=&width=492.7272620476969)
  2. <a name="WkbwX"></a>
  3. # 将声明周期挂载到app上
  4. ```javascript
  5. // 判断是否有生命周期
  6. function isCheckLiftCycle(lifeCycle) {
  7. return lifeCycle && lifeCycle.bootstrap && lifeCycle.mount && lifeCycle.unmount;
  8. }
  9. // 将子应用生命周期挂载到app上
  10. // beforeLoaded 生命周期执行完之后,我们挂载了子应用这些生命周期,所以后面生命周期都会触发到
  11. if (isCheckLiftCycle(lifeCycle)) {
  12. app.bootstrap = lifeCycle.bootstrap;
  13. app.mount = lifeCycle.mount;
  14. app.unmount = lifeCycle.unmount;
  15. }
  • 我们需要先判断子应用是否有生命周期,再将其挂载到子应用app上
  • 此时,主应用beforeLoaded生命周期执行完,开始执行mounted,这个时候,我们就能执行到子应用app上的mount方法,在子应用中做渲染处理
    // 子应用
    export async function mount(app) {
    console.log('mount');
    setMain(app)
    render();
    }