本项目是对于vue-typescript-admin-element中后台框架的改造,利用微前端框架qiankun,将原本完整的单页应用进行独立子应用的分割。
技术栈简介
vue-typescript-admin-element框架为业内广泛使用的中后台框架vue-admin-element的typescript版本。
qiankun是一个在single-spa微前端框架基础二次封装的更为易用的微前端框架。在single-spa基础上增加了js沙盒,html加载解析等。
主应用
main.ts
import Vue from 'vue'
import 'normalize.css'
import ElementUI from 'element-ui'
import SvgIcon from 'vue-svgicon'
import '@/styles/element-variables.scss'
import '@/styles/index.scss'
import App from '@/App.vue'
import store from '@/store'
import router from '@/router'
import '@/icons/components'
import '@/permission'
import QiankunVue from '@/plugins/qiankun-vue'
Vue.use(ElementUI)
Vue.use(SvgIcon, {
tagName: 'svg-icon',
defaultWidth: '1em',
defaultHeight: '1em'
})
// 插件的加载
Vue.use(QiankunVue)
const qiankunVue = new QiankunVue([
{
name: 'dashboard',
entry: '//localhost:5001',
activeUrl: '/dashboard'
},
{
name: 'example',
entry: '//localhost:5002',
activeUrl: '/example'
}
])
Vue.config.productionTip = false
new Vue({
router,
store,
qiankunVue,
render: (h) => h(App)
}).$mount('#main')
本项目中,已经将qiankun封装成了一个vue的标准插件。在实例化时需要传入子项目的注册信息。
framework.vue
用于渲染子项目的容器
子应用
vue-dashboard和vue-example为两个子项目,其中主要的改变是vue.config.js和main.ts的改动。
vue.config.js
const path = require('path')
const { name } = require('./package')
const cors = require('cors')
const port = 5002
module.exports = {
devServer: {
port,
/* headers: {
'Access-Control-Allow-Origin': '*',
}, */
before(app, server) {
app.use(cors())
}
},
publicPath: process.env.NODE_ENV === 'production' ? '/vue-typescript-admin-template/' : `//localhost:${port}`, // TODO: Remember to change this to fit your need
lintOnSave: process.env.NODE_ENV === 'development',
pwa: {
name: name
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: [
path.resolve(__dirname, 'src/styles/_variables.scss'),
path.resolve(__dirname, 'src/styles/_mixins.scss')
]
}
},
/* configureWebpack: {
output: {
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
}
}, */
chainWebpack(config) {
// Provide the app's title in webpack's name field, so that
// it can be accessed in index.html to inject the correct title.
config.set('name', name)
config.plugin("html").tap(args => {
args[0].minify = false;
return args;
});
// 把子应用打包成 umd 库格式
config.output
.jsonpFunction(`webpackJsonp_${name}`)
.library(`${name}-[name]`)
.libraryTarget('umd')
}
}
devServer:这个开发模式下的服务需要允许跨域请求。因为,在微前端项目中,子项目通常都是独立部署到一个独立域名服务下,父项目在拉取子项目资源时,均为跨域访问。虽然在生产环境中,你可以通过配置反向代理等方式来规避跨域,但在开发环境下直接开放跨域访问最为简便。
publicPath:需要配置全域名访问。因为,子项目在运行时,自身会进行很多异步资源加载,如css和异步组件加载等,如果配置相对路径,会导致,子项目加载资源时,访问的是父项目的域名,导致资源加载不到。
umd打包配置:子项目必须用umd打包的方式输出,否则qiankun框架无法正确解析。
main.ts
import Vue from 'vue'
import 'normalize.css'
import ElementUI from 'element-ui'
import SvgIcon from 'vue-svgicon'
import '@/styles/element-variables.scss'
import '@/styles/index.scss'
import App from '@/App.vue'
import store from '@/store'
import router from '@/router'
import '@/icons/components'
import '@/permission'
Vue.use(ElementUI)
Vue.use(SvgIcon, {
tagName: 'svg-icon',
defaultWidth: '1em',
defaultHeight: '1em'
})
Vue.config.productionTip = false
let app: Vue | undefined = undefined
export async function bootstrap() {
console.log('example应用初始化')
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props: any) {
Vue.prototype.$isFramework = props.isFramework
Vue.prototype.$mainApp = props.mainInstance
if (!app) {
app = new Vue({
router,
store,
render: (h) => h(App)
})
}
app.$mount('#app')
props.callback(app)
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载子应用的应用实例
*/
export async function unmount() {
console.log('example应用卸载')
// 销毁
if (app) {
app.$destroy()
app = undefined
}
}
mount函数:当子项目加载时会调用的此函数。因此,需要在此函数中进行vue的实例化。
unmount函数:档子项目切换时,需要将当前的项目进行销毁。