微前端 无界wujie

news2025/3/18 8:43:50

开发环境配置:

Node.js 版本 < 18.0.0
pnpm 脚手架示例模版基于 pnpm + turborepo 管理项目
如果您的当前环境中需要切换 node.js 版本, 可以使用 nvm or fnm 进行安装.

以下是通过 nvm 或者nvs 安装 Node.js 16 LTS 版本
nvs安装教程 https://blog.csdn.net/glorydx/article/details/134056903

C:\>node -v
v20.18.1

C:\>nvs use 16
Specified version not found.
To add this version now: nvs add node/16

C:\>nvs add node/16
Extracting  [###########################################################################################] 100%
Added at: %LOCALAPPDATA%\nvs\node\16.20.2\x64\node.exe
To use this version now: nvs use node/16.20.2/x64

C:\>npx create-wujie@latest
Need to install the following packages:
create-wujie@0.4.0
Ok to proceed? (y) y

📦 Welcome To Create Template for WuJie! V0.3.2
√ Project name: ... wujie-main
√ What framework do you choose as your main application ? » Webpack + Vue2
√ Select the main application route pattern » history
√ What framework do you choose as your sub application ? » Vite, Vue2, Vue3, React16, React17
√ Select the sub application route pattern » history

安装完成以后,分别单独启动wujie的主应用,和子应用,记得将node的版本都统一设置为 16 这样就可以正常体验wujie官方提供的demo。

wujie代码分析 vue2主应用

import "whatwg-fetch"; // fetch polyfill
import "custom-event-polyfill"; // CustomEvent polyfill

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import WujieVue from "wujie-vue2";
import hostMap from "../wujie-config/hostMap";
import credentialsFetch from "../wujie-config/fetch";
import Switch from "ant-design-vue/es/switch";
import Tooltip from "ant-design-vue/es/tooltip";
import button from "ant-design-vue/es/button/index";
import Icon from "ant-design-vue/es/icon/index";
import "ant-design-vue/es/button/style/index.css";
import "ant-design-vue/es/style/index.css";
import "ant-design-vue/es/switch/style/index.css";
import "ant-design-vue/es/tooltip/style/index.css";
import "ant-design-vue/es/icon/style/index.css";
import lifecycles from "../wujie-config/lifecycle";
import plugins from "../wujie-config/plugin";

const isProduction = process.env.NODE_ENV === "production";
const { setupApp, preloadApp, bus } = WujieVue;
Vue.use(WujieVue).use(Switch).use(Tooltip).use(button).use(Icon);

Vue.config.productionTip = false; // 关闭生产提示

bus.$on("click", (msg) => window.alert(msg));

// 在 xxx-sub 路由下子应用将激活路由同步给主应用,主应用跳转对应路由高亮菜单栏
bus.$on("sub-route-change", (name, path) => {
  const mainName = `${name}-sub`;
  const mainPath = `/${name}-sub${path}`;
  const currentName = router.currentRoute.name;
  const currentPath = router.currentRoute.path;
  if (mainName === currentName && mainPath !== currentPath) {
    router.push({ path: mainPath });
  }
});

// 根据浏览器的版本,如果不支持 Proxy,则降级 Object.defineProperty 如果不支持webcomponent 则降级iframe 理论上可以兼容到 IE 9
const degrade =
  window.localStorage.getItem("degrade") === "true" ||
  !window.Proxy ||
  !window.CustomElementRegistry;

const props = {
  // 将主应用的router.push方法传递给子应用,这样子应用就能通过获取jump方法,来控制主应用的跳转
  jump: (name) => {
    router.push({ name });
  },
};
/**
 * 大部分业务无需设置 attrs
 * 此处修正 iframe 的 src,是防止github pages csp报错
 * 因为默认是只有 host+port,没有携带路径
 */
const attrs = isProduction ? { src: hostMap("//localhost:8000/") } : {};
/**
 * 配置应用,主要是设置默认配置
 * preloadApp、startApp的配置会基于这个配置做覆盖
 */

setupApp({
  name: "react16",
  url: hostMap("//localhost:7600/"),
  attrs,
  exec: true,
  props, // 给子应用传递的参数
  fetch: credentialsFetch,
  plugins,
  // prefix 用于改变子路径过长,在主应用中进行替换
  prefix: { "prefix-dialog": "/dialog", "prefix-location": "/location" },
  degrade,
  ...lifecycles,
});

setupApp({
  name: "react17",
  url: hostMap("//localhost:7100/"),
  attrs,
  exec: true, //是否预先执行子应用
  alive: true, // 是否保存子应用的状态
  props,
  fetch: credentialsFetch,
  degrade,
  ...lifecycles,
});
setupApp({
  name: "vue2",
  url: hostMap("//localhost:6100/"),
  attrs,
  exec: true,
  props,
  fetch: credentialsFetch,
  degrade,
  ...lifecycles,
});

setupApp({
  name: "vue3",
  url: hostMap("//localhost:8082/"),
  attrs,
  exec: true,
  alive: true,
  plugins: [
    {
      cssExcludes: [
        "https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css",
      ],
    },
  ],
  props,
  // 引入了的第三方样式不需要添加credentials
  fetch: (url, options) =>
    url.includes(hostMap("//localhost:8082/"))
      ? credentialsFetch(url, options)
      : window.fetch(url, options),
  degrade,
  ...lifecycles,
});
setupApp({
  name: "vite",
  url: hostMap("//localhost:8083/"),
  attrs,
  exec: true,
  props,
  fetch: credentialsFetch,
  degrade,
  ...lifecycles,
});

// 因为已经setupApp注册了,这里可以简写,预加载只写name就可以了
if (window.localStorage.getItem("preload") !== "false") {
  preloadApp({
    name: "react16",
  });

  preloadApp({
    name: "react17",
  });

  preloadApp({
    name: "vue2",
  });

  if (window.Proxy) {
    preloadApp({
      name: "vue3",
    });
    preloadApp({
      name: "vite",
    });
  }
}

new Vue({
  router,
  render: (h) => h(App),
}).$mount("#app");

主应用中,用来显示子应用的配置
wujieVue这个组件只要url一变化,在非保活模式下,子应用就会重新加载

<template>
  <!--保活模式,name相同则复用一个子应用实例,改变url无效,必须采用通信的方式告知路由变化 -->
  <WujieVue width="100%" height="100%" name="react17" :url="react17Url"></WujieVue>
</template>

<script>
import hostMap from "../../wujie-config/hostMap";
import wujieVue from "wujie-vue2"; // 引入wujie-vue2,如果是vue3请引入wujie-vue3 用来显示子应用
export default {
  data() {
    return {
      react17Url: hostMap("//localhost:7100/") + this.$route.params.path, //hostMap区分开发环境和生产环境
    };
  },
  watch: {
    // 保活模式,name相同则复用一个子应用实例,改变url无效,必须采用通信的方式告知路由变化
    "$route.params.path": {
      handler: function () {
        wujieVue.bus.$emit("react17-router-change", `/${this.$route.params.path}`);
      },
      immediate: true,
    },
  },
};
</script>

<style lang="scss" scoped></style>

非保活模式的子应用在主应用中的配置

<template>
  <!--单例模式,name相同则复用一个无界实例,改变url则子应用重新渲染实例到对应路由 -->
  <WujieVue width="100%" height="100%" name="vite" :url="viteUrl"></WujieVue>
</template>

<script>
import hostMap from "../../wujie-config/hostMap";

export default {
  // 如果是非保活模式,不需要watch这个$route.params.path变化,并去调用wujieVue.bus.$emit
  computed: {
    viteUrl() {
      return hostMap("//localhost:8083/") + this.$route.params.path;
    },
  },
};
</script>

<style lang="scss" scoped></style>

vue2主应用,路由的配置

import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import Multiple from "../views/Multiple.vue";
import Vue2 from "../views/Vue2.vue";
import Vue2Sub from "../views/Vue2-sub.vue";
import Vue3 from "../views/Vue3.vue";
import Vue3Sub from "../views/Vue3-sub.vue";
import Vite from "../views/Vite.vue";
import ViteSub from "../views/Vite-sub.vue";
import React16 from "../views/React16.vue";
import React16Sub from "../views/React16-sub.vue";
import React17 from "../views/React17.vue";
import React17Sub from "../views/React17-sub.vue";

const basename = process.env.NODE_ENV === "production" ? "/demo-main-vue/" : ""; // 区分不同环境下,资源所在的不同文件夹
Vue.use(VueRouter);

const routes = [
  {
    path: "/all",
    name: "all",
    component: Multiple,
  },
  {
    path: "/",
    redirect: "/home",
  },
  {
    path: "/home",
    name: "home",
    component: Home,
  },
  {
    path: "/vue2",
    name: "vue2",
    component: Vue2,
  },
  {
    path: "/vue2-sub/:path",
    name: "vue2-sub",
    component: Vue2Sub,
  },
  {
    path: "/vue3",
    name: "vue3",
    component: Vue3,
  },
  {
    path: "/vue3-sub/:path",
    name: "vue3-sub",
    component: Vue3Sub,
  },
  {
    path: "/vite",
    name: "vite",
    component: Vite,
  },
  {
    path: "/vite-sub/:path",
    name: "vite-sub",
    component: ViteSub,
  },
  {
    path: "/react16",
    name: "react16",
    component: React16,
  },
  {
    path: "/react16-sub/:path",
    name: "react16-sub",
    component: React16Sub,
  },
  {
    path: "/react17",
    name: "react17",
    component: React17,
  },
  {
    path: "/react17-sub/:path",
    name: "react17-sub",
    component: React17Sub,
  },
];

// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置

const router = new VueRouter({
  mode: "history",
  base: basename,
  routes,
});

export default router;

vue2主应用vue.config 配置

// vue.config.js

/**
 * @type {import('@vue/cli-service').ProjectOptions}
 */
module.exports = {
  publicPath: process.env.NODE_ENV === "production" ? "/demo-main-vue/" : "/", // 区分开发和生产服务器的路径
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*", // 如果需要跨域,请打开此配置
    },
    open: process.env.NODE_ENV === "development", // 只有开发环境需要使用devServer
    port: "8000",
  },
  lintOnSave: false // 是否关闭eslint检查,只在保存时才检查
};

