【qiankun】微前端项目搭建与通信

官网地址:https://qiankun.umijs.org/

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略

·独立开发、独立部署

主应用

1. 安装 qiankun

$ yarnadd qiankun # 或者 npm i qiankun -S

2. 在主应用中注册子应用(主应用的main.js)

import{ registerMicroApps, start }from'qiankun';

registerMicroApps([

{

name:'react app',// app name registered

entry:'//localhost:7100', //子应用的地址(不包含标识和路由)

container:'#vue’,// 子应用挂载的div

activeRule:'/yourActiveRule', // 子应用的标识,路由中会详细说明

},

]);

// 启动 qiankun

start();

手动加载本项目暂不需要。当子应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的子应用就会被插入到指定的 container 中,同时依次调用子应用暴露出的生命周期钩子。

如果子应用不是直接跟路由关联的时候,你也可以选择手动加载子应用的方式:

import{ loadMicroApp }from'qiankun';

loadMicroApp(

{

name:'app',

entry:'//localhost:7100',

container:'#yourContainer',

}

);

子应用

子应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。

1、入口文件 main.js修改,为了避免根 id #app与其他的 DOM 冲突,需要限制查找范围,在container范围中查找

import'./public-path';

import Vue from'vue';

import VueRouter from'vue-router';

import App from'./App.vue';

import routes from'./router';

import store from'./store';

Vue.config.productionTip =false;

let router =null;

let instance =null;

functionrender(props ={}){

//为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围

const{ container }= props;

router =newVueRouter({

//如果是子应用就设置标识

base:window.__POWERED_BY_QIANKUN__ ?'/app-vue/':'/',

mode:'history',

routes,

});

instance =newVue({

router,

store,

render: h =>h(App),

}).$mount(container ? container.querySelector('#app'):'#app');

}

// 是否独立运行

if(window.__POWERED_BY_QIANKUN__){

__webpack_public_path__ =window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;

}else{

render();

}

// 导出相应的生命周期钩子

exportasyncfunctionbootstrap(){

console.log('[vue] vue app bootstraped');

}

exportasyncfunctionmount(props){

console.log('[vue] props from main framework', props);

render(props);

}

exportasyncfunctionunmount(){

instance.$destroy();

instance.$el.innerHTML ='';

instance =null;

router =null;

}

2.打包配置修改(vue.config.js):

设置跨域和打包格式

const{ name }=require('./package');// 不重要。自己起也可以

module.exports ={

devServer:{

headers:{

'Access-Control-Allow-Origin':'*',// 设置跨域

},

},

configureWebpack:{

output:{

library:`${name}-[name]`,// 配置导出库的名称

libraryTarget:'umd',// 把子应用打包成 umd 库格式

jsonpFunction:`webpackJsonp_${name}`,//不是必须的

},

},

};

3.子应用生命周期钩子介绍

子应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 bootstrap、mount、unmount三个生命周期钩子,以供主应用在适当的时机调用。

/**

  • bootstrap 只会在子应用初始化的时候调用一次,下次子应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。

  • 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。

*/

exportasyncfunctionbootstrap(){

console.log('react app bootstraped');

}

/**

  • 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法

*/

exportasyncfunctionmount(props){

ReactDOM.render(, props.container ? props.container.querySelector('#root'): document.getElementById('root'));

}

/**

  • 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载子应用的应用实例

*/

exportasyncfunctionunmount(props){

ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root'): document.getElementById('root'));

}

/**

  • 可选生命周期钩子,仅使用 loadMicroApp 方式(手动挂载)加载子应用时生效

*/

exportasyncfunctionupdate(props){

console.log('update props', props);

}

父子通信(父子应用store分离的方案实现)

使用vuex的原因是为了获取主应用的实时数据,也为了实现子应用主应用的相互通信。任何项目中vuex都有base.js,用来存储公共信息。比如token、用户名等。(现项目token等存储在localstorage中)子应用单独启动时用自己的vuex中的base.js来存储信息,base.js只能主应用进行修改,同步到子应用。(不考虑子应用单独运行可以不在子应用维护base.js)

