qiankun 方案
qiankun 是蚂蚁金服开源的一款框架,它是基于 single-spa 的。他在 single-spa 的基础上,实现了开箱即用,除一些必要的修改外,子项目只需要做很少的改动,就能很容易的接入。如果说 single-spa 是自行车的话,qiankun 就是个汽车。
微前端中子项目的入口文件常见的有两种方式:JS entry 和 HTML entry
纯 single-spa 采用的是 JS entry,而 qiankun 既支持 JS entry,又支持 HTML entry。
JS entry 的要求比较苛刻:
- 将 css 打包到 js 里面
- 去掉 chunk-vendors.js,
- 去掉文件名的 hash 值
- 将 single-spa 模式的入口文件( app.js )放置到 index.html 目录,其他文件不变,原因是要截取 app.js 的路径作为 publicPath
APP entry 优点 缺点 JS entry 可以配合 systemJs,按需加载公共依赖( vue , vuex , vue-router 等) 需要各种打包配置配合,无法实现预加载 HTML entry 打包配置无需做太多的修改,可以预加载 多一层请求,需要先请求到 HTML 文件,再用正则匹配到其中的 js 和 css
其实 qiankun 还支持 config entry :
{
entry: {
scripts: [
"app.3249afbe.js"
"chunk-vendors.75fba470.js",
],
styles: [
"app.3249afbe.css"
"chunk.75fba470.css",
],
html: http://localhost:5000
}
}
建议使用 HTML entry ,使用起来和 iframe 一样简单,但是用户体验比 iframe 强很多。qiankun 请求到子项目的 index.html 之后,会先用正则匹配到其中的 js/css 相关标签,然后替换掉,它需要自己加载 js 并运行,然后去掉 html/head/body 等标签,剩下的内容原样插入到子项目的容器中 :
使用 qiankun 的好处:
qiankun 自带 js/css 沙箱功能,singles-spa 可以解决 css 污染,但是需要子项目配合
single-spa 方案只支持 JS entry 的特点,限制了它只能支持 vue 、 react 、 angular 等技术开发的项目,对一些 jQuery 老项目则无能为力。qiankun 则没有限制
qiankun 支持子项目预请求功能。
js 沙箱
js/css 污染是无法避免的,并且是一个可大可小的问题。就像一颗定时炸弹,不知道什么时候会出问题,排查也麻烦。作为一个基础框架,解决这两个污染非常重要,不能仅凭“规范”开发。
js 沙箱的原理是子项目加载之前,对 window 对象做一个快照,子项目卸载时恢复这个快照,如图:
那么如何监测 window 对象的变化呢,直接将 window 对象进行一下深拷贝,然后深度对比各个属性显然可行性不高,qiankun 框架采用的是 ES6 新特性,proxy 代理方法。
但是 proxy 是不兼容 IE11 的,为了兼容,低版本 IE 采用了 diff 方法:浅拷贝 window 对象,然后对比每一个属性。
css 沙箱
qiankun 的 css 沙箱的原理是重写 HTMLHeadElement.prototype.appendChild 事件,记录子项目运行时新增的 style/link 标签,卸载子项目时移除这些标签。
single-spa 方案中用了换肤的思路来解决 css 污染:首先 css-scoped 解决大部分的污染,对于一些全局样式,在子项目给 body/html 加一个唯一的 id/class(正常开发部署用),然后这个全局的样式前面加上这个 id/class,而 single-spa 模式则在 mount 周期给 body/html 加上这个唯一的 id/class,在 unmount 周期去掉,这样就可以保证这个全局 css 只对这个项目生效了。
这两个方案的致命点都在于无法解决多个子项目同时运行时的 css 污染,以及子项目对主项目的 css 污染。
虽然说两个项目同时运行常见并不常见,但是如果想实现 keep-alive ,就需要使用 display: none 将子项目隐藏起来,子项目不需要卸载,这时候就会存在两个子项目同时运行,只不过其中一个对用户不可见。
css 沙箱还有个思路就是将子项目的样式局限到子项目的容器范围内生效,这样只需要给不同的子项目不同的容器就可以了。但是这样也会有新的问题,子项目中 append 到 body 的弹窗,样式就无法生效。所以说样式污染还需要制定规范才行,约定 class 命名前缀。
微前端方案实践
改造已有的项目为 qiankun 子项目,由于是 vue 技术栈,所以就以改造一个 vue 项目为例说明,其他的技术栈原理是一样的。
- 在 src 目录新增文件 public-path.js:
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
- 修改 index.html 中项目初始化的容器,不要使用 #app ,避免与其他的项目冲突,建议换成项目 name 的驼峰写法
- 修改入口文件 main.js:
import ./public-path;
import Vue from vue
import App from ./App.vue
import VueRouter from vue-router
import store from ./store;
Vue.use(VueRouter)
Vue.config.productionTip = false
let router = null;
let instance = null;
function render(parent = {}) {
const router = new VueRouter({
// histroy模式的路由需要设置base,app-history-vue根据项目名称来定
base: window.__POWERED_BY_QIANKUN__ ? /app-history-vue : /,
mode: history,
// hash模式不需要上面两行
routes: []
})
instance = new Vue({
router,
store,
render: h => h(App),
data(){
return {
parentRouter: parent.router,
parentVuex: parent.store,
}
},
}).$mount(#appVueHistory);
}
//全局变量来判断环境,独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log(vue app bootstraped);
}
export async function mount(props) {
console.log(props from main framework, props);
render(props.data);
}
export async function unmount() {
instance.$destroy();
instance = null;
router = null;
}
主要改动是引入修改 publicPath 的文件和 export 三个生命周期。
注意:
webpack 的 publicPath 值只能在入口文件修改,之所以单独写到一个文件并在入口文件最开始引入,是因为这样做可以让下面所有的代码都能使用这个。 路由文件需要 export 路由数据,而不是实例化的路由对象,路由的钩子函数也需要移到入口文件。 在 mount 生命周期,可以拿到父项目传递过来的数据,router 用于跳转到主项目/其他子项目的路由,store 是父项目的实例化的 Vuex。
- 修改打包配置 vue.config.js:
const { name } = require(./package);
module.exports = {
devServer: {
headers: {
Access-Control-Allow-Origin: *,
},
},
// 自定义webpack配置
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: umd,// 把子应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,
},
},
};
注: 这个 name 默认从 package.json 获取,可以自定义,只要和父项目注册时的 name 保持一致即可。
这个配置主要就两个,一个是允许跨域,另一个是打包成 umd 格式。为什么要打包成 umd 格式呢?是为了让 qiankun 拿到其 export 的生命周期函数。可以看下其打包后的 app.js 就知道了:
root 在浏览器环境就是 window , qiankun 拿这三个生命周期,是根据注册应用时,你给的 name 值,name 不一致则会导致拿不到生命周期函数
标签:方案,vue,js,qiankun,子项目,entry,css From: https://www.cnblogs.com/wp-leonard/p/17130250.html