【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(<App />, 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中添加:

   <meta http-equiv="Expires" content="0">

<meta http-equiv="Pragma" content="no-cache">

<meta http-equiv="Cache-control" content="no-cache">

<meta http-equiv="Cache" content="no-cache">

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 周期清除

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容