step1:主应用向子应用传递store实例。

image.png

step2:子应用使用主应用共享的store实例并设置只属于子应用的vuex

针对第一种情况,就是在入口文件中引入vuex,并使用该插件,进而在创建vue实例的时候,传入主应用共享的store。

Vuex正常使用的时候,所有的状态值都是响应式的,可以直接用于Vue页面之中,但是这里是非响应式的,导致这个的原因其实十分简单。这里的store实例是由主应用传递过来的,store中的状态对于主应用的vue实例而言是亲儿子,是响应式的,在子应用中,虽然可以使用共享store实例中的commit方法,但是对于子应用的实Vuex例而言,不是亲儿子,是非响应式的,这样分析之后,解决方案就十分明确:

在子应用中将共享的****store****实例进行响应式设置,这是****Vue****现有的****API****方法****Vue.observable(store)

image.png

子应用的文件夹格式和使用方式

image.png
image.png

step4:子应用之间通信

子应用之间的通信可以通过父应用的vuex来传递。

登录

1.1把子应用的登录页面移到主应用

1.2保留子应用的登录,保证可以单独启动(目前登录信息存储在localstorage中)

路由

1.3因为qiankun的特性主应用调用子应用是路由前要加子项目标识

列:子应用中路由为/index,子应用标识为/ child01,主应用的该路由就为/child01 /index

主应用和子应用设置标识必须相同

后台返回的权限路由都要加子应用标识

遇到的问题

1、应用的部署,需要通过nginx做反向代理,把指向子应用的访问路径转发到子应用的服务入口

2、子应用套孙子应用未考虑,一个页面多个子应用未考虑;

应用部署nginx

子应用中需配置,跨域

location / {    

   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';

   if ($request_method = 'OPTIONS') {

       return 204;

   }

}

让微应用文件更新之后访问新的文件

问题描述:发版vue项目后,总是要强制刷浏览器才能生效

vue-cli里的默认配置,css和js的名字都加了哈希值,所以新版本css、js和就旧版本的名字是不同的,不会有缓存问题。

但是把打包好的index.html放到服务器里去的时候,index.html在服务器端可能是有缓存的,这需要在服务器配置不让缓存index.html

解决方法如下:

前端在index.html中添加:

   






nginx 配置如下:

location = /index.html {

   add_header Cache-Control "no-cache, no-store";

}

开发规范

1.所有的资源(图片/音视频等)都应该放到src目录,不要放在public或者static

资源放 src 目录,会经过 webpack 处理,能统一注入 publicPath。否则在主项目中会404。

2.请给 axios 实例添加拦截器,而不是 axios 对象

// 正确做法:给 axios 实例添加拦截器

const instance = axios.create();

instance.interceptors.request.use(function () {/.../});

// 错误用法:直接给 axios 对象添加拦截器

axios.interceptors.request.use(function () {/.../});

3.避免 css 污染

组件内样式的css-scoped是必须的。

对于一些插入到body的弹窗,无法使用scoped,请不要直接使用原class修改样式,请添加自己的class,来修改样式。尽量不将元素插入body

.el-dialog{

/* 不推荐使用组件原有的class */

}

.my-el-dialog{

/* 推荐使用自定义组件的class */

}

4.谨慎使用 position:fixed

在父项目中,这个定位未必准确,应尽量避免使用,确有相对于浏览器窗口定位需求,可以用position: sticky,但是会有兼容性问题(IE不支持)。如果定位使用的是bottom和right,则问题不大。

5.给 body 、 document 等绑定的事件,请在 unmount 周期清除

js 沙箱只劫持了 window.addEventListener。使用

document.body.addEventListener 或者 document.body.onClick 添加的事件并不会被沙箱移除,会对其他的页面产生影响,请在 unmount 周期清除

你可能感兴趣的:(【qiankun】微前端项目搭建与通信)