设置带有vue和单个spa的微型前端架构

2023-11-01

A practical walkthrough on building a micro frontend architecture with multiple Vue.js apps using single-spa. Note that you can replace Vue with your framework of choice.

关于使用单个spa使用多个Vue.js应用程序构建微前端架构的实用演练。 请注意,您可以使用自己选择的框架替换Vue。

The first article of a three part guide that aims at laying the foundations on how to setup and organize a micro frontend architecture for your projects using single-spa.

这是一个由三部分组成的指南的第一篇文章,旨在为如何使用Single-Spa为您的项目设置和组织微型前端架构奠定基础。

Part 1 — setup the orchestrator layer which loads the necessary assets, 3rd party libraries and our micro applications;

第1部分 -设置协调器层,以加载必要的资产,第三方库和我们的微型应用程序;

Part 2 (TBD) — organizing common styles and reusable components; sharing and manipulating state between apps;

第2部分(TBD) -组织通用样式和可重用组件; 在应用之间共享和处理状态;

Part 3 (TBD) — deploying to Netlify while avoiding concurrency;

第3部分(TBD) -在避免并发的同时部署到Netlify;

什么是微前端? (What are Micro Frontends?)

The concept of micro frontends has been around for a while now but has been getting more attention in the past couple of years.

微型前端的概念已经存在了一段时间,但在过去的几年中得到了越来越多的关注。

Evolution of the search term “Micro Frontend” on Google Trends, 2015–2020
2015–2020年Google趋势中搜索词“ Micro Frontend”的演变

Micro frontends extend the concept of backend micro-services: breaking down a web app (a monolith SPA) into distinct pieces. Then, through an orchestrator layer, each part is assembled (or composed) together. There are multiple ways of doing so. In our case we will do what is called client-side composition using single-spa.

微型前端扩展了后端微型服务的概念:将Web应用程序(整体式SPA)分解为不同的部分。 然后,通过协调器层,将每个部分组装(或组成)在一起。 这样做有多种方法。 在我们的案例中,我们将使用Single Spa做所谓的客户端组合。

我什么时候应该使用这种方法? (When should I use this approach?)

At Unbabel we currently use a micro frontend architecture for one of our new customer-facing product.

Unbabel,我们目前将微前端架构用于我们面向客户的新产品之一。

Like any technical decision, there are gains and tradeoffs. We weighted a couple of factors when deciding for this approach:

像任何技术决策一样,也有收益和权衡。 在决定采用这种方法时,我们权衡了以下两个因素:

  • The product to build would be comprised of at least 6 distinct areas, i.e. interfaces;

    要构建的产品将至少包含6个不同的区域,即界面;
  • Several multi-disciplinary teams would own and have full autonomy on delivering parts of the product;

    几个跨学科的团队将拥有并拥有完全的自主权来交付部分产品;
  • Have the possibility of partially changing parts of the product’s stack. Although Vue is the company’s framework of choice, we don’t know if it’ll still be what we want to use 3 years from now;

    有可能部分更改产品堆栈的一部分。 尽管Vue是该公司的首选框架,但我们不知道3年后它是否仍将是我们要使用的框架。

You should consider these advantages:

您应该考虑以下优点:

  • Teams have a greater autonomy on delivering value into the product at different paces as their development can be largely independent from other teams;

    团队在以不同的速度为产品交付价值方面拥有更大的自主权,因为他们的开发可以很大程度上独立于其他团队。
  • Ability to have totally separate repositories, test and deployment flows;

    具有完全独立的存储库,测试和部署流程的能力;
  • Ability to easily override parts of an application’s interface (i.e. A/B testing, incremental rollout);

    能够轻松覆盖应用程序界面的各个部分(即A / B测试,增量推出);
  • Use different frameworks side-by-side or perform experiments without affecting other parts of your application;

    并排使用不同的框架或进行实验,而不会影响应用程序的其他部分;
  • Ability to refactor parts of your product without having to change it all at once;

    能够重构产品的某些部分而无需一次更改所有内容;

You should consider these caveats:

