Skip to content
索引

wujie框架应用(主子应用都是vite4+vue3)

改造

主应用中安装wujie的vue3组件

js
import WujieVue from 'wujie-vue3';
app.use(WujieVue);
import WujieVue from 'wujie-vue3';
app.use(WujieVue);

主应用改造

主应用的界面左侧是菜单栏,右侧是菜单的对应页面

主应用原先是采用动态组件的方式渲染右侧对应页面

js
<component
   v-if="dynamicComponent"
   :is="dynamicComponent"
></component>
<component
   v-if="dynamicComponent"
   :is="dynamicComponent"
></component>
js
let modules = import.meta.glob('../dynamicComponent/**/*.vue');
...
computed: {
  /** 动态组件 */
  dynamicComponent() {
    let filename = this.currentNode.routerName;
    ...
    return defineAsyncComponent(
      modules[`../dynamicComponent/${filename}.vue`]
      );
   }
},
let modules = import.meta.glob('../dynamicComponent/**/*.vue');
...
computed: {
  /** 动态组件 */
  dynamicComponent() {
    let filename = this.currentNode.routerName;
    ...
    return defineAsyncComponent(
      modules[`../dynamicComponent/${filename}.vue`]
      );
   }
},

现在把component动态组件替换成wujie框架

不在主界面进行动态组件的渲染,而是拆分至子应用,把相应路径传递给子应用,子应用收到后进行动态组件的渲染,这样就可以自行拆分页面到相应子应用

js
<WujieVue
   style="height: 100%"
   name="vue3"
   :url="vue3Url"
   :plugins="plugins"
></WujieVue>
<WujieVue
   style="height: 100%"
   name="vue3"
   :url="vue3Url"
   :plugins="plugins"
></WujieVue>
js
 /** 点击树节点也就是单个菜单时触发 */
    handleNodeClick(data) {
      ...
      wujieVue.bus.$emit('vue3-router-change', this.currentNode.routerName);
    },
 /** 点击树节点也就是单个菜单时触发 */
    handleNodeClick(data) {
      ...
      wujieVue.bus.$emit('vue3-router-change', this.currentNode.routerName);
    },

子应用mounted生命周期里渲染监听事件

子应用中再使用动态组件的方式渲染

js
<component
   v-if="dynamicComponent"
   :is="dynamicComponent"
></component>
<component
   v-if="dynamicComponent"
   :is="dynamicComponent"
></component>

mounted生命周期里渲染监听事件,设置动态组件路径

js
mounted() {
    window.$wujie?.bus.$on('vue3-router-change', (path) => {
      this.wujie_dynamicComponent = path;
    });
  },
mounted() {
    window.$wujie?.bus.$on('vue3-router-change', (path) => {
      this.wujie_dynamicComponent = path;
    });
  },
js
let modules = import.meta.glob('../dynamicComponent/**/*.vue');
... 
computed: {
    /** 动态组件 */
    dynamicComponent() {
      if (this.wujie_dynamicComponent) {
        return defineAsyncComponent(
          modules[`../dynamicComponent/${this.wujie_dynamicComponent}.vue`]
        );
      }
    },
let modules = import.meta.glob('../dynamicComponent/**/*.vue');
... 
computed: {
    /** 动态组件 */
    dynamicComponent() {
      if (this.wujie_dynamicComponent) {
        return defineAsyncComponent(
          modules[`../dynamicComponent/${this.wujie_dynamicComponent}.vue`]
        );
      }
    },

注意点

主子应用共享localstorage,所以不用传token,用户信息到子应用

同样,要注意主子应用的localstorage的key不能重名,也不能随意localstorage.clear

多包运行库lerna

lerna 库可用于命令行方式进行多包管理

安装

sh
pnpm i -D @lerna-lite/cli @lerna-lite/run
pnpm i -D @lerna-lite/cli @lerna-lite/run

配置

lerna.json

npmClient 用于指定使用的包管理工具,用于lerna内部逻辑处理

version 字段是必须的

json
{
  "npmClient": "pnpm",
  "version": "0.0.1"
}
{
  "npmClient": "pnpm",
  "version": "0.0.1"
}

pnpm-workspace.yaml

Lerna 将使用 pnpm-workspace.yaml 解析包位置

js
packages:
  - "packages/*"
packages:
  - "packages/*"

package.json

根目录运行lerna run start,将运行所有子包里的start命令,从而可以一键启动所有子应用,打包同理

sh
 "scripts": {
    "start": "lerna run start --parallel",
    ...
  },
 "scripts": {
    "start": "lerna run start --parallel",
    ...
  },

尚未完成的改造

  • 刷新完页面恢复到首页 (加localstorage保存dynamicComponent传参)
  • 查漏补缺,完善dynamicComponent的页面传参
  • 添加生产环境配置,现在还是开发环境的配置

基于lerna的monorepo改造现有项目实战总结

https://juejin.cn/post/7155098034571837447#heading-1

todo:隔离localstorage

html-loader

无界提供插件在运行时对子应用的 html 文本进行修改,使用该插件给所有子项目插入localstorage前缀

js
const plugins = [
  {
    // 对子应用的template进行的aaa替换成bbb
    htmlLoader: (code) => {
      return code.replace('aaa', 'bbb');
    },
  }
];
const plugins = [
  {
    // 对子应用的template进行的aaa替换成bbb
    htmlLoader: (code) => {
      return code.replace('aaa', 'bbb');
    },
  }
];

js-before-loaders

如果用户想在html中所有的js之前做,使用该插件给所有子项目插入localstorage前缀:

  1. 在子应用运行一个src="http://xxxxx"的脚本
  2. 在子应用中运行一个内联的 js 脚本<script>content</script>
  3. 执行一个回调函数

那么这些工作可以放置在js-before-loaders中进行

js
const plugins = [
  {
    // 在子应用所有的js之前
    jsBeforeLoaders: [
      // 插入一个外联脚本
      { src: "http://xxxx.js" },
      // 插入一个内联监本
      { content: 'console.log("test")' },
      // 执行一个回调,打印子应用名字
      {
        callback(appWindow) {
          console.log("js-before-loader-callback", appWindow.__WUJIE.id);
        },
      },
    ],
  },
];
const plugins = [
  {
    // 在子应用所有的js之前
    jsBeforeLoaders: [
      // 插入一个外联脚本
      { src: "http://xxxx.js" },
      // 插入一个内联监本
      { content: 'console.log("test")' },
      // 执行一个回调,打印子应用名字
      {
        callback(appWindow) {
          console.log("js-before-loader-callback", appWindow.__WUJIE.id);
        },
      },
    ],
  },
];

问题:子应用vite图片路径是主应用ip

https://github.com/Tencent/wujie/issues/398

实际上是vite问题,vite子应用打包图片使用的是self.location而不是import.meta.url

https://github.com/vitejs/vite/issues/10842

Released under the MIT License.