vue项目嵌入子项目,这里以vue3 vite项目为案例

这里的子项目使用的是vue3项目案例,是从gitee上下载下来的,是一个免费开源的海报设计器;
git地址:https://gitee.com/sourcenet/gzm-design/tree/develop/
感兴趣的可以下载看看,个人感觉还不错

海报设计器在线预览

方案一(iframe)

这里使用的是传统的方案,iframe嵌入子项目,可以采取以下两种:
1.将子项目打包成dist包,把dist包放入到主项目的public目录文件夹下,在相关页面引入包中的index.html
2.也可以将子项目部署,把部署好的地址在相关页面中使用iframe嵌入进来

这里说一下子项目与主项目通过iframe如何通信

  1. 父页面发送消息给子项目:iframe.contentWindow.postMessage
  2. 子项目发送消息给父页面:window.parent.postMessage
  3. 父子项目接收对方消息:window.addEventListener('message', this.handleMessage)

父页面:

<template>
  <div>
    <el-button @click="sendMessageToIframe">发送消息</el-button>
    <iframe
      src="http://localhost:5173/#/editor"
      frameborder="0"
      class="iframe"
      id="myIframe"
    ></iframe>
  </div>
</template>

<script>
export default {
  name: 'wel',
  mounted() {
    window.addEventListener('message', this.handleMessage);
  },
  beforeDestroy() {
    window.removeEventListener('message', this.handleMessage);// 在组件销毁前移除事件监听器
  },
  methods: {
    handleMessage(event) {
    	//接收到的消息域名 不是 子项目域名 退出代码执行
      if (event.origin !== 'http://localhost:5173') return;
      console.log('父窗口接收到的消息Received message from iframe:', event.data);
    },
    sendMessageToIframe() {
      const iframe = document.getElementById('myIframe');
      iframe.contentWindow.postMessage('父页面发送的消息:hello', 'http://localhost:5173/#/editor');
    },
  },
};
</script>

<style>
.iframe {
  width: 100%;
  height: 100vh;
}
</style>

子页面:

onMounted(() => {
    // iframe
  window.addEventListener("message", function (event) {
    if (event.origin !== "http://localhost:2888") return;
    // 处理接收到的消息
    console.log("子窗口接收消息Received message:", event.data);
    // 向父窗口发送回应消息
    window.parent.postMessage('子页面回应:hello', event.origin);
  });
})

方案二(一个项目两个挂载实例)

这种解决方案采取的是:
原来主项目只有一个根节点,createApp会创建一个vue实例, 它有一个mount方法可以将该实例挂载到指定的dom元素上, 该实例会解析这个dom元素的vue语法, 进行处理
现在处理成在主项目的基础上新增了一个根节点,用于挂载子项目的vue实例

注意:
如果你引入的子项目页面访问路由是跟路径/,那么就把子项目中的路由给改一下,不要与主项目路由冲突

步骤

1.将子项目挂载名称起名embedded-project,并在main.js中修改以下代码 (注意:要与主项目中修改一致,名称可自定义)

const app1 = createApp(App)
//后面的代码统一修改app1

示例图在这里插入图片描述
在这里插入图片描述