react子应用代码分析

子应用可以用到的wujie的数据 https://wujie-micro.github.io/doc/api/wujie.html#VPSidebarNav

import "react-app-polyfill/stable";
import "react-app-polyfill/ie11";

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";
import "./styles.css";

const basename = process.env.NODE_ENV === "production" ? "/demo-react16/" : "";

// 如果作为无界的子应用打开,就需要使用 window.__POWERED_BY_WUJIE__ 判断,然后挂载函数__WUJIE_MOUNT和卸载函数__WUJIE_UNMOUNT
if (window.__POWERED_BY_WUJIE__) {
  // eslint-disable-next-line no-undef
  window.__WUJIE_MOUNT = () => {
    ReactDOM.render(
      <Router basename={basename}>
        <App />
      </Router>,
      document.getElementById("root")
    );
  };
  window.__WUJIE_UNMOUNT = () => {
    ReactDOM.unmountComponentAtNode(document.getElementById("root"));
  };
} else {
  ReactDOM.render(
    <Router basename={basename}>
      <App />
    </Router>,
    document.getElementById("root")
  );
}

react 子应用,嵌套其他子应用

import React from "react";
import WujieReact from "wujie-react"; // 需要引入的wujie react 组件
import lifecycles from "./lifecycle"; // 对应的生命周期
import hostMap from "./hostMap"; // 对应的一些开发环境和生产环境的host映射