您应该考虑以下警告:

  • Increased overhead to set up, deploy and maintain depending on the scope and characteristics of the product you’re building;

    根据要构建的产品的范围和特性,增加设置,部署和维护的开销;
  • More moving parts: it’s important to have a solid documentation on how everything is setup and relates as well as defining guidelines that govern how development is to be done within the architecture;

    移动的更多部分:重要的一点是要有一个坚实的文档,说明所有内容的设置和相关方式以及定义指导如何在体系结构内完成开发的准则;
  • Steeper learning for developers to understand the architecture, its lifecycle and dependencies. Hence having thorough documentation is imperative;

    为开发人员提供更深入的学习,以了解其架构,生命周期和依赖性。 因此,必须有详尽的文档记录;
  • Micro frontends are still relatively new and there is no one-size-fits-all methodology or well established consensus on how to achieve this. Be ready to do a fair amount of R&D depending on your case;

    微型前端仍然是相对较新的技术,尚无一种千篇一律的方法论或如何达成此目标的公认共识。 根据您的情况准备好大量的研发工作;

In my experience, this approach is best when building a relatively large web app where you want to provide flexibility to multiple teams and have enough time to dedicate to governance and documentation.

以我的经验,在构建一个相对大型的Web应用程序时,这种方法是最好的,在该应用程序中,您希望为多个团队提供灵活性,并有足够的时间专门用于治理和文档编制。

Having said that, you can definitely leverage on many of the micro frontend advantages with a team of 2-3 people or even alone.

话虽如此,您绝对可以在2-3人甚至一个人的团队中利用许多微型前端优势。

架构图 (Architecture Diagram)

In a nutshell: vendor dependencies get loaded from a CDN, app 1 and 2 bundles get loaded from S3/GCS and our orchestrator composes/bundles it all together.
简而言之:从CDN加载供应商依赖项,从S3 / GCS加载应用程序1和2捆绑包,我们的协调器将它们全部组合/捆绑在一起。

构建Orchestrator应用 (Building the Orchestrator App)

The orchestrator app is nothing but the project holding single-spa which is responsible for determining which applications get loaded depending on their activation function (more on that later).

Orchestrator应用程序不过是拥有单个spa的项目,该项目负责根据激活功能来确定加载哪些应用程序(稍后会详细介绍)。

This walkthrough follows single-spa’s recommended setup. You can also check the vue-microfrontends example repo.

本演练遵循单水疗中心的推荐设置 。 您还可以查看vue-microfrontends示例repo

建立专案 (Create project)

  1. Create a parent folder that will hold all of your project folders and cd into it;

    创建一个父文件夹,该文件夹将保存您所有的项目文件夹并cd进入其中;

  2. Create a folder named orchestrator and cd into it;

    创建一个名为orchestrator的文件夹并cd进入其中;

  3. Run npm init to create an empty package.json — when asked for the entry file name, set it as main.js;

    运行npm init来创建一个空的package.json -当要求输入文件名时,将其设置为main.js;

  4. Modify the package.json’s scripts:

    修改package.json的脚本:

..."scripts": {"serve": "webpack-dev-server --mode=development --env.isLocal=true","lint": "eslint src","prettier": "prettier --write './**'","build": "webpack --mode=production"
},
...

5. Install the required dependencies:

5.安装所需的依赖项:

npm i -D @babel/core @babel/preset-env @types/systemjs babel-eslint babel-loader clean-webpack-plugin concurrently eslint eslint-config-important-stuff eslint-config-prettier eslint-plugin-prettier html-webpack-plugin mini-css-extract-plugin prettier pretty-quick webpack webpack-cli webpack-dev-server dotenv-webpack

添加index.ejs (Add index.ejs)

This is what our users will hit when visiting our application. It uses EJS to easily generate the HTML markup on build time.

这就是我们的用户在访问我们的应用程序时会受到的打击。 它使用EJS在构建时轻松生成HTML标记。

On the root of the Orchestrator folder, create an index.ejs file and add the following content:

在Orchestrator文件夹的根目录上,创建一个index.ejs文件并添加以下内容:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Microfrontend</title>
    
    <meta name="importmap-type" content="systemjs-importmap">
    <script type="systemjs-importmap" src="/importmap.json"></script>


    <% if (process.env.NODE_ENV === 'development') { %>
    <script type="systemjs-importmap" src="/local-importmap.json"></script>
    <% } %>


    <script src="https://unpkg.com/import-map-overrides@1.7.2/dist/import-map-overrides.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/system.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/amd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/named-exports.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/named-register.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/use-default.min.js"></script>
  </head>


  <body>
    <script>
      System.import('orchestrator');
    </script>
    <import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
  </body>
  
  <style type="text/css">
    .snackbar-container {
      transition: all 500ms ease;
      transition-property: top, right, bottom, left, opacity;
      font-family: Roboto, sans-serif;
      font-size: 14px;
      min-height: 14px;
      background-color: #070b0e;
      position: fixed;
      display: flex;
      justify-content: space-between;
      align-items: center;
      color: white;
      line-height: 22px;
      padding: 18px 24px;
      bottom: -100px;
      top: -100px;
      opacity: 0;
      z-index: 9999;
    }
    .snackbar-container .action {
        background: inherit;
        display: inline-block;
        border: none;
        font-size: inherit;
        text-transform: uppercase;
        color: #4caf50;
        margin: 0 0 0 24px;
        padding: 0;
        min-width: min-content;
        cursor: pointer;
    }
    .snackbar-pos.bottom-left {
      top: auto !important;
      bottom: 0;
      left: 0;
    }
    @media (min-width: 640px) {
      .snackbar-container {
        min-width: 288px;
        max-width: 568px;
        display: inline-flex;
        border-radius: 2px;
        margin: 24px;
      }
    }
    @media (max-width: 640px) {
      .snackbar-container {
        left: 0;
        right: 0;
        width: 100%;
      }
    }
  </style>