2.将子项目打包成dist
3.将dist包放置到主项目的public目录下(可以更改dist包名,这里的dist我起名为embedded-project
在这里插入图片描述
4.修改主项目的入口文件index.html,添加以下代码

 <!-- id名称要与子项目挂载名称一致 -->
    <div id="childApp"></div>
    <!-- 把dist目录中的index.html相关文件引入以下 -->
    <link rel="stylesheet" href="/embedded-project/assets/index-7f87dba8.css">
    <script type="module" crossorigin src="/embedded-project/assets/index-620920c9.js"></script>

示例图:
在这里插入图片描述

5.修改主项目的入口js文件main.js,添加以下代码

const app1 = createApp(App);
app1.mount('#embedded-project');//切记这里的挂在名要与子项目中的挂在名一致

6.在主项目的App.vue项目中添加以下代码;用来切换主项目和子项目

  export default {
        watch: {
            $route(to, from) {
                if (to.href == '/editor') {  //子项目路由
                    document.getElementById('embedded-project').style.display = 'block';
                    document.getElementById('app').style.display = 'none';
                } else {   //主项目路由
                    document.getElementById('embedded-project').style.display = 'none';
                    document.getElementById('app').style.display = 'block';
                }
        },
    },

最后展示的结果

主项目页面跳转子项目的页面

在这里插入图片描述
在这里插入图片描述

方案三sdk(组件库打包umd格式)

sdk各版本地址

一、父级项目

1.创建工程,使用vite创建, 选择vue

pnpm create vite

2.初始化项目结构
在这里插入图片描述
3.修改过后的项目结构
在这里插入图片描述

3.新增了一个组件页面topComponent.vue;用于加载子项目页面,代码如下:

<script setup>
import { onMounted } from "vue";
import renderVueComponentToDOM from "../plugin/sdk.js";

onMounted(() => {
  renderVueComponentToDOM(document.getElementById("embedded-project"));
 
});
</script>
<template>
  <div>
    <div id="embedded-project">用于加载子项目</div>
  </div>
</template>

4.HelloWorld.vue页面引入组件topComponent.vue

<script setup>
import topComponent from "./topComponent.vue";
</script>

<template>
	<top-component></top-component>
</template>

5.新增插件目录plugin,里面存放的是从子项目中打包的sdk.js文件

二、效果图

在这里插入图片描述

三、子级项目

注意:
在做之前要搞清楚暴露的页面是哪个,因为这里会影响到你需要将那些文件以及组件导出到sdkJs中,我这里是editor.vue

在这里插入图片描述

新增入口文件index.js

main.ts同级目录下新增index.js;看一下editor.vue文件中引入了那些方法;以及main.ts文件中引入的库,依赖、图标、样式等全都复制进来,代码如下:

import { createApp } from "vue";
import { getActiveCore } from "@/views/Editor/core";
import { appInstance } from "@/views/Editor/app";
import App from "./views/Editor/editor.vue";//暴露的文件

/* main.ts中引入的库和依赖等 */
import pinia from "@/store";
import ArcoVue from "@arco-design/web-vue";
import "@arco-design/web-vue/dist/arco.css";
// CSS
import "@unocss/reset/tailwind-compat.css";
import "virtual:uno.css";
import "virtual:svg-icons-register";
import "./style.less";
import "./mock";
import "@/utils/request";
// 额外引入图标库
import ArcoVueIcon from "@arco-design/web-vue/es/icon";
import IconFontPlugin from "./plugins/iconFontPlugin";

import { createCore } from "@/views/Editor/core";
const core = createCore();
import { myPlugin } from "@/views/testPlugin";
core.use(myPlugin);

function renderVueComponentToDOM(domElement) {
  let app = createApp(App);
  app.use(pinia);
  app.use(ArcoVue);
  app.use(core);
  app.use(ArcoVueIcon);
  app.use(IconFontPlugin);
  app.use(getActiveCore).use(appInstance);
  app.mount(domElement);
}

export default renderVueComponentToDOM;

vite打包配置

因为是需要单独的当作sdk库使用, 所以需要把组件的css打包到js中
所以需要安装vite插件vite-plugin-css-injected-by-js

pnpm add vite-plugin-css-injected-by-js -D

配置vite.config.js

import { defineConfig } from 'vite'

import vue from '@vitejs/plugin-vue'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
export default defineConfig({
  plugins: [
    vue(),
    cssInjectedByJsPlugin(),
  ],
  define: { 'process.env.NODE_ENV': '"production"' },
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.js'),
      name: 'sdk',
      fileName: 'sdk'
    }
  },
})

最后执行

pnpm build

在这里插入图片描述
而我们只需要把dist目录中的sdk.js文件引入到我们的项目中即可(父级上面写到了)

遇到的问题以及解决方案

这是我新建的vue3 vite项目里面嵌入sdk,可以正常显示;但我把sdkJs引入到我们开发的项目中就报错;报错如下
在这里插入图片描述

优化代码

子项目打包配置修改,新增库模式

build: {
   lib: {
     entry: resolve(__dirname, "src/index.js"),
     formats: ['es', 'cjs', 'umd', 'iife'],//库模式
     name: "sdk",
     fileName: 'sdk'
   },
 },

执行打包命令,生成dist目录,里面的sdk.cjs修改如下:export default renderVueComponentToDOM;
在这里插入图片描述
最后就是各个项目引入了,注意有的项目引用sdk.js(自己新建的框架,以及vue2框架应该适用),有的项目就得需要引 sdk.cjs(类似引入这种的,我感觉像是公司都有自己的框架和环境比较适宜),其他两种我还没有尝试,等尝试过后告知

四、父项目获取子项目数据方法定义

子项目打包js优化,全部代码(index.js)

function renderVueComponentToDOM(domElement) {
  let app = createApp(App);
  const { editor } = useEditor()
  // 获取海报json数据
  function saveJson() {
    editor.getCurrentPage();
    let json = editor.contentFrame.toJSON();
    return json
  };
  // 获取海报base64图片数据
  const preview = async () => {
    const result = await editor.contentFrame.export('png')
    return result.data;
  }
  return {getJson:saveJson,getUrl:preview};
}