function selfFetch(url, options) {
  const includeFlag = process.env.NODE_ENV === "production";
  return window.fetch(url, { ...options, credentials: includeFlag ? "include" : "omit" });
}

export default function React17() {
  const react17Url = hostMap("//localhost:7100/");
  const degrade = window.localStorage.getItem("degrade") === "true";
  const props = {
    jump: (name) => {
      window?.$wujie.props.jump(name); // 从主应用vue2中得到的改变主应用router的函数jump,再传递给嵌套的子应用
    },
  };
  return (
    <div>
      <h2>子应用嵌套</h2>
      <div className="content" style={{ border: "1px dashed #ccc", overflow: "auto" }}>
        <WujieReact
          width="100%"
          height="500px"
          name="react17"
          url={react17Url}
          alive={true}
          sync={true}
          fetch={selfFetch}
          props={props}
          degrade={degrade}
          beforeLoad={lifecycles.beforeLoad}
          beforeMount={lifecycles.beforeMount}
          afterMount={lifecycles.afterMount}
          beforeUnmount={lifecycles.beforeUnmount}
          afterUnmount={lifecycles.afterUnmount}
        ></WujieReact>
      </div>
    </div>
  );
}

子应用可能还会遇到的问题
无界快速上手 https://wujie-micro.github.io/doc/guide/start.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317114.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux应用:程序运行

