微前端-qiankun实战

一、CSS 隔离方案

子应用之间样式隔离

Dynamic Stylesheet 动态样式表,当应用切换时移除老应用样式,添加新应用样式

主应用和子应用之间的样式隔离

BEM (Block Element Modifier) 约定项目前缀
CSS-Modules 打包时生成不冲突的选择器名
Shadow DOM 真正意义上的隔离
css-in-js

let shadowDom = shadow.attachShadow({ mode: 'open' });
let pElement = document.createElement('p');
pElement.innerHTML = 'hello world';
let styleElement = document.createElement('style');
styleElement.textContent = `
p{color:red}
`
shadowDom.appendChild(pElement);
shadowDom.appendChild(styleElement)
shadow DOM 可以实现真正的隔离机制

二、JS 沙箱机制

 

当运行子应用时应该跑在内部沙箱环境中
快照沙箱,在应用沙箱挂载或卸载时记录快照,在切换时依据快照恢复环境 ( 无法支持多实
)
Proxy 代理沙箱 , 不影响全局环。
 
 
1). 快照沙箱
1. 激活时将当前 window 属性进行快照处理
2. 失活时用快照中的内容和当前 window 属性比对
3. 如果属性发生变化保存到 modifyPropsMap 中,并用快照还原 window 属性
4. 在次激活时,再次进行快照,并用上次修改的结果还原 window
快照沙箱只能针对单实例应用场景 , 如果是多个实例同时挂载的情况则无法解决,只能通过
proxy 代理沙箱来实现

 

2).Proxy 代理沙箱
每个应用都创建一个 proxy 来代理 window ,好处是每个应用都是相对独立,不需要直接更
改全局 window 属性!

 

class ProxySandbox {
constructor() {
const rawWindow = window;
const fakeWindow = {}
const proxy = new Proxy(fakeWindow, {
set(target, p, value) {
target[p] = value;
return true
},
get(target, p) {
return target[p] || rawWindow[p];
}
});
this.proxy = proxy
}
}
let sandbox1 = new ProxySandbox();
let sandbox2 = new ProxySandbox();
window.a = 1;
((window) => {
window.a = 'hello';
console.log(window.a)
})(sandbox1.proxy);
((window) => {
window.a = 'world';
console.log(window.a)
})(sandbox2.proxy);
每个应用都创建一个 proxy 来代理 window ,好处是每个应用都是相对独立,不需要直接更
改全局 window 属性!

三、主应用编写

 

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
import {registerMicroApps,start} from 'qiankun';
const apps = [ 
  {
    name:'vueApp', // 应用的名字
    entry:'//localhost:10000', // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
    container:'#vue', // 容器名
    activeRule:'/vue', // 激活的路径
    props:{a:1}

  },
  {
    name:'reactApp',
    entry:'//localhost:20000', // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
    container:'#react',
    activeRule:'/react',
  }
]
registerMicroApps(apps); // 注册应用
start({
  prefetch:false // 取消预加载
});// 开启

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

 

App.vue

 

<template>
  <div>
  <el-menu :router="true" mode="horizontal">
      <!--基座中可以放自己的路由-->
      <el-menu-item index="/">Home</el-menu-item> 
       <!--引用其他子应用-->
      <el-menu-item index="/vue">vue应用</el-menu-item>
      <el-menu-item index="/react">react应用</el-menu-item>
  </el-menu>
    <router-view ></router-view>
    <div id="vue"></div>
    <div id="react"></div>
  </div>
</template>

 

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

  const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

 

四、子应用

 

vue子项目

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

// Vue.config.productionTip = false


let instance = null
function render(props) {
    instance = new Vue({
        router,
        render: h => h(App)
    }).$mount('#app'); // 这里是挂载到自己的html中  基座会拿到这个挂载后的html 将其插入进去
}
if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行
    render();
}
// 子组件的协议就ok了
export async function bootstrap(props) {

};
export async function mount(props) {
  console.log(props)
    render(props)
}
export async function unmount(props) {
    instance.$destroy();
}

 

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

  const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: '/vue',
  routes
})

export default router

 

react子项目

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

function render(){
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  );
}
if(!window.__POWERED_BY_QIANKUN__){
  render();
}
export async function bootstrap(){

}
export async function mount() {
  render()
}
export async function unmount(){
  ReactDOM.unmountComponentAtNode( document.getElementById('root'));
}

App.js

 

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Route, Link } from 'react-router-dom'

function App() {
  return (
    <BrowserRouter basename="/react">
      <Link to="/">首页</Link>
      <Link to="/about">关于页面</Link>
      <Route path="/" exact render={() => (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Edit <code>src/App.js</code> and save to reload.
         </p>
            <a
              className="App-link"
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              Learn React
         </a>
          </header>
        </div>
      )}></Route>
      <Route path="/about" render={()=><h1>about页面</h1>}></Route>
    </BrowserRouter>
  );
}

// qiankun 无关的技术栈

export default App;

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值