网站首页 > 技术文章 正文
1. 基本远程组件加载
<template>
<div>
<h1>远程组件示例</h1>
<button @click="loadRemoteComponent">加载远程组件</button>
<Suspense v-if="showRemoteComponent">
<template #default>
<RemoteComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue';
const showRemoteComponent = ref(false);
const RemoteComponent = ref(null);
const loadRemoteComponent = async () => {
showRemoteComponent.value = true;
RemoteComponent.value = defineAsyncComponent(() =>
import('https://your-cdn.com/path/to/remote-component.js').then(module => {
// 可能需要根据实际导出进行调整
return module.default || module;
})
);
};
</script>
2. 带参数的远程组件加载
<template>
<div>
<button @click="loadComponent('UserProfile')">加载用户资料组件</button>
<button @click="loadComponent('ProductCard')">加载产品卡片组件</button>
<Suspense>
<template #default>
<component :is="currentComponent" v-bind="componentProps" />
</template>
<template #fallback>
<div>加载组件中...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { ref, shallowRef, defineAsyncComponent } from 'vue';
const currentComponent = shallowRef(null);
const componentProps = ref({});
const componentMap = {
UserProfile: {
url: 'https://your-cdn.com/components/user-profile.js',
props: { userId: '123' }
},
ProductCard: {
url: 'https://your-cdn.com/components/product-card.js',
props: { productId: '456' }
}
};
const loadComponent = async (componentName) => {
const config = componentMap[componentName];
if (!config) return;
currentComponent.value = defineAsyncComponent(() =>
import(/* @vite-ignore */ config.url).then(module => module.default || module)
);
componentProps.value = config.props;
};
</script>
3. 带错误处理的远程组件
<div>
<button @click="loadComponent">加载远程组件</button>
<div v-if="error" class="error">
加载失败: {{ error.message }}
<button @click="retry">重试</button>
</div>
<Suspense v-else-if="isLoading">
<template #default>
<RemoteComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue';
const isLoading = ref(false);
const error = ref(null);
const RemoteComponent = ref(null);
const loadComponent = async () => {
try {
isLoading.value = true;
error.value = null;
RemoteComponent.value = defineAsyncComponent({
loader: () => import(/* @vite-ignore */ 'https://your-cdn.com/components/special-component.js'),
onError: (err, retry, fail) => {
error.value = err;
console.error('加载组件失败:', err);
}
});
} catch (err) {
error.value = err;
} finally {
isLoading.value = false;
}
};
const retry = () => {
error.value = null;
loadComponent();
};
</script>
4. 使用 Webpack 模块联邦的远程组件
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://remote-domain.com/remoteEntry.js'
}
})
]
};
// Vue组件中使用
<template>
<Suspense>
<template #default>
<RemoteComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const RemoteComponent = defineAsyncComponent(() =>
import('remoteApp/SomeComponent').then(module => module.default)
);
</script>
服务器端配置
const app = express();
// 设置CORS
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// 提供组件文件
app.get('/components/:componentName.js', (req, res) => {
const componentName = req.params.componentName;
// 这里可以根据请求动态生成组件,或返回预编译的文件
const componentCode = `
export default {
name: '${componentName}',
template: '<div>这是远程加载的${componentName}组件</div>'
}
`;
res.type('application/javascript').send(componentCode);
});
app.listen(3000, () => console.log('Server running on port 3000'));
Vue3 直接远程加载 .vue 文件
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
// 允许跨域
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// 提供.vue文件
app.get('/remote-component.vue', (req, res) => {
const filePath = path.join(__dirname, 'components', 'RemoteComponent.vue');
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
res.status(404).send('Component not found');
return;
}
res.type('text/plain').send(data);
});
});
// 静态文件服务(如果需要加载组件中的资源)
app.use(express.static('public'));
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
远程组件示例 (RemoteComponent.vue)
<template>
<div class="remote-component">
<h2>这是远程加载的组件</h2>
<p>当前时间: {{ new Date().toLocaleString() }}</p>
<button @click="count++">点击计数: {{ count }}</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
mounted() {
console.log('远程组件已挂载');
}
}
</script>
<style scoped>
.remote-component {
border: 2px dashed #42b983;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
}
.remote-component h2 {
color: #42b983;
}
</style>
优化版本(使用预编译)
对于生产环境,建议服务端预编译组件:
app.get('/precompiled-component.js', async (req, res) => {
const filePath = path.join(__dirname, 'components', 'RemoteComponent.vue');
const vueFileContent = await fs.promises.readFile(filePath, 'utf8');
// 使用@vue/compiler-sfc预编译
const { compile } = require('vue/compiler-sfc');
const { descriptor } = compile(vueFileContent);
const script = descriptor.script?.content || 'export default {}';
const template = descriptor.template?.content || '<div></div>';
const componentCode = `
${script}
export default {
...${script.includes('export default') ? '__script__' : '{}'},
template: ${JSON.stringify(template)}
}
`.replace('export default', 'const __script__ =');
res.type('application/javascript').send(componentCode);
});
然后在客户端可以直接加载预编译的版本:
const module = await import('http://localhost:3000/precompiled-component.js');
remoteComponent.value = module.default;
};
猜你喜欢
- 2025-07-14 CompletableFuture.failedFuture 在 java 8中的替代方法
- 2025-07-14 harmony-utils之PickerUtil,拍照、文件选择和保存,工具类
- 2025-07-14 Java异步编程7大夺命坑!阿里P8血泪逃生指南(附性能核弹包)
- 2025-07-14 Nuxt错误处理完整指南:从基础到高级实践
- 2025-07-14 webpack的几个常见loader源码浅析,动手实现一个md2html-loader
- 2025-07-14 async/await 在 C# 语言中是如何工作的?(中)
- 2025-07-14 用 async 模块控制并发数(@async 并发100000)
- 2025-07-14 webpack 常见loader原理剖析,动手实现一个md2html的loader
- 2025-07-14 Vue 3最佳实践:10万QPS性能调优手册
- 2025-07-14 Teleport+Suspense:动态组件加载新方案
- 1512℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 556℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 504℃MySQL service启动脚本浅析(r12笔记第59天)
- 482℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 480℃启用MySQL查询缓存(mysql8.0查询缓存)
- 460℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 441℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 438℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)