kill kill命令的这种用法是向指定的进程发送特定信号编号的信号。信号在操作系统中是一种软件中断机制&#xff0c;用于通知进程发生了某种特定事件或要求进程执行特定操作。​ kill - 信号编号 进程 ID 信号编号的含义&#xff1a;不同的信号编号代表不同的事件或操作。例如…

基于SpringBoot+Vue3实现的宠物领养管理平台功能一

一、前言介绍&#xff1a; 1.1 项目摘要 随着社会经济的发展和人们生活水平的提高&#xff0c;越来越多的人开始关注并参与到宠物领养中。宠物已经成为许多家庭的重要成员&#xff0c;人们对于宠物的关爱和照顾也日益增加。然而&#xff0c;传统的宠物领养流程存在诸多不便&a…

SpringCloud 学习笔记2(Nacos)

Nacos Nacos 下载 Nacos Server 下载 | Nacos 官网 下载、解压、打开文件&#xff1a; 更改 Nacos 的启动方式 Nacos 的启动模式默认是集群模式。在学习时需要把他改为单机模式。 把 cluster 改为 standalone&#xff0c;记得保存&#xff01; 启动startup.cmd Ubuntu 启动…

Blender-MCP服务源码4-初始化项目解读

Blender-MCP服务源码4-初始化项目解读 上篇文章针对Blender开发框架完成了一个基础模板的搭建&#xff0c;并在Blender中成功进行了运行&#xff0c;那这个初始化项目中是如何进行页面效果呈现的&#xff0c;尝试手动进行功能精简来拆解项目代码 1-核心知识点 1&#xff09;如…

基于eNSP的IPV4和IPV6企业网络规划

基于eNSP的IPV4和IPV6企业网络规划 前言网络拓扑设计功能设计技术详解一、网络设备基础配置二、虚拟局域网&#xff08;VLAN&#xff09;与广播域划分三、冗余协议与链路故障检测四、IP地址自动分配与DHCP相关配置五、动态路由与安全认证六、广域网互联及VPN实现七、网络地址转…

C#特性和反射

1。特性概念理解&#xff1f; 特性&#xff08;Attribute&#xff09;是用于在【运行时】传递程序中各种元素&#xff08;比如类、属性、方法、结构、枚举、组件等&#xff09;行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所…

mysql5.x和mysql8.x查看和设置隔离级别

MySQL的隔离级别 级别标志值描述读未提交READ-UNCOMMITTED0存在脏读、不可重复读、幻读的问题读已提交READ-COMMITTED1解决脏读的问题&#xff0c;存在不可重复读、幻读的问题可重复读REPEATABLE-READ2mysql 默认级别&#xff0c;解决脏读、不可重复读的问题&#xff0c;存在幻…

3.17学习总结

写了两道题 刚开始用的之前做组合输出的方法&#xff0c;时间超限了&#xff0c;想不出怎么优化&#xff0c;后面看了题解&#xff0c;代码如下 #include <stdio.h> #include <stdlib.h> int n,min2e9; int a[11],b[11]; //搜索 void hly(int s,int x,int y) {//当…

Blender材质 - 层权重

层权重 混合着色器 可以让 面朝向的一面显示一种材质 另一面显示另一种材质 就能实现挺不错的材质效果 移动视角 材质会跟着变化 有点类似虚幻的视差节点BumpOffset

【JavaEE】Spring Boot 日志

目录 一、日志概述二、使用日志2.1 打印日志2.2 日志框架2.2.1 门面 / 外观 模式 2.3 日志级别2.3.1 六大分类2.3.2 使用 2.4 日志级别配置2.5 日志的持久化2.6 日志文件分割2.7 日志文件格式2.8 Slf4j 简单打印日志 一、日志概述 ⽇志主要是为了发现问题, 分析问题, 定位问题…

