1、简述
本文承接上一篇micro-app在vue-element-admi中的搭建,对micro-app在vue-element-admin中的一些平时开发中常用的功能做了一些研究。本文代码
2、路由
关于路由,这边从两方面进行研究,一方面是对菜单的配置,另一方面是页面之间的跳转。下面分别介绍一下:
2-1、路由菜单配置
关于路由的配置,之前搭建vue-element-admin项目时,将子应用嵌入到基座时已经对路由的配置进行了搭建,子应用1为例具体如下:
如果是配置菜单的话,以上配置就可以了。配置代码如下:
micro-app-element\vue-element-base\src\router\modules\first-child.js
// 子应用1路由菜单
import Layout from '@/layout'
import { CHILD_PREFIX } from '@/micro/config'
const appFirstRouter = {
path: `/${CHILD_PREFIX}/first-child`,
component: Layout,
redirect: `/${CHILD_PREFIX}/first-child`,
name: 'FirstChild',
meta: {
title: '子应用模块',
icon: 'nested'
},
children: [
{
path: 'menu1',
name: 'Menu1',
meta: { title: '子应用菜单1' }
},
{
path: 'menu2',
name: 'Menu2',
meta: { title: '子应用菜单2' }
}
]
}
export default appFirstRouter
micro-app-element\vue-element-first\src\router\modules\nested.js
/** When your routing table is too long, you can split it into small modules **/
import Layout from '@/layout'
console.log(' window.__MICRO_APP_BASE_ROUTE__', window.__MICRO_APP_BASE_ROUTE__)
const nestedRouter = {
path: window.__MICRO_APP_BASE_ROUTE__ || '/nested',
component: Layout,
// redirect: '/nested/menu1/menu1-1',
name: 'Nested',
meta: {
title: '子应用',
icon: 'nested'
},
children: [
{
path: 'menu1',
component: () => import('@/views/nested/menu1/index'), // Parent router-view
name: 'Menu1',
meta: { title: '子应用菜单11' }
},
{
path: 'menu2',
name: 'Menu2',
component: () => import('@/views/nested/menu2/index'),
meta: { title: '子应用菜单22' }
}
]
}
export default nestedRouter
2-2、页面之间跳转
我们代码开发中,有很多场景都是一个菜单中有一个标签或按钮,点击可以直接跳转到另一个菜单。因为之前都是在同一个应用里,跳转肯定是没问题的。但是使用微前端后,可能会从基座菜单跳转到子应用菜单,也可能会从子应用菜单跳转到基座菜单,还有可能是子应用直接互相跳转,下面对这些跳转都做了尝试
2-2-1、子应用之间的互相跳转
举例:从子应用1的菜单1跳转到子应用2的菜单1,实现效果如下图所示:
对于应用之间的跳转,有如下两个地方要修改:
(1)基座:在一开始搭建路由的时候,在基座中调用了子应用,如果要实现应用之间互相跳转,基座部分需要获取子路由传递的信息,具体实现如下:
micro-app-element\vue-element-base\src\layout\components\AppMain.vue
<micro-app
v-if="isChild"
v-bind="micro"
destory
@datachange="handleDataChange"
/>
/**
* 获取子路由传递的信息
* */
handleDataChange(event) {
const data = event.detail.data
if (data.route) this.$router.push({ name: data.route.name })
},
(2)子应用点击部分的设置
micro-app-element\vue-element-first\src\views\nested\menu1\index.vue
<div style="height: 50px;">
<el-button type="primary" @click="jumpApp2('DynamicTable')">我想从子应用1菜单1跳转到子应用2菜单1</el-button>
</div>
methods: {
/**
* 点击跳转到页面
* @param name
*/
jumpApp2(name) {
// 向基项目发送数据
window.microApp && window.microApp.dispatch({ route: { name }})
}
}
2-2-2、子应用跳转到基座
举例:从子应用1的菜单2跳转到基座图标页,实现效果如下图所示:
有了2-2-1的基础后,接下来的操作就方便很多,不需要再去修改基座了,之间在子应用里面实现跳转即可
micro-app-element\vue-element-first\src\views\nested\menu2\index.vue
<template>
<div style="padding:30px;">
我是子应用菜单2
<div style="height: 50px;">
<el-button type="primary" @click="jumpAppBase('Icons')">我想从子应用1菜单2跳转到基座图标页</el-button>
</div>
<el-alert :closable="false" title="menu 2" />
</div>
</template>
<script>
export default {
name: 'Menu2',
methods: {
/**
* 点击跳转到页面
* @param name
*/
jumpAppBase(name) {
// 向基项目发送数据
window.microApp && window.microApp.dispatch({ route: { name }})
}
}
}
</script>
2-2-3、基座跳转到子应用
举例:从基座引导页跳转到子应用1菜单2,实现效果如下图所示:
基座跳转到子应用与上面2-2-1和2-2-2方法不同,需要注意
首先从官网中可以看出,跳转的时候路由方式不一样,url也有所不用,如果是哈希需要加“#”,如果不知道自己的路由方式,可以在router/index.js下去找mode,默认是哈希。我这边就是哈希
接下来按照官网介绍进行配置即可
micro-app-element\vue-element-base\src\views\guide\index.vue
<template>
<div class="app-container">
<aside>
{{ $t('guide.description') }}
<a href="https://github.com/kamranahmedse/driver.js" target="_blank">driver.js.</a>
</aside>
<el-button icon="el-icon-question" type="primary" @click.prevent.stop="guide">
{{ $t('guide.button') }}
</el-button>
<div style="height: 50px;">
<el-button type="primary" @click="jumpApp1()">我想从基座跳转到子应用1菜单2</el-button>
</div>
</div>
</template>
<script>
import Driver from 'driver.js' // import driver.js
import 'driver.js/dist/driver.min.css' // import driver.js css
import steps from './steps'
import { CHILD_PREFIX } from '../../micro/config'
export default {
name: 'Guide',
data() {
return {
driver: null
}
},
mounted() {
this.driver = new Driver()
},
methods: {
guide() {
this.driver.defineSteps(steps)
this.driver.start()
},
/**
* 基座跳转到子应用
*/
jumpApp1() {
window.history.pushState(null, '', `/#/${CHILD_PREFIX}/first-child/menu2`)
// 主动触发一次popstate事件
window.dispatchEvent(new PopStateEvent('popstate', { state: null }))
}
}
}
</script>
3、数据通信
关于这个数据通信,官网里讲了如下四点,感觉看着蛮不舒服的。其实按我们正常的思维,传值与取值是一起使用的
(1)子应用获取来自基座应用的数据
(2)子应用向基座应用发送数据
(3)基座应用向子应用发送数据
(4)基座应用获取来自子应用的数据
所以将以上(1)(3)和(2)(4)合并起来作为两点进行研究
3-1、基座发送数据,子应用获取数据
基座向子应用传值有两种方式:
3-1-1、data属性发送数据
基座发送数据:
<micro-app
v-if="isChild"
v-bind="micro"
destory
:data="dataForChild"
@datachange="handleDataChange"
/>
dataForChild: { type: 'data属性发送数据给子应用' },
子应用获取数据:
created() {
const data = window.microApp.getData() // 返回基座下发的data数据
console.log('子应用获取父应用数据', data)
},
3-1-2、基座手动发送数据
基座
import microApp from '@micro-zoe/micro-app'
microApp.setData('first-child', { type: '基座手动发送新的数据' })
子应用
<template>
<div style="padding:30px;">
我是子应用菜单2
<div style="height: 50px;">
<el-button type="primary" @click="jumpAppBase('Icons')">我想从子应用1菜单2跳转到基座图标页</el-button>
</div>
<el-alert :closable="false" title="menu 2" />
</div>
</template>
<script>
export default {
name: 'Menu2',
created() {
// const data = window.microApp.getData() // 返回基座下发的data数据
// console.log('子应用获取父应用数据', data)
/** 绑定数据【data属性】监听事件 */
window.microApp && window.microApp.addDataListener(this.dataListener, true)
},
destroyed() {
/** 移除数据【data属性】监听事件 */
window.microApp && window.microApp.removeDataListener(this.dataListener)
},
methods: {
dataListener(data) {
console.log('来自基座应用的数据', data)
},
/**
* 点击跳转到页面
* @param name
*/
jumpAppBase(name) {
// 向基项目发送数据
window.microApp && window.microApp.dispatch({ route: { name }})
}
}
}
</script>
3-2、子应用发送数据,基座获取数据
关于子应用发送数据,基座获取数据的,我们在前面写跳转的时候就遇到了
子应用
// 向基项目发送数据
window.microApp && window.microApp.dispatch({ route: { name }})
基座
<micro-app
v-if="isChild"
v-bind="micro"
destory
:data="dataForChild"
@datachange="handleDataChange"
/>
/**
* 获取子路由传递的信息
* */
handleDataChange(event) {
const data = event.detail.data
if (data.route) this.$router.push({ name: data.route.name })
},
4、隔离
4-1、样式隔离
micro-app有默认的样式隔离,我简单试了一下
(1)修改子应用的颜色,子应用发生变化,但是不会影响到基座的样式
(2)修改基座颜色,基座会发生变化,不会影响到子应用
4-2、元素隔离
micro-app也有默认的元素隔离,元素不会逃离元素边界,子应用只能对自身的元素进行增、删、改、查的操作。
5、资源共享
官网描述:当多个子应用拥有相同的js或css资源时,可以指定这些资源在多个子应用之间共享,在子应用加载时直接从缓存中提取数据,从而提高渲染效率和性能。我大概可以理解为在基座中配置资源共享,这样子应用就可以直接用这个资源了。那我好奇要是子应用单独运行呢?这个资源还存在吗?或者是就直接没有这些共享的资源了?
因此自己亲自试了一下,将子应用1引入的样式注释掉,在基座中引入,基座是没看出来有什么问题,但是子应用1因为失去了样式就变的乱七八糟,关于这一点网上也没有找到太多的内容
但是在下面6、组件共享实现后,可以用同一方式实现样式共享,具体可看6中介绍。
6、组件共享
在目前微前端搭建的基础上,可以发现一些问题,公共组件。如果一个组件在基座和子应用中都需要用到,目前的状态是每个项目都写一遍,这样很麻烦。所以要想个办法实现组件共享
关于共享,我这边看到一个 monorepo(单一代码库) ,差不多意思是单个代码库里包含了许多项目的代码,但是后面没用该方法,所以也不知道好不好实现。
我这边用的是后台大佬分享的这篇文章中的方法三,大体参考,但是实际还需要修改一番,接下来看看我的实现步骤:
注:我这边把通用组件都放在基座base中,如果你想单独建一个项目来放通用组件也行,具体看个人想法。
6-1、项目目录结构
如下图所示目录结构。基座和子应用都放在一个大的文件夹micro-app-element下,假装base中的组件是通用组件,我现在在子应用1vue-element-first中进行一些操作,来实现组件的共享
6-2、修改vue.config.js
参考文章中是修改webpack.js配置。我们项目是vue-element-admin。webpack的相关配置我这边找到是在vue.config.js中配置的,所以路径相关在vue.config.js中配置即可。
// 注:根据自己的项目结果及项目名称修改
console.log('__dirname', __dirname, typeof __dirname, path.join(path.parse(__dirname).dir, 'vue-element-base/src'), '\r\n', path.parse(__dirname))
// 注:只需要添加baseComponent那一行即可,如果想调用子应用2,可以在或其它可以在重新配置一条
resolve: {
alias: {
'@': resolve('src'),
'baseComponent': path.join(path.parse(__dirname).dir, 'vue-element-base/src')
}
}
6-3、配置路由共享组件
这边在子应用1的路由中随便改一下首页的路径,让子应用1去访问基座中的首页页面
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
// component: () => import('@/views/dashboard/index'),
component: () => import('baseComponent/views/dashboard/index'),
name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard', affix: true }
}
]
},
然后因为三个应用内容都差不多,去基座改一下首页
接下来看效果:
6-4、import导入共享组件
在6-3中尝试了在路由中直接修改路径共享到基座菜单的方法,接下来在尝试一下在页面中通过import导入基座组件的方法吧。同样在基座中随便找了个组件Dropzone,照着抄抄就好了
micro-app-element\vue-element-first\src\views\nested\menu1\index.vue
<template>
<div style="padding:30px;">
<div style="height: 50px;">
<el-button type="primary" @click="jumpApp2('DynamicTable')">我想从子应用1菜单1跳转到子应用2菜单1</el-button>
</div>
<el-alert :closable="false" title="menu 1">
我是子应用菜单1
<div class="editor-container">
<dropzone id="myVueDropzone" url="https://httpbin.org/post" @dropzone-removedFile="dropzoneR" @dropzone-success="dropzoneS" />
</div>
<router-view />
</el-alert>
</div>
</template>
<script>
import Dropzone from 'baseComponent/components/Dropzone'
export default {
name: 'Menu1',
components: { Dropzone },
methods: {
dropzoneS(file) {
console.log(file)
this.$message({ message: 'Upload success', type: 'success' })
},
dropzoneR(file) {
console.log(file)
this.$message({ message: 'Delete success', type: 'success' })
},
/**
* 点击跳转到页面
* @param name
*/
jumpApp2(name) {
// 向基项目发送数据d
window.microApp && window.microApp.dispatch({ route: { name }})
}
}
}
</script>
然后同样为了明显的看清楚我这边子应用1嵌入了base组件,我同样在代码里加了一点描述用来区分
<div style="color: #ff4949;font-size: 18px;">我是基座的公共组件</div>
实现效果如下图所示:
7、总结
到这里,对micro-app在vue-element-admin中的使用就研究的差不多了,因为初学,可能也存在一些问题,所以欢迎搭建评论区留言纠正。