网站首页 > 技术文章 正文
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-09-18 GPU集群扩展:Ray Serve与Celery的技术选型与应用场景分析
- 2025-09-18 【不背八股】2.操作系统-进程、线程、协程的基本理解
- 2025-09-18 两张图看透Android Handler使用与机制
- 2025-09-18 Spring Boot 3.x 日志配置与 Logback 集成指南
- 2025-09-18 解锁C++异步之力:高效并发编程指南
- 2025-09-18 Flutter框架分析(八)-Platform Channel
- 2025-09-18 原来你是这样打印日志的,怪不得天天背锅……
- 2025-09-18 .NET Aspire 9.4 发布了 CLI GA、交互式仪表板和高级部署功能
- 2025-09-18 27.8K!一把梭 LLM:LiteLLM 带你用一套接口召唤 100+ 大模型
- 2025-09-18 Rust异步编程神器:用Tokio轻松创建定时任务
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (77)
- vector线程安全吗 (73)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)
