微前端利用分为主利用与子利用,部署形式是别离编译好主利用与子利用,将主利用与子利用部署到 nginx
配置好的目录即可。
代码仓库 https://github.com/jwchan1996/qiankun-micro-app
别离进入 portal
、app1
、app2
根目录,执行:
开发模式
# portalyarnyarn start
# app1、app2npm installnpm run dev
生产模式
# portalyarn build
# app1、app2npm run build
主利用
主利用 js
文件引入 qiankun
注册子利用,并编写导航页显示跳转逻辑。
<!DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8"> <title>QianKun Example</title></head><body> <div class="mainapp"> <!-- 标题栏 --> <header class="mainapp-header"> <h1>导航</h1> </header> <div class="mainapp-main"> <!-- 侧边栏 --> <ul class="mainapp-sidemenu"> <li class="app1">利用一</li> <li class="app2">利用二</li> </ul> <!-- 子利用 --> <main id="subapp-container"></main> </div> </div> <script src="./index.js"></script></body></html>
主利用 js
入口文件:
import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start, initGlobalState } from 'qiankun';import './index.less';/** * 主利用 **能够应用任意技术栈** * 以下别离是 React 和 Vue 的示例,可切换尝试 */import render from './render/ReactRender';// import render from './render/VueRender';/** * Step1 初始化利用(可选) */render({ loading: true });const loader = loading => render({ loading });/** * Step2 注册子利用 */registerMicroApps( [ { name: 'app1', entry: process.env.NODE_ENV === 'production' ? '//192.168.2.192:7100' : '//localhost:7100', container: '#subapp-viewport', loader, activeRule: '/app1', }, { name: 'app2', entry: process.env.NODE_ENV === 'production' ? '//192.168.2.192:7101' : '//localhost:7101', container: '#subapp-viewport', loader, activeRule: '/app2', } ], { beforeLoad: [ app => { console.log('[LifeCycle] before load %c%s', 'color: green;', app.name); }, ], beforeMount: [ app => { console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name); }, ], afterUnmount: [ app => { console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name); }, ], },);const { onGlobalStateChange, setGlobalState } = initGlobalState({ user: 'qiankun',});onGlobalStateChange((value, prev) => console.log('[onGlobalStateChange - master]:', value, prev));setGlobalState({ ignore: 'master', user: { name: 'master', },});/** * Step3 设置默认进入的子利用 */// setDefaultMountApp('/app1');/** * Step4 启动利用 */start();runAfterFirstMounted(() => { console.log('----------------------------------') console.log(process.env.NODE_ENV) console.log('----------------------------------') console.log('[MainApp] first app mounted');});//浏览器地址入栈function push(subapp) { history.pushState(null, subapp, subapp) }//配合导航页显示逻辑function initPortal(){ //主利用跳转 document.querySelector('.app1').onclick = () => { document.querySelector('.mainapp-sidemenu').style.visibility = 'hidden' push('/app1') } document.querySelector('.app2').onclick = () => { document.querySelector('.mainapp-sidemenu').style.visibility = 'hidden' push('/app2') } //回到导航页 document.querySelector('.mainapp-header h1').onclick = () => { push('/') } if(location.pathname !== '/'){ document.querySelector('.mainapp-sidemenu').style.visibility = 'hidden' }else{ document.querySelector('.mainapp-sidemenu').style.visibility = 'visible' } if(location.pathname.indexOf('login') > -1){ document.querySelector('.mainapp-header').style.display = 'block' }else{ document.querySelector('.mainapp-header').style.display = 'none' } //监听浏览器后退回退 window.addEventListener('popstate', () => { if(location.pathname === '/'){ document.querySelector('.mainapp-sidemenu').style.visibility = 'visible' } if(location.pathname.indexOf('login') > -1){ document.querySelector('.mainapp-header').style.display = 'block' }else{ document.querySelector('.mainapp-header').style.display = 'none' } }, false)}initPortal()
docker nginx 配置
此处 nginx
次要作用是用于端口目录转发,并配置主利用拜访子利用的跨域问题。
应用 docker
配置部署 nginx
:
# docker-compose.ymlversion: '3.1'services: nginx: restart: always image: nginx container_name: nginx ports: - 8888:80 - 8889:8889 - 7100:7100 - 7101:7101 volumes: - /app/volumes/nginx/nginx.conf:/etc/nginx/nginx.conf - /app/volumes/nginx/html:/usr/share/nginx/html - /app/micro/portal:/app/micro/portal - /app/micro/app1:/app/micro/app1 - /app/micro/app2:/app/micro/app2
将编译后的主利用以及子利用放到对应的数据卷挂载目录即可,如主利用 /app/micro/portal
。
同理,也须要将配置好的 nginx.conf
文件放到指定的数据卷挂载目录,应用 docker-compose up -d
启动即可。
nginx
端口目录转发配置:
# nginx.confuser nginx;worker_processes 1;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;events { worker_connections 1024;}http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; server { listen 8889; server_name 192.168.2.192; location / { root /app/micro/portal; index index.html; try_files $uri $uri/ /index.html; } } server { listen 7100; server_name 192.168.2.192; # 配置跨域拜访,此处是通配符,严格生产环境的话能够指定为主利用 192.168.2.192:8889 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; location / { root /app/micro/app1; index index.html; try_files $uri $uri/ /index.html; } } server { listen 7101; server_name 192.168.2.192; # 配置跨域拜访,此处是通配符,严格生产环境的话能够指定为主利用 192.168.2.192:8889 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; location / { root /app/micro/app2; index index.html; try_files $uri $uri/ /index.html; } }}
子利用适配框架
下体面利用以惯例 vue
我的项目为例。
入口文件 main.js
在入口文件减少 qiankun
环境判断,判断以后是 qiankuan
环境的则将子利用引入到主利用框架内,而后在主框架内执行失常的 vue
元素挂载。
// 在所有代码的文件之前引入判断if (window.__POWERED_BY_QIANKUN__) { // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}import Vue from "vue";import App from "./App";import router from "./router";let instance = null;function render(props = {}) { // 此处 container 是主利用生成的用于装载子利用的 div 元素 // 如 <div id="__qiankun_microapp_wrapper_for_app_1_1596504716562__" /> const { container } = props; instance = new Vue({ router, render: h => h(App), }).$mount(container ? container.querySelector('#app') : '#app');}if (!window.__POWERED_BY_QIANKUN__) { render();}function storeTest(props) { props.onGlobalStateChange && props.onGlobalStateChange( (value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true, ); props.setGlobalState && props.setGlobalState({ ignore: props.name, user: { name: props.name, }, });}export async function bootstrap() { console.log('[vue] vue app bootstraped');}export async function mount(props) { console.log('[vue] props from main framework', props); storeTest(props); render(props);}export async function unmount() { instance.$destroy(); instance.$el.innerHTML = ''; instance = null;}
router 配置
路由须要依据 qiankun
环境配置 base
门路,以及设置路由的 history
模式。
// router/index.jsconst router = new Router({ // 此处 /app1 是子利用在主利用注册的 activeRule base: window.__POWERED_BY_QIANKUN__ ? '/app1' : '/', mode: 'history', routes: [ { …… …… } ]})// portal/index.jsregisterMicroApps( [ { name: 'app1', entry: process.env.NODE_ENV === 'production' ? '//192.168.2.192:7100' : '//localhost:7100', container: '#subapp-viewport', loader, activeRule: '/app1', } ])
子利用打包
打包 umd 格局
output: { library: 'portal', libraryTarget: 'umd'}
字体图标与 css 背景图片门路问题
默认状况下,在 css
援用的资源应用 url-loader
加载打包进去是相对路径的,所以会呈现子利用的资源拼接到主利用的 domain
的状况,造成加载资源失败。
因为 element-ui
的字体图标是在 css
外面引入的,还有相干背景图片的引入也是在 css
里,所以须要配置 webpack
的 url-loader
,生产模式状况下间接指定资源前缀。
module: { rules: [ { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: "url-loader", options: { limit: 10000, name: utils.assetsPath("img/[name].[hash:7].[ext]"), //这里 192.168.2.192:7100 是子利用部署地址 publicPath: process.env.NODE_ENV === 'production' ? '//192.168.2.192:7100' : '' } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: "url-loader", options: { limit: 10000, name: utils.assetsPath("fonts/[name].[hash:7].[ext]"), publicPath: process.env.NODE_ENV === 'production' ? '//192.168.2.192:7100' : '' } } ]}