</html>

What’s happening here?

这里发生了什么事?

  1. We want to make use of “bare import specifiers” in the browser through import maps. Because the import map specification is only implemented in Chrome, we’ll use SystemJS to load our import map (line 9) and import the modules we want (line 25);

    我们想通过导入地图在浏览器中使用“裸导入说明符”。 由于导入地图规范仅在Chrome中实现,因此我们将使用SystemJS加载导入地图(第9行)并导入所需的模块(第25行);

  2. Lines 12–13 allow us to load additional modules or override modules defined in importmap.json;

    第12-13行允许我们加载其他模块或覆盖importmap.json中定义的模块

  3. Line 25 loads our project’s entry point (main.js);

    第25行加载了我们项目的入口点( main.js );

  4. Because SystemJS is a dynamic JS module loader and by using import-map-overrides (line 27), we’re able to dynamically reload modules on-the-fly (more on that later);

    因为SystemJS是动态JS模块加载器,并且通过使用import-map-overrides (第27行),所以我们能够即时动态地重新加载模块(稍后会详细介绍);

  5. The style tag is temporary for now, simply used to style the Snackbar we’ll use in part 2 of the walkthrough;

    样式标签暂时是暂时的,仅用于为演练的第二部分中使用的Snackbar样式。

Using this approach of browser ES Modules + import-maps has several advantages, but mainly, it allows us to only load once the dependencies shared across micro apps (ex. Vue) and easily share common code between apps.

使用这种浏览器ES模块+导入映射的方法具有几个优点 ,但是主要是,它允许我们仅加载一次跨微应用程序(例如Vue)共享的依赖项,并轻松地在应用程序之间共享通用代码。

添加importmap.json (Add importmap.json)

Create a file in the project’s root named importmap.json and add the following content:

在项目的根目录中创建一个名为importmap.json的文件,并添加以下内容:

{
  "imports": {
    "single-spa": "https://cdnjs.cloudflare.com/ajax/libs/single-spa/5.3.2/system/single-spa.min.js",
    "vue": "https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js",
    "vue-router": "https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js",
    "pubsub-js": "https://cdnjs.cloudflare.com/ajax/libs/pubsub-js/1.7.0/pubsub.js",
    "snackbar": "https://cdn.jsdelivr.net/npm/node-snackbar@0.1.16/src/js/snackbar.min.js"
  }
}

This file determines the location of the imports we want to do in-browser. For now we’re only loading libraries we need for our architecture, but it will also point to the locations of our applications once these are made available. We’re loading it locally for now, but this JSON file should also be served independently, allowing you to change it separately from any CI/CD process. For instance, you can use jsonbin.io to host JSON (for tests only) and easily change its content.

该文件确定我们要在浏览器中进行导入的位置。 目前,我们只加载我们架构所需的库,但是一旦这些库可用,它也将指向应用程序的位置。 我们现在暂时在本地加载它,但是此JSON文件也应独立提供,从而使您可以与任何CI / CD流程分开对其进行更改。 例如,您可以使用jsonbin.io托管JSON(仅用于测试)并轻松更改其内容。

Besides the fundamental dependencies (single-spa, vue & vue-router), we’re also importing two libraries we’ll use in the 2nd part of this walkthrough:

除了基本的依赖关系( single-spavuevue-router )之外,我们还将导入两个库,我们将在本演练的第二部分中使用它们:

  • pubsub-js, a publish/subscribe pattern implementation that will act as an event bus;

    pubsub-js ,将充当事件总线的发布/订阅模式实现;

  • snackbar, a Material style notification toast;

    快餐栏 ,材料风格的通知吐司;

