微前端
最近项目需要解耦,比如系统设置相关的功能抽成了一个权限系统,而多个项目需要使用它,当其中一个项目中需要对功能进行升级,其它需要同样更新的项目也需要更新代码,产生的开销比较大。
如果将这些项目解耦,各自开发,在需要的时候插入到基座上,就不用担心项目之间的依赖关系,也不用担心项目之间的代码冲突。而微前端很好的解决了这个问题,微应用都放在各自的沙箱中。
基座项目改造
安装qiankun
微应用配置
zhName
和entry_dev
为我的自定义字段,用于开发需求,其它字段是qiankun
框架必须的。
{ "APPCODE": "MicroBaseApp", "MICRO_APPS": [ { "zhName": "应用1", "name": "app1", "entry": "http://xxx.com", "entry_dev": "localhost:8080", "container": "#appContainer", "activeRule": "/app1" }, { "zhName": "应用2", "name": "app2", "entry": "http://xxx.com", "entry_dev": "localhost:8081", "container": "#appContainer", "activeRule": "/app2" }, { "zhName": "应用3", "name": "app3", "entry": "http://xxx.com", "entry_dev": "localhost:8082", "container": "#appContainer", "activeRule": "/app3" } ] }
|
模板文件添加挂载容器
<div id="app"></div>
<div id="appContainer"></div>
|
注册微应用并启动
import { registerMicroApps, start } from 'qiankun'; import axios from './axios';
axios.get(`${process.env.BASE_URL}config/${process.env.VUE_APP_CONFIG}.json?t=${new Date().valueOf()}`).then(res => { const cnotConfig = getConfig(res.data) Vue.prototype.$config = cnotConfig const baseVue = new Vue({ router, store, render: h => h(App) }).$mount('#app') baseVue.$nextTick(() => { const { MICRO_APPS: microApps } = cnotConfig const entryAttr = process.env.NODE_ENV === 'development' ? 'entry_dev' : 'entry' registerMicroApps(microApps.map((app) => ({ ...app, entry: app[entryAttr], props: { cnotConfig } })))
start({ opts: 'all', }) }) })
|
如果跨域,还需要配置devServer。
微应用项目改造
创建public-path.js,解决静态资源加载问题。
if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ }
|
main.js改造
import './public-path' let instance = null function render({container, parentStore} = {}){ instance = new Vue({ router, store, data(){ return { parentStore } }, render: h => h(App) }).$mount(container ? container.querySelector('#app') : '#app') }
if (!window.__POWERED_BY_QIANKUN__) { axios.get(`${process.env.BASE_URL}config/${process.env.VUE_APP_CONFIG}.json?t=${new Date().valueOf()}`).then(res => { Vue.prototype.$config = getConfig(res.data) render() }) }
export async function bootstrap () { console.log('cnot vue app bootstraped') } export async function mount (props) { axios.get(`${__webpack_public_path__}config/${process.env.VUE_APP_CONFIG}.json?t=${new Date().valueOf()}`).then(res => { Vue.prototype.$config = getConfig(res.data) render(props) }) } export async function unmount () { instance.$destroy() instance.$el.innerHTML = '' instance = null }
|
package.json需要添加name字段,需和基座name保持一致,配合webpack使用。
const { name } = require('./package')
output: { library: `${name}-[name]`, libraryTarget: 'umd', jsonpFunction: `webpackJsonp_${name}` }
|
同时,如果单页应用主容器使用了结构包装,需要通过判断是否显示导航以及面包屑等等。
<div class="fe-no-shrink" v-if="!isMicroApp"> <Navbar></Navbar> <Breadcrumb></Breadcrumb> </div> <script> import Navbar from '@/components/Navbar' import Breadcrumb from '@/components/Breadcrumb'
export default { name: 'Main', components: { Navbar, Breadcrumb }, data() { return { isMicroApp: !!window.__POWERED_BY_QIANKUN__ } } } </script>
|
warning: 微应用和微应用之间样式会隔离,但是基座和微应用之间样式会互相影响,如果发生不在预期内的变化,建议加前缀进行人为隔离。