五、子项目接收父项目传递的数据

子项目

1.安装mitt:
npm install mitt
2.配置mitt的ts文件

我们新建一个emitter.ts文件, 目录最好是tools或者utils下 (我的位置在src\utils\emitter.ts) ts的内容如下:

// 引入mitt
import mitt from "mitt";
 
// 创建emitter
const emitter = mitt()
 
/*
    我们可以直接在这里定义一些事件,也可以在具体的vue组件中定义
*/
 
// 创建并暴露mitt
export default emitter
3.mitt的使用
  1. 注册一个事件监听器emitter.on(event, handler)
  2. 移除一个事件监听器emitter.off(event, handler)
  3. 触发一个事件emitter.emit(event, data);
4.项目中使用

在接收方和发送方引入文件

import emitter from "@/utils/emitter";
5.项目完整代码引用

index.js

import { createApp } from "vue";
import emitter from "@/utils/emitter";
import { getActiveCore } from "@/views/Editor/core";
import { appInstance } from "@/views/Editor/app";
import { EditorMain } from "@/views/Editor/app/editor";
import { useEditor } from "@/views/Editor/app";
import App from "./views/Editor/editor.vue";

import pinia from "@/store";
import ArcoVue from "@arco-design/web-vue";
import "@arco-design/web-vue/dist/arco.css";
// CSS
import "@unocss/reset/tailwind-compat.css";
import "virtual:uno.css";
import "virtual:svg-icons-register";
import "./style.less";
import "./mock";
import "@/utils/request";
// 额外引入图标库
/*  */
import ArcoVueIcon from "@arco-design/web-vue/es/icon";
import IconFontPlugin from "./plugins/iconFontPlugin";

import { createCore } from "@/views/Editor/core";
const core = createCore();
import { myPlugin } from "@/views/testPlugin";
core.use(myPlugin);

function renderVueComponentToDOM(domElement) {
  let app = createApp(App);
  app.config.globalProperties.$emitter = emitter;
  app.use(pinia);
  app.use(ArcoVue);
  app.use(core);
  app.use(ArcoVueIcon);
  app.use(IconFontPlugin);
  app.use(getActiveCore).use(appInstance);
  app.mount(domElement);

  const { editor } = useEditor();
  function saveJson() {
    editor.getCurrentPage();
    let json = editor.contentFrame.toJSON();
    return json;
  }
  const preview = async () => {
    const result = await editor.contentFrame.export("png");
    return result.data;
  };
  // 接收父级传来的数据
  const callback = (type, dataFn) => {
    console.log(dataFn);
    if(!type){
      emitter.emit('sum-sum',dataFn)
    }else{
     // 系统中点击保存按钮时,触发父系统回调
      emitter.on('syyCabon', (value) =>{
        dataFn(value)
      });
    }
  }
  return { getJson: saveJson, getUrl: preview,callback };
}

export default renderVueComponentToDOM;

父级引用(别忘记更新js包)

<script setup lang="ts">
import { ref,reactive,onMounted } from 'vue'
import renderVueComponentToDOM from "../plugin/sdk.cjs";
let state:any = reactive({
  app:null
})
const embeddedProject = ref<any>(null);
onMounted(() => {
  state.app = renderVueComponentToDOM(document.getElementById("embedded-project"));
  console.log(state.app,"./");
  // 子系统中点击保存按钮时,触发父系统回调
  state.app?.callback("callback",(value:any)=>{
    console.log(value,"./")
  });
  
});
const getData = () => {
  console.log(state.app?.getJson(),"./")
  console.log(state.app?.getUrl(),"./")
}
const clickFn = () => {
  state.app?.callback(null,"hello world");
}
</script>

<template>
  <div>
    <div id="embedded-project" ref="embeddedProject">用于加载子项目</div>
    <button @click="getData">点击获取子项目数据</button>
    <br>
    <button @click="clickFn">给子项目传递参数</button>
  </div>
  
</template>

打印结果
在这里插入图片描述

六、新增打包命令

第一种:
1.把vite打包配置文件复制出来一份,修改名为vite.name.config.ts
2.package.json文件命令代码中添加对应文件命令:
在这里插入图片描述
第二种:
1.配置打包环境模式,一般是环境模式是开发模式与生产模式,咋们可以新增一个环境模式用于处理打包格式,先新增打包命令
在这里插入图片描述
2.在vite配置文件中判断模式
mode就是指明模式,比如:development或者production,如果在vite.config.ts中配置的话,那么就会把servebuild模式下覆盖掉
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值