添加local-importmap.json (Add local-importmap.json)

Having a local import map is crucial for local development, otherwise we’d need to spin every micro frontend locally in order to have the whole architecture run properly. local-importmap.json allows us to add and override anything defined in importmap.json (i.e. override an app in staging or production or load a different version of Vue).

拥有本地导入地图对于本地开发至关重要,否则我们需要在本地旋转每个微前端,以使整个体系结构正常运行。 local-importmap.json允许我们添加和覆盖importmap.json中定义的任何内容(即在登台或生产中覆盖应用程序或加载其他版本的Vue)。

For now we’ll only define the entry point for our Orchestrator app running locally:

现在,我们仅定义在本地运行的Orchestrator应用的入口点:

{
  "imports": {
    "orchestrator": "http://localhost:5000/main.js"
  }
}

Add this file to your .gitignore, allowing team members to import different apps depending on what they’re working on without affecting other’s imports.

将此文件添加到您的.gitignore中 ,使团队成员可以根据自己的工作导入不同的应用程序,而不会影响其他人的导入。

添加webpack.config.js (Add webpack.config.js)

Create a webpack.config.js in the project’s root and add the following content to it:

在项目的根目录中创建一个webpack.config.js,并向其中添加以下内容:

const path = require('path');
const Dotenv = require('dotenv-webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {
  CleanWebpackPlugin,
} = require('clean-webpack-plugin');


module.exports = env => ({
  entry: {
    'main': path.resolve(__dirname, 'main'),
  },
  output: {
    filename: '[name].js',
    libraryTarget: 'system',
    path: path.resolve(__dirname, 'dist')
  },
  devtool: 'sourcemap',
  module: {
    rules: [
      { parser: { system: false } },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [{ loader: 'babel-loader' }]
      }
    ]
  },
  devServer: {
    port: 5000,
    headers: {
      'Access-Control-Allow-Origin': '*'
    },
    disableHostCheck: true,
    historyApiFallback: true
  },
  plugins: [
    new Dotenv({
      path: path.resolve(__dirname, './.env')
    }),
    new HtmlWebpackPlugin({
      inject: false,
      template: 'index.ejs',
      templateParameters: {
        isLocal: env && env.isLocal
      }
    }),
    new CleanWebpackPlugin()
  ],
  externals: [
    'single-spa',
    'vue',
    'vue-router'
  ]
});

添加.env (Add .env)

Creaste a .env file with the content:

使用以下内容创建一个.env文件:

NODE_ENV=development

添加main.js (Add main.js)

Add a main.js file with the following content:

添加具有以下内容的main.js文件:

import {
  start,
} from 'single-spa';


Promise.all([
  System.import('pubsub-js'),
  System.import('snackbar')
]).then(() => {
  
  start();


  Snackbar.show({
    text: 'Single SPA loaded!'
  });
});

For now the entry point is pretty simple: it imports single-spa, waits for all modules to be imported (pubsub-js & snackbar) and once loaded, starts single-spa and displays a success toast. We’ll add more to this file as we evolve the architecture.

现在,入口点非常简单:它导入single-spa ,等待所有模块导入( pubsub-js快餐栏 )并加载后,启动single-spa并显示成功的吐司。 随着架构的发展,我们将向此文件添加更多内容。

You can now run npm run serve and visit http://localhost:5000. If all goes well, you should see a toast on the bottom left corner saying “Single SPA loaded”.

现在,您可以运行npm run serve并访问http:// localhost:5000 。 如果一切顺利,您应该在左下角看到一个烤面包,上面写着“已加载单个SPA”。

We now have the basic setup that we’ll use to load and orchestrate the micro frontends we’ll build.

现在,我们有了基本的设置,可用于加载和编排要构建的微型前端。

The orchestrator app should look like this.

协调器应用程序应如下所示

创建第一个Vue应用 (Create the first Vue App)

Our Vue app will need to suffer a couple of minor modifications in order to be registrable with single-spa. Luckily, single-spa allows to easily integrate with all major frameworks.

我们的Vue应用程序需要进行一些小的修改,才能通过Single-spa进行注册。 幸运的是,单spa可以轻松地与所有主要框架集成

In our case, we’ll use the vue-cli-plugin-single-spa which performs those changes for us.

在我们的例子中,我们将使用vue-cli-plugin-single-spa为我们执行这些更改。

  1. Within the parent folder, create a Vue app named app-one:

    在父文件夹中,创建一个名为app-one的Vue应用程序

vue create app-one

2. cd into app-one and install the single-spa Vue plugin which will perform the changes as described here:

