Vue 3 CLI与Webpack模块联邦:组件从错误的主机加载

4

我目前正在尝试设置一个使用Webpack的模块联邦来共享组件的项目。

为此,我使用cli设置了两个基本的vue项目,并在两个项目中添加了vue.config.js文件:

主机项目(将包含共享组件) (运行在localhost:8000上)

const { ModuleFederationPlugin } = require('webpack').container

module.exports = {
  configureWebpack: {
    plugins: [
      new ModuleFederationPlugin({
        name: 'shell',
        filename: 'remoteEntry.js',
        remotes: {
          component: 'component@http://localhost:8001/remoteEntry.js'
        },
        exposes: {},
        shared: {}
      })
    ]
  }
}

组件项目(共享该组件的)(运行在 localhost:8001 上):
const { ModuleFederationPlugin } = require('webpack').container

module.exports = {
  configureWebpack: {
    plugins: [
      new ModuleFederationPlugin({
        name: 'component',
        filename: 'remoteEntry.js',
        remotes: {},
        exposes: {
          './HelloWorld': './src/components/HelloWorld.vue'
        },
        shared: {}
      })
    ]
  }
}

我尝试在我的App.vue中加载组件:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Welcome to Your Vue.js App" />
  <otherComp />
</template>

<script>
import { defineAsyncComponent } from "vue";
import HelloWorld from "./components/HelloWorld.vue";

const otherComp = defineAsyncComponent(() => import("component/HelloWorld"));

export default {
  name: "App",
  components: {
    HelloWorld,
    otherComp,
  },
};
</script>

确实它试图加载组件,但是它没有从本地主机:8001(组件托管的地方)加载它,而是试图从本地主机:8000加载它:

在本地主机:8001上存在相同的路径。一些调试显示,webpack publicPath 似乎被设置为“ /”(导致localhost:8000上的托管应用程序将url设置为/js/src_components_HelloWorld_vue.js

/******/    /* webpack/runtime/publicPath */
/******/    !function() {
/******/        __webpack_require__.p = "/";
/******/    }();

我认为这是由于vue-cli与webpack互动的方式造成的。这是一个已知的问题,如何解决?

我认为你的问题很清楚,然而,我认为一个主机项目是托管其他人可以使用的组件的项目。另一方面,你有一个消费项目将使用主机的组件。我在我的答案中指定了这样做来清楚区分这两个项目。 - Cloudkollektiv
1个回答

2
你尝试更改过publicPath变量(源代码)吗?它以绝对路径/作为默认值,但你必须在两个项目的vue.config.js中显式地设置它们。我描述了一个详细的示例,告诉你如何配置它。为了简单起见,host项目是暴露组件的项目,consumer项目是消费这些远程组件的项目。
  1. The host project should expose components. So you explicitly need to set publicPath variable to the full hostname. Note that devServer.port is set to 8000, so you don't have to do this manually.

     // vue.config.js host project 
    
     const ModuleFederationPlugin =
       require("webpack").container.ModuleFederationPlugin;
    
     module.exports = {
       publicPath: "http://localhost:8000/",
       configureWebpack: {
         plugins: [
           new ModuleFederationPlugin({
             name: "host",
             filename: "remoteEntry.js",
             exposes: {
               "./HelloWorld": "./src/components/HelloWorld",
             },
           }),
         ],
       },
       devServer: {
         port: 8000,
       },
     };
    
  2. On the other side, we have a project that uses those components, which is specified through the remotes field. For the sake if simplicity, this is the consumer project. Again, we set the publicPath to the host where it is running on. Note that to use the remote component we have to know the name, hostname and port of the host project: host@http://localhost:8000/remoteEntry.js.

     // vue.config.js consumer project
    
     const ModuleFederationPlugin =
       require("webpack").container.ModuleFederationPlugin;
    
     module.exports = {
       publicPath: "http://localhost:8001/",
       configureWebpack: {
         plugins: [
           new ModuleFederationPlugin({
             name: "consumer",
             filename: "remoteEntry.js",
             remotes: {
               host: "host@http://localhost:8000/remoteEntry.js",
             },
           }),
         ],
       },
       devServer: {
         port: 8001,
       },
     };
    

您可以在WebPack模块联邦的作者们的Github代码库中找到一个非常详细的示例。这个示例项目同时使用了Vue和Webpack。此外,还有一些您可能需要的其他功能:

  1. With the remotes option, you can specify more than one remote source. This is the idea of having many Micro Frontends. For example:

     new ModuleFederationPlugin({
       // ...
       remotes: {
         host: "host@http://localhost:8000/remoteEntry.js",
         other: "other@http://localhost:9000/remoteEntry.js",
       }
     })
    
  2. The shared option lets you share dependencies between instances. For example, you can share UIkits, so that both instances have the same styling. However, there are still some caveats when sharing packages like vue, react or angular. You will probably run into the following error: Shared module is not available for eager consumption. This post describes several ways to work around this problem, this is one of them:

     const deps = require('./package.json').dependencies
    
     new ModuleFederationPlugin({
       // ...
       shared: {
         ...deps,
         vue: {
           eager: true,
           singleton: true,
           requiredVersion: deps.vue,
           strictVersion: true,
         },
       },
     })
    
注意:ModuleFederation 仅包含在 webpack 5(及更高版本)中。

1
@RoarS. 他向 OP 提出了这个建议,他不会尝试在本地复制他的设置,当 OP 可以在大约30秒内完成此操作时。而且,并不是因为它是500rep,你就需要花费数天时间来完成它。 - kissu
1
截至2022年,设置publicPath:'auto'的功能完美运行。 - Duc Trung Mai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接