qiankun项目转vite的思考
Qiankun 的优点:
- 微前端架构支持:Qiankun 是一个微前端框架,它允许将大型单体应用拆分为多个小型、独立的前端应用,这些应用可以独立开发、部署和扩展。这种架构模式提高了前端应用的可维护性和可扩展性。
- 灵活的应用间通信:Qiankun 提供了灵活的方式来实现应用间的通信,包括全局状态管理、事件总线等,这使得在微前端架构中实现复杂的交互变得更加容易。
- 良好的生态系统:虽然 Qiankun 本身是一个相对较新的框架,但它已经积累了一套丰富的生态系统,包括各种插件和工具,可以帮助开发者更高效地构建微前端应用。
Vite 的优点:
- 极快的冷启动速度:Vite 使用 ES Modules(ESM)进行开发服务器的构建,这使得它能够提供极快的冷启动速度,几乎无需等待构建过程即可开始开发。
- 内置的热模块替换(HMR) :Vite 的开发服务器支持热模块替换(HMR),这意味着在开发过程中,只有修改过的文件会被重新加载,大大提高了开发效率。
- 现代化的构建工具:Vite 支持最新的 JavaScript 特性,如 ES6+、TypeScript、CSS 预处理器等,并且它的构建工具链非常现代化,包括 Rollup 作为构建工具和 esbuild 作为预构建工具,这使得 Vite 在构建性能上表现出色。
结合使用 Qiankun 和 Vite:
尽管 Qiankun 和 Vite 都是现代前端的重要工具,但它们之间的兼容性并不是自动的。然而,通过使用 vite-plugin-qiankun
插件,开发者可以在 Qiankun 微前端架构中集成 Vite 构建的子应用。这意味着开发者可以享受到 Vite 的快速开发和高效构建,同时也能利用 Qiankun 的微前端架构优势,构建出高效、可维护的前端应用
打包
-
首先是打包文件的改变:
- 主应用vite, 改动较少,主要是全局定义和vite的基础配置的改变,在打包的时候注意把环境带上,默认是dev环境:
pnpm run build -- ${env}
import react from '@vitejs/plugin-react' import path from 'path' import { defineConfig } from 'vite' import tsconfigPaths from 'vite-tsconfig-paths' const cwd = process.cwd() const args = process.argv.slice(2) // https://vitejs.dev/config/ export default defineConfig({ define: { VITE_BASE_ENV: JSON.stringify(args?.[args?.length - 1] || 'dev'), }, build: { outDir: path.resolve(cwd, 'dist'), chunkSizeWarningLimit: 1600, // Set the warning limit to 1600 KiB }, plugins: [react(), tsconfigPaths()], server: { host: '127.0.0.1', headers: { 'Access-Control-Allow-Origin': '*', }, port: 8080, proxy: { '/api': { target: 'https://xxx.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), secure: false, }, }, }, css: { preprocessorOptions: { less: { javascriptEnabled: true, }, }, }, base: '/', resolve: { alias: { '@': path.resolve(cwd, 'src'), }, }, })
-
副应用vite,需要引入qiankun-vite插件
@quni/vite-qiankun-plugin
,并且base需要判断是否是生产环境还是开发环境,因为两个的路径前缀可能不一样
//vite import react from '@vitejs/plugin-react' import path from 'path' import { defineConfig } from 'vite' import qiankun from '@quni/vite-qiankun-plugin' const cwd = process.cwd() const args = process.argv.slice(2) // https://vitejs.dev/config/ export default defineConfig(({ mode }) => ({ define: { VITE_BASE_ENV: JSON.stringify(args?.[args?.length - 1] || 'dev'), }, plugins: [ react({ babel: { babelrc: false, // 支持解析装饰器【结合 qiankunPlugin 处理开发环境热更新问题】 plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]], }, }), qiankun('children', {//name保证唯一 useDevMode: true, }), ], base: mode === 'development' ? '/' : '/subapp/chilren/', build: { outDir: path.resolve(cwd, 'dist'), chunkSizeWarningLimit: 1600, // Set the warning limit to 1600 KiB }, server: { host: 'xxx', headers: { 'Access-Control-Allow-Origin': '*', }, port: 8081, proxy: { '/api': { target: 'https://xxx.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), secure: false, }, }, }, css: { preprocessorOptions: { less: { javascriptEnabled: true, }, }, }, resolve: { alias: { '@': path.join(cwd, './src'), }, }, }))
- 主应用vite, 改动较少,主要是全局定义和vite的基础配置的改变,在打包的时候注意把环境带上,默认是dev环境:
为了避免子系统的报错问题和路径相关问题,在vite-plugin-qiankun的基础上做了改造:改为用@quni/vite-qiankun-plugin:
pnpm add @quni/vite-qiankun-plugin -D
- 主应用入口配置:
import { registerMicroApps, start } from 'qiankun'
...
const microApps = [{
name: 'children-app',
entry: 127.0.0.1:8080/subapp/children, //子应用实际路径 端口号可以和主应用不一样 写成一样方便打包调试
activeRule: '/children',//绑定在父应用上的路径
container: '#sub-app', //子应用挂载的div
}]
registerMicroApps(microApps, {
beforeLoad: [async (app) => console.log('before load', app.name)],
beforeMount: [async (app) => console.log('before mount', app.name)],
afterMount: [async (app) => console.log('after mount', app.name)],
})
start()
...
- 子应用配置:
子应用需要导出qiankun的生命周期:
import { qiankunWindow, renderWithQiankun } from '@quni/vite-qiankun-plugin/cjs/helper'
// 导出 qiankun 的生命周期钩子函数
renderWithQiankun({
bootstrap() {
console.log('react app bootstraped')
},
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
mount(props: any) {
render(props)
},
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
unmount() {
root.unmount()
},
update(props: any) {
console.log('[react18] react app update', props)
},
})
同时在root.render的时候,通过qiankunWindow判断,因为在vite中__POWERED_BY_QIANKUN__
绑定的为proxy对象:
/children为父应用的跳转路径
import { qiankunWindow, renderWithQiankun } from '@quni/vite-qiankun-plugin/cjs/helper'
root.render(
//@ts-ignore
<BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? '/children' : ''}>
<App />
</BrowserRouter>,
)
模拟运行调试:
为了保证发布到生产环境的代码稳定,需要调试打包好的dist代码
const Koa = require('koa')
const Router = require('koa-router')
const mount = require('koa-mount')
const path = require('path')
const koaServerHttpProxy = require('koa-server-http-proxy')
const args = process.argv.slice(2)
const host = '127.0.0.1'
const app = new Koa()
const router = new Router()
// 静态文件夹路径
const distPath1 = './packages/main/dist' // 替换为你的第一个dist文件夹路径
const distPath2 = './packages/children/dist' // 替换为你的第二个dist文件夹路径
// 使用 koa-mount 中间件挂载不同的dist文件夹
app.use(mount('/', require('koa-static')(path.join(__dirname, distPath1))))
app.use(mount('/subapp/children', require('koa-static')(path.join(__dirname, distPath2))))
app.use(
koaServerHttpProxy('/api', {
target: 'https://XXX.com',
pathRewrite: { '^/api': '' },
changeOrigin: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
}),
)
app.listen(8080, host)
webpack使用esbuild-builder提高打包速度:
如果项目是webpack开发qiankun的话,可以使用esbuild-builder提高打包速度。
使用esbuild-builder配置简单,改动较小
打包rules:
{
oneOf: [
{
test: /\.[jt]sx$/,
exclude: /node_modules/,
use: [
{
loader: 'esbuild-loader',
options: {
target: 'es2015',
loader: 'tsx',
},
},
path.resolve(__dirname, './jsx-loader'),
],
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'esbuild-loader',
options: {
target: 'es2015',
loader: 'ts',
},
},
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'esbuild-loader',
options: {
target: 'es2015',
loader: 'jsx',
},
},
],
},
],
},
文件压缩:
optimization: {
...,
minimizer: [
new EsbuildPlugin({
target: 'es2015',
}),
],
},
注意webpack的globalObject和libraryTarget需要配置为window
output: {
...
globalObject: 'window',
libraryTarget: 'window',
},
转载自:https://juejin.cn/post/7368313344711032859