2. cd应用酮和安装单温泉 Vue的插件,它会执行的改变这里描述

vue add single-spa

3. Install webpack’s EventHooksPlugin:

3.安装webpackEventHooksPlugin:

npm i -D event-hooks-webpack-plugin

4. Cleanup the boilerplate code from Vue by deleting the components, assets and public folders, and modify App.vue like this:

4.清除从Vue公司的样板代码通过删除组件资产公共文件夹,并修改App.vue是这样的:

<template>
  <div class="mf-Container">
    <h1>App One</h1>
  </div>
</template>


<script>

export default {
  name: 'AppOne',
}
</script>

5. Create a vue.config.js and add the following content:

5.创建一个vue.config.js并添加以下内容:

const path = require('path');
const fs = require('fs');
const EventHooksPlugin = require('event-hooks-webpack-plugin');


module.exports = {
  chainWebpack: (config) => {
    config.devServer.headers({
      'Access-Control-Allow-Origin': '*',
    });
    config.devServer.set('disableHostCheck', false);
    config.devServer.set('sockPort', 8080);
    config.devServer.set('sockHost', 'localhost');
    config.devServer.set('port', 8080);
    config.devServer.set('inline', false);
    config.devServer.set('hot', true);


    config.output.filename('[name].js');
    config.output.publicPath('/');


    config.externals([
      'vue',
      'vue-router'
    ]);
  },
  lintOnSave: true,
  filenameHashing: false,
  configureWebpack: {
    plugins: [
      new EventHooksPlugin({
        done: () => {
          if (process.env.NODE_ENV !== 'development') {
            const buildDir = path.join(__dirname, '/dist');
            fs.unlinkSync(`${buildDir}/index.html`);
          }
        },
      }),
    ],
  },
};

A couple of things to note here:

这里需要注意几件事:

  • We’re telling webpack that we want the output to be done at the root of the bundle folder (line 17);

    我们告诉webpack我们希望在bundle文件夹的根目录下完成输出(第17行);

  • We’re telling webpack not to include some dependencies in the final bundle through config.externals, as Vue and vue-router will be provided by the Orchestrator app;

    我们告诉Webpack通过config.externals在最终的包中不要包含某些依赖项,因为Vue和vue-router将由Orchestrator应用提供;

  • We’re not hashing file names;

    我们不对文件名进行哈希处理;
  • We’re removing index.html from the dist built folder as it’s not necessary;

    我们从dist生成的文件夹中删除index.html ,因为这是没有必要的。

App-one should now be setup like shown in this branch. Let’s spin it up and make sure its entry file is accessible.

现在应按此分支所示设置App-one。 让我们旋转它并确保其条目文件可访问。

Run npm run serve and visit http://localhost:8080/app.js. You should be able to see the compiled Javascript.

运行npm run serve并访问http:// localhost:8080 / app.js。 您应该能够看到已编译的Javascript

向单spa注册应用 (Register the app with single-spa)

We now need to tell single-spa we want to register an app in order to mount it within the Orchestrator app. Within the Orchestrator app:

现在,我们需要告诉单温泉我们要注册一个应用程序,以便将其安装在Orchestrator应用程序中。 在Orchestrator应用中:

  1. Add the module entry to local-importmap.json so that SystemJS can import it:

    将模块条目添加到local- importmap.json中,以便SystemJS可以导入它:

{
  "imports": {
    "orchestrator": "http://localhost:5000/main.js",
    "app-one": "http://localhost:8080/app.js"
  }
}

Note that the key value “app-one” must be the same used by the app’s setPublicPath function located in src/set-public-path.js.

请注意 ,键值“ app-one”必须与位于src / set-public-path.js中的应用程序的setPublicPath函数使用的键相同

2. Modify main.js so that it looks like this:

2.修改main.js ,使其如下所示:

import {
  start,
  registerApplication
} from 'single-spa';


Promise.all([
  System.import('pubsub-js'),
  System.import('snackbar')
]).then(() => {
  registerApplication({
    name: 'app-one',
    app: () => System.import('app-one'),
    activeWhen: location => location.pathname.startsWith('/')
  });
  start();
});

Here registerApplication is used to register an app within single-spa.

这里的registerApplication用于在单spa中注册应用程序

activeWhen is an important function (activation function) that essentially determines when should this app be mounted. It must return true to mount it. In this example, we simply want the current location to start with “/” but you could verify many additional aspects, such as whether a user has a specific role, device type, etc.

