# 快速开始

# 子应用开发者

# 安装编译插件

npm install @jmodule/plugin-webpack

TIP

请先检查项目依赖:webpack@4.x 或 webpack@5.x

# 子应用定义

以vue3为例,在项目入口文件中添加 JModule.define('xxx', {...});

完整示例参考 child-app-vue3 (opens new window)

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App);
if (window.__JMODULE_HOST__) {
    // 以自应用模式启动
    JModule.define('childAppVue3', {
        mount(module, el, stats) {
            if (stats.mountTimes === 0) {
                app.mount(el);
            }
        },
    });
} else {
    // 应用自启动
    app.mount('#app')
}

其它框架参考示例:

  1. vue2, 子应用模式 (opens new window)
  2. vue2, 路由模式 (opens new window)
  3. react, 子应用模式 (opens new window)

TIP

  1. 具体规则定义,依赖宿主应用规约。文档中的字段规约与项目 demo (opens new window) 中一致。
  2. 比如demo 中的路由模式,前端框架依赖宿主应用的实现。

# 配置编译插件

// .jmodule.conf.js
module.exports = {
    mode: 'modules',
    devConfig: process.env.NODE_ENV === 'development' ? {
        modulesConfig: {
            childAppVue3: {
                type: 'app',
                name: 'Vue3 的子应用',
            },
        },
        currentServer: 'http://localhost:8080', // 当前子应用开发服务地址
        platformServer: 'http://localhost:3002', // 宿主应用服务地址
        platformLocalPort: '8090', // 集成调试服务端口,在这个端口可进行集成开发调试
        onAllServerReady: () => { // 自动探测服务,当所有服务正常启动后执行
            console.log(
                chalk.green('\n\t已启动集成服务: '),
                chalk.cyanBright(`http://localhost:${platformLocalPort}/`),
                '\n',
            );
        },
    } : undefined,
}

  1. 构建完成,项目中将自动生成 index.js 文件,即为接入宿主应用的入口文件
  2. 开发过程中,通过访问 platformLocalPort 定义的端口,可以在宿主应用的环境中直接访问子应用开发环境的服务
  3. 为确保热更新的正常使用,开发模式需要设置 publicPath, 参考 vue.config.js (opens new window), 其它框架类似。

TIP

更多高级特性参考:plugin-webpack

# 导出子应用的组件

子应用可以导出更细粒度的组件,导出的组件列表可以被宿主应用或者其他子应用使用。在组件共享、插件开发场景中有极大帮助。

vue3工程为例,其他工程相同

  1. 导出一个组件
import { createApp } from 'vue'
import App from './App.vue'
import PipelineApp from './PipelineApp.vue'

const app = createApp(App);
if (window.__JMODULE_HOST__) {
    // 创建一个 app 类型的子应用
    JModule.define('pipeline', {
        mount(module, el, stats) { // 如果仅共享组件,则不需要 mount
            if (stats.mountTimes === 0) {
                app.mount(el);
            }
        },
        exports: { // 导出一个组件
            PipelineApp,
        }
    });
} else {
    app.mount('#app')
}

  1. 在其他子应用中使用该组件
const getPipelineApp = JModule.require('pipeline.models.PipelineApp');

getPipelineApp.then((PipelineApp) => {
    const pipelineAppIns = new PipelineApp({ appId: this.appId });
});

# 定义基准路由

举例:

// vue2
const router = new VueRouter({
    mode: 'history',
    base: 'blue', // blue 为 moduleKey,请自行替换
    routes
})
// react
    <Router basename={getBasename()}>

# 获取publicPath

# 获取方式

JModule.import('$module.meta')

# 结合webpack使用

 __webpack_public_path__ =  JModule.import('$module.meta')?.server

# Demo

vue2 (opens new window)

vue3 (opens new window)

react (opens new window)

# 宿主应用开发者

# 安装 @jmodle/client

npm install @jmodle/client --save

# 建立子应用定义规约

即根据自身需要,读取子应用 JModule.define 的配置参数进行进一步处理。即这里用什么字段,子应用才提供什么字段。

JModule.addHook('afterInit', (module, pkg) => {
    // 实现子应用配置
    if (module.type === 'app') {
        return appAdapter(module, pkg);
    }
    // 实现基于路由的配置
    if (module.type === 'module') {
        return moduleAdapter(module, pkg);
    }
    return [module, pkg];
});

adapter实现具体参考:

moduleAdapter (opens new window)

appAdapter (opens new window)

可以根据需要,自行扩展其它 type 以及对应的 adapter。

# 注册子应用

const modules = JModule.registerModules([
    { key: 'app1', url: 'http://localhost:8080/index.js'},
    { key: 'app2', url: '...' }
]);

# 子应用预加载

modules.forEach(module => module.load('preload'));

# 子应用挂载

  1. 对于基于动态路由的子应用挂载,由宿主 router 处理,通常不需要额外处理,但可以通过路由钩子来实现细粒度控制。

  2. 对应通过生命周期方式挂载的完整子应用,需要一些特别处理,主要包括:

    2.1 监听路由变化

    2.2 按路由规则判断当前需要挂载的 module

    2.3 加载模块:module.load()

    2.4 bootstrapModule()

    2.5 mountModule()

一段完整的示例可以参考:host-vue2 (opens new window)