likes
comments
collection
share

qiankun项目转vite的思考

作者站长头像
站长
· 阅读数 51

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 的微前端架构优势,构建出高效、可维护的前端应用

打包

  • 首先是打包文件的改变:

    1. 主应用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'),
          },
        },
      })
            
    
    
    1. 副应用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-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
评论
请登录