activeWhen是一项重要功能(激活功能),它从本质上确定该应用程序的安装时间。 挂载它必须返回true 。 在此示例中,我们仅希望当前位置以“ /”开头,但是您可以验证许多其他方面,例如用户是否具有特定角色,设备类型等。

Inspecting the app through Chrome’s Dev tools
通过Chrome的开发工具检查应用

Now visit http://localhost:5000 and you should see the text “App One”. If we inspect the browser’s code using developer tools, we can see single-spa is appending our app to the body tag.

现在访问http:// localhost:5000 ,您应该看到文本“ App One”。 如果我们使用开发人员工具检查浏览器的代码,则可以看到single-spa将我们的应用程序附加到body标签。

You can go ahead and change the content of the H1 tag and verify that the hot code reloading functionality is working as intended.

您可以继续更改H1标签的内容,并验证热代码重载功能是否按预期工作。

创建第二个Vue应用 (Create the second Vue App)

We’ll now create another Vue app by following the steps 1 through 5 from the previous section, to the exception of these changes:

现在,我们将按照上一部分中的步骤1至5来创建另一个Vue应用,但以下更改除外:

  1. Name it “app-two”;

    将其命名为“ app-two”;
  2. Change the port’s number in vue.config.js to 8081 to avoid clashing ports with app-one;

    vue.config.js中的端口号更改为8081,以避免与app-one冲突的端口

  3. Change the content of the H1 tag in App.vue to “App Two”;

    App.vue中 H1标签的内容更改为“ App Two”;

注册应用二 (Register app-two)

  1. Again, we’ll add the module entry point to our local-importmap.json file which will look like:

    再次,我们将模块入口点添加到我们的local-importmap.json文件中,该文件如下所示:
{
  "imports": {
    "orchestrator": "http://localhost:5000/main.js",
    "app-one": "http://localhost:8080/app.js",
    "app-two": "http://localhost:8081/app.js"
  }
}

2. Now register it in main.js but this time we’ll abstract a bit how apps are registered:

2.现在在main.js中注册它,但是这次我们将简要介绍如何注册应用程序:

import {
  start,
  registerApplication
} from 'single-spa';


const apps = [
  {
    name: 'app-one',
    app: () => System.import('app-one'),
    activeWhen: location => location.pathname.startsWith('/')
  },
  {
    name: 'app-two',
    app: () => System.import('app-two'),
    activeWhen: location => location.pathname.startsWith('/')
  }
]


Promise.all([
  System.import('pubsub-js'),
  System.import('snackbar')
]).then(() => {
  apps.forEach(app =>  registerApplication(app) );
  start();
});

If we now visit http://localhost:5000, we should see both of our apps:

如果现在访问http:// localhost:5000,则应该看到两个应用程序:

Our two apps shown on top of each other.
我们的两个应用程序彼此重叠显示。

At this stage your code should look like this for orchestrator, like this for app-one and like this for app-two.

在这个阶段,你的代码看起来应该像这样配器像这样应用程序,一个 像这样应用程序两种。

控制布局 (Controlling the Layout)

Notice in the above image our apps are stacked on top of each other.

请注意,在上图中,我们的应用程序相互堆叠。

That’s single-spa’s default behavior. If you only allow to mount one single app per location (i.e. app-one for /foo and app-two for /bar), that shouldn’t be a problem.

这是单一温泉的默认行为。 如果只允许在每个位置挂载一个应用程序(即/ foo 的一个 应用程序和 / bar 的两个应用程序 ),那应该不是问题。

But what if you want two apps mounted simultaneously and control how they are positioned? For that, we can define where we want Vue to mount the app, using appOption.el.

但是,如果要同时安装两个应用程序并控制它们的位置怎么办? 为此,我们可以使用appOption.el定义我们希望Vue挂载应用程序的位置

定义占位符元素 (Defining the placeholder elements)

We’ll be mounting app-one and app-two inside the elements with ids “appOne-placeholder and “appTwo-placeholder respectively. We’ve replaced the style tag with what determines how the placeholders are positioned (we’ll apply the previous styles in a moment).

