发布于2021-05-30 11:34 阅读(1293) 评论(0) 点赞(2) 收藏(3)
一个WEB端管理系统包含好几个单独的模块,相互之间没有耦合,如果放到同一个项目里,同时好几个人去维护,不利于管理,单独子模块的上线会对整个项目全量上线风险比较大,并且容易代码冲突
微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。微前端的核心在于拆,拆完后在合!
我们可以将一个应用划分成若干个子应用,将子应用打包成一个个的 lib 。当路径切换 时加载不同的子应用。这样每个子应用都是独立的,技术栈也不用做限制了!从而解决了前端协同开发问题。
2018 年 Single-SPA 诞生了, single-spa 是一个用于前端微服务化的 JavaScript 前端解决方案 ( 本身没有处理样式隔离, js 执行隔离 ) 实现了路由劫持和应用加载。
2019 年 qiankun 基于 Single-SPA, 提供了更加开箱即用的 API ( single-spa + sandbox + import-html-entry ) 做到了,技术栈无关、并且接入简单(像 i frame 一样简单)。
demo地址:https://github.com/li1164267803/qiankun-demo
查看demo的时候三个项目都要跑起来,然后在主服务里面看效果
这里我们打算建立三个项目进行实操,一个 Vue 项目充当主应用,另一个 Vue 和 React 应用充当子应用
vue create qiankun-box
vue create qiankun-vue
cnpm install -g create-react-app
create-react-app qiankun-react
三个项目
基座:qiankun-base 子应用:qiankun-vue、qiankun-react
项目创建好后我们首先进行主应用 qiankun-base 的配置,进入 man.js 文件进行配置, 在 main.js 中加入以下代码,要注意的是,entry 这项配置是我们两个子项目的域名和端口,我们必须确保两字子项目运行在这两个端口上面,container 就是我们的容器名,就是我们子应用挂载的节点,相当于 Vue 项目里面的 app 节点,activeRule 就是我们的激活路径,根据路径来显示不同的子应用。
npm i qiankun -S
// 引入qiankun
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: "vueApp", // 应用的名字
entry: "//localhost:3001", // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
container: "#container", // 容器id
activeRule: "/app/vue" // 根据路由 激活的路径
},
{
name: "reactApp",
entry: "//localhost:20000",
container: "#container",
activeRule: "/app/react"
},
]);
start();
npm i element-ui -S
在 main.js 中引入 element-ui:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
<template>
<div id="app">
<el-menu :router="true" mode="horizontal">
<el-menu-item index="/home">
<span slot="title">Home</span>
</el-menu-item>
<!--引用其他子应用-->
<el-menu-item index="/app/vue">
<span slot="title">qiankun-vue</span>
</el-menu-item>
<el-menu-item index="/app/react">
<span slot="title">qiankun-react</span>
</el-menu-item>
</el-menu>
<router-view></router-view>
<div id="container"></div>
</div>
</template>
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',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
// base: process.env.BASE_URL,
base: '',
routes
})
export default router
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('#qkApp'); // 这里是挂载到自己的html中 基座会拿到这个挂载后的html 将其插入进去
}
if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行
render();
}
// 父应用加载子应用,子应用必须暴露三个接口:bootstrap、mount、unmount
// 子组件的协议就ok了
export async function bootstrap(props) {
};
export async function mount(props) {
render(props)
}
export async function unmount(props) {
instance.$destroy();
}
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',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: '/vue',
routes
})
export default router
在子应用的根目录下面新建一个 Vue.config.js 文件
module.exports = {
lintOnSave: false, // 关闭eslint检测
devServer: {
port: 3001,//这里的端口是必须和父应用配置的子应用端口一致
headers: {
//因为qiankun内部请求都是fetch来请求资源,所以子应用必须允许跨域
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
//资源打包路径
library: 'vueApp',
libraryTarget: 'umd'
}
}
}
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')); // 卸载节点
}
先引入 react-app-rewired,在修改 package.json 启动命令
npm install react-app-rewired
修改 package.json 启动命令
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
再进行 dev 以及打包的配置,根目录下创建 config-overrides.js
module.exports = {
webpack: (config) => {
config.output.library = 'reactApp';
config.output.libraryTarget = 'umd';
config.output.publicPath = 'http://localhost:20000/'; // 此应用自己的端口号
return config;
},
devServer: (configFunction) => {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
config.headers = {
"Access-Control-Allow-Origin": '*'
}
return config
}
}
}
react
+ react-router
技术栈的主应用:只需要让子应用的 activeRule
包含主应用的这个路由即可。
vue
+ vue-router
技术栈的主应用:
例如:主应用需要在 login 页面登录,登录成功后跳转到 main 后台管理界面,在 main 管理界面下可以显示子应用。
修改主应用 router.js:
// 如果这个路由有其他子路由,需要另外注册一个路由,任然使用这个组件即可。
// 本案例就是有子路由,所以需要才后面重新定义main页面的路由
const routes = [
{
path: "/",
name: "Login",
component: Login
},
{
path: "/main",
name: "Main",
component: Main,
children: [
{
path: "/home",
name: "Home",
component: () => import(/* webpackChunkName: "about" */ "../views/Home")
}
]
},
{
path: "/app/*", // 所有的子应用要走的路径
name: "Main",
component: Main
}
]
修改主应用 main.js 的文件:
// 子应用的 activeRule 需要包含主应用的这个路由 path
import { registerMicroApps, start} from "qiankun";
registerMicroApps([
{
name: "vueApp", // 应用的名字
entry: "//localhost:3001", // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
container: "#container", // 容器id
activeRule: "/app/vue" // 根据路由 激活的路径
},
{
name: "reactApp",
entry: "//localhost:20000",
container: "#container",
activeRule: "/app/react"
},
]);
start();
修改主应用 main.vue 页面代码:
```javascript
// 在 Main.vue 这个组件的 mounted 周期调用 start 函数,注意不要重复调用。
<template>
<div class="main-content">
<el-menu :router="true" mode="horizontal">
<el-menu-item index="/home">
<span slot="title">Home</span>
</el-menu-item>
<!--引用其他子应用-->
<el-menu-item index="/app/vue">
<span slot="title">qiankun-vue</span>
</el-menu-item>
<el-menu-item index="/app/react">
<span slot="title">qiankun-react</span>
</el-menu-item>
</el-menu>
<router-view></router-view>
<div id="container"></div>
</div>
</template>
<script>
import { start } from "qiankun";
export default {
name: "Main",
mounted() {
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start();
}
},
};
</script>
微应用的main.js 中一定要加上测试环境开启 devTools 的代码
写入到 render 函数中
// vue-devtools 调试
if (window.__POWERED_BY_QIANKUN__ && process.env.NODE_ENV === "development") {
// vue-devtools 加入此处代码即可
// After you create app
window.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = instance.constructor;
let instanceDiv = document.createElement("div");
instanceDiv.__vue__ = instance;
document.body.appendChild(instanceDiv);
}
在vue.config.js 中配置
devServer: {
hot: true,
disableHostCheck: true,
overlay: {
warnings: false,
errors: true
},
headers: {
"Access-Control-Allow-Origin": "*" // 子应用必须支持跨域
},
}
作者:小兔崽子
链接:http://www.qianduanheidong.com/blog/article/116023/4558d4f5e6944c3a3af3/
来源:前端黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 前端黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-3
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!