子项目嵌入主项目方案
这里的子项目使用的是vue3项目案例,是从
gitee上下载下来的,是一个免费开源的海报设计器;
git地址:https://gitee.com/sourcenet/gzm-design/tree/develop/
感兴趣的可以下载看看,个人感觉还不错
方案一(iframe)
这里使用的是传统的方案,
iframe嵌入子项目,可以采取以下两种:
1.将子项目打包成dist包,把dist包放入到主项目的public目录文件夹下,在相关页面引入包中的index.html
2.也可以将子项目部署,把部署好的地址在相关页面中使用iframe嵌入进来
这里说一下子项目与主项目通过iframe如何通信
- 父页面发送消息给子项目:
iframe.contentWindow.postMessage - 子项目发送消息给父页面:
window.parent.postMessage - 父子项目接收对方消息:
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格式)
一、父级项目
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的使用
- 注册一个事件监听器
emitter.on(event, handler)。 - 移除一个事件监听器
emitter.off(event, handler)。 - 触发一个事件
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中配置的话,那么就会把serve和build模式下覆盖掉


6518

被折叠的 条评论
为什么被折叠?