我们将分别在ID为“ appOne-placeholder 和“ appTwo-placeholder 的元素中安装app-oneapp-two 。 我们已经用决定占位符位置的方式替换了样式标签(我们稍后将应用以前的样式)。

  1. First we must modify Orchestrator’s index.ejs to have the receiving placeholders for the micro frontends. Modify the file so it looks like:

    首先,我们必须修改Orchestrator的index.ejs以使其具有微前端的接收占位符。 修改文件,使其看起来像:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Microfrontend</title>
    
    <meta name="importmap-type" content="systemjs-importmap">
    
    <script type="systemjs-importmap" src="/importmap.json"></script>


    <% if (process.env.NODE_ENV === 'development') { %>
    <script type="systemjs-importmap" src="/local-importmap.json"></script>
    <% } %>


    <script src="https://unpkg.com/import-map-overrides@1.7.2/dist/import-map-overrides.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/system.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/amd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/named-exports.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/named-register.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.1.1/extras/use-default.min.js"></script>
  </head>


  <body>
    <div class="mf-Containers">
      <div id="appOne-placeholder" class="mf-Container"></div>
      <div id="appTwo-placeholder" class="mf-Container"></div>
    </div>
    <script>
      System.import('orchestrator');
    </script>
    <import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
  </body>


  <style type="text/css">
    .mf-Containers {
      display: flex;
      align-items: center;
      justify-content: space-around;
    }
    .mf-Container {
      width: 100%;
    }
  </style>
</html>

2. Modify app-one’s main.js like such:

2.像这样修改app-onemain.js

import './set-public-path';
import Vue from 'vue';
import singleSpaVue from 'single-spa-vue';


import App from './App.vue';


Vue.config.productionTip = false;


const containerSelector = '#appOne-placeholder'


const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    render: (h) => h(App),
    el: containerSelector
  },
});


export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;


export const devtools = {
  overlays: {
    selectors: [
      containerSelector
    ],
  }
};

Notice the value of containerSelector (in this case and id attribute) must match that of the placeholder we defined previously. We’re also adding lines 23 through 29 in order to be able to normally use single-spa’s extension highlight functionality.

请注意, containerSelector的值(在本例中为id属性)必须与我们先前定义的占位符的值匹配。 我们还将添加第23至29行,以便能够正常使用Single-Spa的扩展突出显示功能。

3. Do the same on app-two’s main.js except change containerSelector’s value to “#appTwo-placeholder”;

3.在应用程式二main.js上执行相同的操作,除了将containerSelector的值更改为“#appTwo-placeholder”;

As you can see, we’re now able to have the two apps side by side:

如您所见,我们现在可以同时使用两个应用程序:

Apps mounted in their respective placeholder containers.
应用安装在各自的占位符容器中。

At this stage you code should look like this for orchestrator, like this for app-one and like this for app-two. You should now have a pretty good understanding of how to create a micro frontend architecture using single-spa.

在这个阶段,你的代码看起来应该像这样配器像这样应用程序,一个 像这样应用程序两种。 您现在应该对如何使用Single-Spa创建微前端架构有一个很好的了解。

But should we place all of our CSS within index.ejs? Should it live inside the orchestrator app? How do applications “share state”? Can I reuse components? The second part of this guide will focus on showing you how to do these and more.

但是我们是否应该将所有CSS放在index.ejs中? 它应该存在于Orchestrator应用程序中吗? 应用程序如何“共享状态”? 我可以重复使用组件吗? 本指南的第二部分将重点介绍如何执行这些操作以及更多操作。

In the meantime…

同时…

阅读材料 (Reading Material)

.. here are some more resources to learn and discover more about micro frontend architecture:

..这里有一些更多的资源可用于学习和发现有关微前端架构的更多信息:

Give this article a

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设置带有vue和单个spa的微型前端架构 的相关文章

