优秀的编程知识分享平台

网站首页 > 技术文章 正文

Vue3 远程加载组件(vue3远程加载组件)

nanyue 2025-07-14 20:19:10 技术文章 1 ℃

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;
};

Tags:

最近发表
标签列表