什么是微前端
微前端
,听这名字感觉很超前,其实已经有很多公司都已经用上了该技术,例如我的项目组也用上了,跟后端的微服务很类似,用作解耦。现在的网站开发日渐复杂,通常会有很多独立的模块组成,分成不同的业务组,在之前用的是iframe
进行嵌套。随着技术的发展现在出现了微前端的概念,目前微前端框架有很多,如single-spa
qiankun(阿里)
Micro App(京东)
wujie(腾讯)
擎天(vivo)
等
无界官网
micro-app官网
乾坤官网
在微前端的架构下,需要一个主应用,也可以叫基座应用
,其次每一个独立的模块都可以叫做子应用
,子应用和主应用不受框架的限制,可以用任何框架任何技术。
举个栗子
在这张图我们可以把菜单和头部看做是一个主应用
,然后菜单中的每一个模块都可以看做一个子应用(也就是每一个单独的项目)
,这样就可以就行解耦,并且应用之间是可以进行通讯的。
这一章我们主要体验一下micro-app
支持一下自家产品。他的原理也很简单,就是把子应用做成一个webComponents
进行沙箱隔离,应用之间的通讯用的是CustomEvent
开整
在开整之前得吐槽一下,在github的issues提的很多问题,得不到及时回复,平时忙也能理解,如果使用vite
将变的比较复杂,做好准备。
tips:当子应用是vite应用时需要做特别的适配,适配vite的代价是巨大的,我们必须关闭沙箱功能,因为沙箱在module script
下不支持,这导致大部分功能失效,包括:环境变量、样式隔离、元素隔离、资源路径补全、baseroute 等。
- 框架我这边
主子应用
使用vite+vue3
,大家可以随意选择框架。 - 目录结构mfe->主应用,web放子应用,子应用可以有多个第一个是
main
3.主子应用安装依赖npm install 然后 启动主应用,和子应用使用 npm run dev。启动完成之后我这边主应用端口是5173,子应用是5174,当然也可以自己配置不要冲突就行。
4.主应用
安装micro-app
npm install @micro-zoe/micro-app
5.主应用
在views新建一个文件如views/page.vue
,子应用修改vite.config.ts
主子应用不要搞错,
子应用的vite.config.ts 增加代码
tips:如果是production记得换成服务器的地址,如果是本地增加一个自定义前缀随便写我这儿写的basename
export default defineConfig({
base: `${process.env.NODE_ENV === 'production' ? 'http://xxxxx.com' : ''}/basename/`,
plugins:[vue()]
})
6.在刚才主应用新建的page.vue 加入以下代码
<template>
<div>
<h1>子应用</h1>
<micro-app disable-sandbox disable-scopecss name='main' :url='url'></micro-app>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from "vue"
let url = ref('')
if (import.meta.env.MODE == "development") {
url.value = "http://localhost:5174/basename"
} else {
url.value = "http://xxxxxxxx/basename"
}
</script>
<style lang="less" scoped>
</style>
解释一下 micro-app 是个自定义组件可以直接用,如果报错可以配置以下代码忽略警告
主应用的vite.config.ts 修改
vue({
template: {
compilerOptions: {
isCustomElement: tag => /^micro-app/.test(tag)
}
}
})
其次是必须要设置 disable-scopecss 这个属性取消沙箱,不然打包之后会有乱码,也就是必须得放弃沙箱。
url 就是子应用的地址,本地的地址和服务器地址做个切换。
7.主应用
配置路由 主应用使用history模式,子应用换成hash模式createWebHashHistory
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/vue3:page*', //自定义地址
component: ()=> import('@/views/page.vue') //刚才主应用创建的page.vue
},
]
})
export default router
8.主应用加载子应用 main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import microApp from '@micro-zoe/micro-app'
const app = createApp(App)
app.use(router)
app.mount('#mfe')
microApp.start({
plugins:{
modules:{
main: [{
loader(code) {
if (import.meta.env.MODE === 'development') {
// 这里 basename 需要和子应用vite.config.js中base的配置保持一致
code = code.replace(/(from|import)(\s*['"])(\/basename\/)/g, all => {
return all.replace('/basename/', 'http://localhost:5174/basename/')
})
}
return code
}
}]
}
}
})
固定写法,有额外的子应用只需要改一下code.replace
正则和all.replace
第一个参数,以及第二个参数对应的地址就好,其他不用动
tips:注意modules下面的名字需要跟micro-app 的name对应 我这儿都是main
tips:主子应用不能使用同一个#App名字 我这儿主应用
的名称换成了mfe,app.mount('#mfe')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微前端</title>
</head>
<body>
<!--名字要变-->
<div id="mfe"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
访问主应用你会发现子应用已经出来了
tips:注意我得路由写的是/vue3
坑来了:你的图片如果加载不出来,加载静态资源的方式变了
//js获取
const logo = new URL('./assets/logo.png',import.meta.url).href
//template使用
<img alt="Vue logo" class="logo" :src="logo" width="125" height="125" />
如果还出不来,我也遇到了 需要把vite的版本降级为3.0.0方可好使
上线
打包主应用npm run build 记得换成服务器地址之前提到的
子应用需要修改vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import {join} from 'path'
import {writeFileSync} from 'fs'
// https://vitejs.dev/config/
export default defineConfig({
base: `${process.env.NODE_ENV === 'production' ? '上线地址' : ''}/basename/`,
plugins: [vue(), vueJsx(),(function () {
let basePath = ''
return {
name: "vite:micro-app",
apply: 'build',
configResolved(config:any) {
basePath = `${config.base}${config.build.assetsDir}/`
},
writeBundle (options:any, bundle:any) {
for (const chunkName in bundle) {
if (Object.prototype.hasOwnProperty.call(bundle, chunkName)) {
const chunk = bundle[chunkName]
if (chunk.fileName && chunk.fileName.endsWith('.js')) {
chunk.code = chunk.code.replace(/(from|import\()(\s*['"])(\.\.?\/)/g, (all:any, $1:any, $2:any, $3:any) => {
return all.replace($3, new URL($3, basePath))
})
const fullPath = join(options.dir, chunk.fileName)
writeFileSync(fullPath, chunk.code)
}
}
}
},
}
})() as any],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
build:{
outDir:"../basename"
}
})
解释一下 这是micro-app写的一个vite插件主要是子应用支持微前端做的直接贴进去就行了
outDir打包之后我输出到上层目录这个随意
子应用然后进行打包 npm run build
服务器我用的是宝塔方便
目录结构 主应用放到最外面,子应用直接把文件夹拖进去我得是basename
修改nginx代理
location /{
add_header Access-Control-Allow-Origin *;
if ( $request_uri ~* ^.+.(js|css|jpg|png|gif|tif|dpg|jpeg|eot|svg|ttf|woff|json|mp4|rmvb|rm|wmv|avi|3gp)$ ){
add_header Cache-Control max-age=7776000;
add_header Access-Control-Allow-Origin *;
}
try_files $uri $uri/ /index.html;
}
location /basename {
add_header Access-Control-Allow-Origin *;
if ( $request_uri ~* ^.+.(js|css|jpg|png|gif|tif|dpg|jpeg|eot|svg|ttf|woff|json|mp4|rmvb|rm|wmv|avi|3gp)$ ){
add_header Cache-Control max-age=7776000;
add_header Access-Control-Allow-Origin *;
}
try_files $uri $uri/ /basename/index.html;
}
上线预览
标签:vue,前端,micro,应用,import,app,vite From: https://blog.51cto.com/u_13463935/6024298