随机推荐

  • 操作系统理论知识9

    我的操作系统笔记 第四章 存储器管理 存储器部件 主存 保存进程运行时的程序和数据 寄存器 速度最快 价格昂贵容量不大 一般以字为单位 只要存放指令一次操作的数据就够了 高速缓存 速度快 存放部分内存数据 硬件自动处理 磁盘缓存 内存的一部
  • Quartz概述

    Quartz是开源任务调度框架中的翘楚 它提供了强大的 任务调度机制 Quartz允许开发人员灵活的定义触发器的调度时间表 并可对触发器和任务进行关联映射 此外 Quartz提供了调度运行环境的持久化机制 可以保存并恢复调度现场 即使系统因
  • Photoscan/Metashape 2.0.0中的地面激光扫描处理

    在Metashape 原Photoscan 2 0 0 结构化地面激光扫描和非结构化航空激光扫描都可以使用导入点云 文件 gt 导入 gt 导入点云 命令导入 导入时会保留所有点属性 包括结构化信息 本文讨论以下主题 如何将激光扫描数据导入
  • 创建进程函数fork的使用

    1 pid t fork void 作用 创建一个新的进程 返回值 如果调用成功 返回两次 返回值为0 代表当前进程是子进程 返回值为非负数 代表当前进程为父进程 调用失败 返回 1 C程序一开始 就会产生一个进程 当这个进程执行到fork
  • 力扣刷题序号459.重复的子字符串——C语言实现

    给定一个非空的字符串 s 检查是否可以通过由它的一个子串重复多次构成 思路与算法 根本思路 区间移动 1 先判断一定不是由子串构成的情况 即输入的母串长度为0或1的情况 2 当母串长度 gt 2时 需考虑多种情况 需枚举算法 先遍历整个母串
  • 关于css中的z-index 属性

    检索或设置对象的层叠顺序 较大 number 值的对象会覆盖在较小 number 值的对象之上 如两个绝对定位对象的此属性具有同样的 number 值 那么将依据它们在HTML文档中声明的顺序层叠 对于未指定此属性的绝对定位对象 此属性的
  • ios弱网测试_弱网测试方法整理

    背景 昨天和几个同事讨论弱网测试方法 发现并不是很多人在没有公司专门开发的弱网工具的前提下 知道如何去进行弱网的模拟测试 于是就整理了以下几种测试方法 供大家参考 下面只是对弱网设置的界面进行了简单的介绍 有兴趣的童鞋可以深入研究 另外还有
  • win10 git 命令行出现 no matching host key type found. Their offer: ssh-rsa 解决方案

    一 现象 win10电脑 配置好公私钥之后 仍然无法直接用 git ssh的方式 下载代码 出现形如 no matching host key type found Their offer ssh rsa 的错误 转载 https www
  • CSS动画——加载的菊花转动画

    CSS动画 加载的菊花转动画 最近在整理工作过程中用到的一些动画 菊花转loading就是其中一个 本人比较爱较劲 看到这个就想用代码实现 虽然我很菜 但是我也要做一个菜中VIP 话不多说 先插播一个类似想要实现的gif吧 对不起这个gif
  • scrapy效率提升篇

    scrapy基于twisted异步IO框架 downloader是多线程的 但是 由于python使用GIL 全局解释器锁 保证同时只有一个线程在使用解释器 这极大限制了并行性 在处理运算密集型程序的时候 Python的多线程效果很差 而如
  • vue+wangEditor的富文本编辑器的使用

    vue wangEditor的富文本编辑器的使用 先配置新建一个
  • 华为防火墙配置了限制一台主机只能访问固定域名和IP的安全策略后打开网站加载速度很慢半天打不开

    环景 华为USG6311E VRP Software Version 5 170 USG6300E V600R007C00SPC200 V200R007C00SPC091 PC联想win10专业版 谷歌浏览器版本 88 0 4324 182
  • SVM —— 在复杂数据上应用核函数

    对于非线性可分的数据 我们需要使用一种称为核函数 kernel 的工具将数据转换成易于分类器理解的形式 目录 利用核函数将数据映射到高位空间 径向基核函数 利用核函数将数据映射到高位空间 对于非线性可分的数据 我们要将数据从一个特征空间转换
  • docker-compose部署elk

    version 3 4 services es master container name es master image elasticsearch 7 3 1 restart always ports 19200 9200 19300
  • MySQL数据库8(十四)聚合函数和运算符

    MySQL数据库8 十四 聚合函数和运算符 利用一些统计函数 聚合函数 count 统计每组中的数量 如果统计的目标是字段 那么不统计空NULL字段 如果为 代表统计记录 avg 求平均值 sum 求和 max 求最大值 min 求在最小值
  • 【游戏提取/超详细记录向】关于unity游戏的资源提取所需资源及方法简介(AssetBundle及libil2cpp.so等解密)

    方法一 AssetStudio提取 仅针对ab包及 assets boundle和 unity3d未加密的情况下 1 在我们拿到一个游戏的安装包时 首先会疑惑如何打开 下载bandzipBandizip Free zip 7z unzip
  • WIN10搭建FTP(全套完整)

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net zhj 1121 article details 85344185 目录 已经搭建好了
  • [源码解读]深入理解pthread中的condition条件变量

    深入理解pthread中的condition条件变量 文章目录 深入理解pthread中的condition条件变量 pthread cond init c pthread cond wait c pthread cond signal c
  • java希尔排序

    public class ShellSort public static void main String args int a 9 8 7 0 1 3 2 10 5 12 7 0 15 int n a length for int add
  • 设置带有vue和单个spa的微型前端架构

    A practical walkthrough on building a micro frontend architecture with multiple Vue js apps using single spa Note that y
Powered by Hwhale