如何用solidworks画齿轮

齿轮还是很有技术含量的,专业名词太多看不懂, 只会画 (这个东西不能自己想当然画, 齿轮之间不啮合是很有问题的,会积累磨损) 步骤1 打开设计库里的toolbox 选择正齿轮,右键生成零件 需要改的有几个关键的地方,我是只知道内圆外圆所以,对我来说最重要的是标称轴直径 (即正中间…

详解布隆过滤器及其模拟实现

目录 布隆过滤器 引入 概念 工作原理 模拟实现布隆过滤器 哈希函数集 布隆过滤器基本框架 add函数&#xff08;添加到布隆过滤器中&#xff09; contains函数&#xff08;判断是否存在该值&#xff09; 完整代码 布隆过滤器的删除 布隆过滤器的误判率 布隆过滤器的…

element-plus中DatePicker 日期选择器组件的使用

1.选择某一天 代码&#xff1a; <el-date-pickerv-model"invoice_date"type"date"placeholder"请选择日期"style"width: 200px;"clearable /> 运行效果&#xff1a; 问题所在&#xff1a;这个数据的格式不是我们后端需要的那种&…

SvelteKit 最新中文文档教程(4)—— 表单 actions

前言 Svelte&#xff0c;一个语法简洁、入门容易&#xff0c;面向未来的前端框架。 从 Svelte 诞生之初&#xff0c;就备受开发者的喜爱&#xff0c;根据统计&#xff0c;从 2019 年到 2024 年&#xff0c;连续 6 年一直是开发者最感兴趣的前端框架 No.1&#xff1a; Svelte …

力扣hot100二刷——二叉树

第二次刷题不在idea写代码&#xff0c;而是直接在leetcode网站上写&#xff0c;“逼”自己掌握常用的函数。 标志掌握程度解释办法⭐Fully 完全掌握看到题目就有思路&#xff0c;编程也很流利⭐⭐Basically 基本掌握需要稍作思考&#xff0c;或者看到提示方法后能解答⭐⭐⭐Sl…

字符串哈希从入门到精通

一、基本概念 字符串哈希是将任意长度的字符串映射为固定长度的哈希值&#xff08;通常为整数&#xff09;的技术&#xff0c;核心目标是实现O(1)时间的子串快速比较和高效查询。其本质是通过数学运算将字符串转换为唯一性较高的数值&#xff0c;例如&#xff1a; ​​​​​​…

C语言:编程设计猜数游戏

先由计算机想一个数给用户猜&#xff0c;如果猜对了&#xff0c;提示“right&#xff01;”&#xff0c;猜错了&#xff0c;提示“wrong&#xff01;及大小” 思路&#xff1a;用随机函数rand&#xff08;&#xff09;取到计算机想的数 代码&#xff1a; #include <stdio.…

win10 c++ VsCode 配置PCL open3d并显示

win10 c VsCode配置PCL open3d并显示 一、效果图二、配置步骤2.1 安装vscode2.2 pcl-open3d配置2.3 vscode中设置 三、测试代码四、注意事项及后续 一、效果图 二、配置步骤 2.1 安装vscode vscode下载链接 下载中文插件、c相关插件 2.2 pcl-open3d配置 1&#xff09;下载…

Vala 开发环境搭建

介绍 Vala 是一种使用现代高级抽象的编程语言&#xff0c;与用 C 语言编写的应用程序和库相比&#xff0c;没有施加额外的运行时要求&#xff0c;也不需要使用不同的 ABI。 Vala 使用 GObject 类型系统&#xff0c;并具有额外的代码生成例程&#xff0c;使面向 GNOME 堆栈变得简…

【网页】自制流光卡片

概述 小红书有个博主自己搞的笔记排版工具叫“流光卡片”&#xff0c;类似的还有个Markdown排版工具叫MD2Card。 我这个版本类似&#xff0c;但是自己写的东西&#xff0c;控制性更好。 初期就写了个静态页面&#xff0c;后期结合Godot快速生成&#xff0c;并可能结合JS库&a…