乐趣区

关于前端:vitepluginfederation

背景

随着业务量的扩大、业务复杂度的逐步进步,咱们的零碎也在高速成长的过程中呈现了工程收缩、开发保护艰难、开发团队间协同艰难等相干问题。随之而来,微前端的概念也被一直提出。vite作为一个新兴的前端开发与构建工具,也须要在这方面进行摸索与尝试。

概述

vite-plugin-federation是一款为 vite 提供,用于反对多个独立构建的利用能够将本人的局部能力作为组件提供进去,组成一个应用程序。他们之间不存在相互依赖,能够进行独立的开发和部署。灵感来源于 webpack 5 提供的 Module Federation 个性。
尽管这款插件还在通知成长的过程中,也曾经过得了 vite 社区的关注,并收录进了其框架插件中

什么是Module Federation

Module Federation是心愿援用动静援用其余利用的模块,这里的模块与通常意义上的利用级微前端相比,要小。比方咱们通常要公共应用的头部导航,侧边栏等。各个利用独立构建、打包、部署,按需援用、组合成一个新的利用。通常各个援用之间是通过对立入口文件中,对外裸露模块及其相干信息,通过异步援用的的伎俩,加载共享进去的模块,供本人应用。

咱们为什么要应用 Vite

💡 极速的服务启动

应用原生 ESM 文件,无需打包!

⚡️ 轻量疾速的热重载

无论应用程序大小如何,都始终极快的模块热重载(HMR)

🛠️ 丰盛的性能

对 TypeScript、JSX、CSS 等反对开箱即用。

📦 优化的构建

可选“多页利用”或“库”模式的预配置 Rollup 构建

🔩 通用的插件

在开发和构建之间共享 Rollup-superset 插件接口。

🔑 齐全类型化的 API

灵便的 API 和残缺 TypeScript 类型。

基于以上 vite 给咱们提供了这么多的理由,咱们将 vite 作为前端开发与构建工具的最优抉择之一也是理所应当的,特地是在咱们的前端工程日益宏大的明天,疾速的批改即所得是一件如许美好的事件。

module federation我的项目构建的根本逻辑

vite-plugin-federation 通过 viterollup提供的 hock,对构建文件进行干涉,从而将所有近程模块的组件,汇总到remoteEntry.js 中。本地模块通过 remoteEntry.js 入口,从而调用加载三方组件以及组件编译后的 jscss 等文件。

Remote: 近程模块,不属于以后构建,在应用时从容器(近程模块)加载编译好的文件。
Host:生产其余 Remote 提供的组件的本地模块。
Shared:Host应用 Remote 提供的组件时,须要依赖的第三方库,例如 vuereact 等。

更具体的架构,能够参考一下社区的架构文档。

如何上手

基于 vite 的劣势,社区奉献了 vite-plugin-federation,能够让咱们在本地模块应用近程模块的纯js 组件,用以抽取一些公共组件、小团队开发一些绝对独立的性能,实现独立部署并提供给其余本地模块应用。
接下来以 vue3 为例,看一下如何开发、配置和应用 vite-plugin-federation 来达成咱们的想法。

装置 vite 并创立 vue3 我的项目

vite-plugin-federation既然是 vite 的组件,咱们要先装置 vite 并建设两个 vue3 的我的项目,这些筹备工作就不在这里赘述了,有趣味的敌人能够先去理解一下,也比较简单:
搭建第一个 Vite 我的项目
当然,如果你的我的项目当初应用的是 webpack,想要体验一下vite 前端开发与构建工具,也能够应用 webpack-to-vite 工具,来转换一下曾经存在我的项目的,看看 vite 是不是趁手。

装置vite-plugin-federation

npm i @originjs/vite-plugin-federation

根本构造

projects
└───home // 对应 remote 远端模块
│   │   Content.vue // 子组件
│   │   button.js   // 子组件
│   │   vite.config.ts   // 绝对应配置
│   
└───layout // 对应 host 本地模块
│   │   main.js // 异步引入远端模块
│   │   vite.config.ts   // 绝对应配置

创立 Remote 近程模块

1. 创立一个 vue3 的组件

你能够应用 h 函数进行绘制
Button.js

import {h} from "vue";
​
const button = {
    name: "btn-component",
    render() {
        return h(
            "button",
            {
                id: "btn-remote",
                style: {
                    'background-color': 'red',
                    'border': 'none',
                    'color': 'white',
                    'padding': '15px 32px',
                    'text-align': 'center',
                    'text-decoration': 'none',
                    'display': 'inline-block',
                    'font-size': '16px'
                },
                onClick: () => {this.$store.state.cartItems++}
            },
            "Hello Remote Button"
        );
    },
};
​
export default button;
​

也能够应用 vue3 的组件文件
Content.vue

<template>
  <div :class="$style.red">{{title}}</div>
  <p id='cart-item' :class="$style.red">cartItems from vuex: {{cartCount}}</p>
  <div :class="[$style.red, $style.bold]">Red and bold</div>
</template>
<script>
export default {data() {
    return {title: 'Remote Component in Action..'}
  },
  computed:{cartCount() {return this.$store.state.cartItems}
  }
}
</script>
<style module>
.red {color: red;}
.bold {font-weight: bold;}
</style>
​

留神:

为了尽量避免近程模块与本地模块 css 款式之间互相净化,能够应用 css-module 等,相干 css 隔离计划或者行内款式。

2. 在 vite.config.ts 中,配置federation 选项

vite.config.ts

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue' // 引入 vite 针对 vue 反对的插件
import federation from "@originjs/vite-plugin-federation"; // 引入 vite-plugin-federation
​
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),
    federation({
      name: 'home', // 近程模块名称
      filename: 'remoteEntry.js', // 近程模块入口文件,与本地模块中 `remotes` 配置绝对应
      exposes: {
        './Content': './src/components/Content.vue', // 组件名称及其对应文件
        './Button': './src/components/Button.js'
      },
      shared: ["vue","vuex"] // 对外提供的组件所依赖的第三方依赖,这个例子应用了 `vue`,`vuex`,此处还能够配置依赖版本,参考 `Readme.md`
    })
  ],
  build: {
    target:'es2020', // 针对非行内款式,须要构建规格为 es2020,否则款式会生效,控制台给出提醒
    minify: false,
    cssCodeSplit: false,
    rollupOptions: {
      output: {minifyInternalExports: false}
    }
  }
})
​

3. 设置 federation.shared 选 项,用于在近程模块与本地模块之间共享第三方依赖

依据须要设置 federation.shared 选项,具体能够参考
vite.config.ts

export default defineConfig({
  ...
  plugins: [
    ...
    federation({
      ...
      shared: ["vue","vuex"] // 这里是繁难配置,具体配置能够点击参考,查看官网 Readme 文档
    })
  ],
})

Host 本地模块中,配置并应用近程模块中的组件

至此,咱们构建的远端模块曾经提供了,Content、Button 两个子组件,能够让本地模块进行应用。

1. 在 vite.config.ts 中配置近程模块的入口文件

vite.config.ts

import {defineConfig} from "vite";
import vue from "@vitejs/plugin-vue";
import federation from "@originjs/vite-plugin-federation";
​
// https://vitejs.dev/config/
export default defineConfig({
  server:{
    // host: "192.168.56.1", // 在 dev 场景下尽量显示申明 ip、port,避免 `vite` 启动时 ip、port 主动获取机制导致不精确的问题
    // port: 5100
  },
  cacheDir: "node_modules/.cacheDir", // 存储缓存文件的目录,非关键配置项
  plugins: [vue(),
    federation({
      name: "layout", // 近程模块名称,一个服务既能够作为本地模块应用近程模块组件,能够作为近程模块,对外提供组件
      filename: "remoteEntry.js", // 近程模块入口文件,与本地模块中 `remotes` 配置绝对应
      remotes: {
        home: "http://localhost:5001/remoteEntry.js", // 近程模块入口文件的网络地址,用于获取近程模块的 `remoteEntry.js` 来加载组件
        "common-lib": "http://localhost:5002/remoteEntry.js",
        "css-modules":"http://localhost:5003/remoteEntry.js"
      },
      shared: ["vue","vuex"] // 近程模块组件应用的第三方依赖,如果本地有能够优先应用本地;在 dev 模式下尽量在本地援用这些第三方依赖,避免第三方组件在 dev 和打包模式下不同导致的问题。})
  ],
  build: {
    target:'es2020',
    minify: false,
    cssCodeSplit: true,
    rollupOptions:{
      output:{minifyInternalExports:false}
    }
  },
});

2. 异步加载近程组件

main.js

import {createApp, defineAsyncComponent} from "vue";
import store from './store';
import Layout from "./Layout.vue";
​
const HomeContent = defineAsyncComponent(() => import("home/Content")); // 创立一个只有在须要时才会加载的异步组件, 近程模块名 / 组件名
const HomeButton = defineAsyncComponent(() => import("home/Button"));
​
const app = createApp(Layout); // 返回一个提供利用上下文的利用实例。利用实例挂载的整个组件树共享同一个上下文。​
app.component("home-content", HomeContent); // 将近程模块的组件,注册为组件
app.component("home-button", HomeButton);
​
app.use(store); // 装置 Vue.js 插件,这里是 `vuex`
app.mount("#root");
​

申明 vuex 插件,不是本文次要波及到的内容,按需便携即可
store.js

import {createStore} from 'vuex';
​
export default createStore({state() {
        return {cartItems: 5}
    }
});

3. 应用组件

<template>
  <Content />
  <Button />
  <hr />
  <!-- 应用近程模块,-->
  <home-content />
  <home-button />
</template>
​
<script>
import Content from "./components/Content.vue"; // 此处援用的是本地模块本人的组件
import Button from "./components/Button.js";
export default {  components: {
    Content,
    Button,
    UnusedButton,
  },};
</script>
​
<style scoped>
img {width: 200px;}
.h1 {
  border: 5px solid red !important;
  padding: 1px !important;
}
.section {
  border: 1px solid black;
  padding: 10px;
}
</style>
​

近程模块动态资源的反对

vite默认反对将导入或援用资源将内联为 base64 编码,这里和 webpack 是比拟类似的,不过设置阈值为4kb,比拟小,可能会让咱们不经意间引入的某些动态资源无奈在本地模块失常显示。当然,这也是各个编译、构建工具在衡量多申请和单个申请文件大小之间给出的一个衡量,大家能够依据本人的须要进行批改。

这里依据 Remote 须要对外提供的组件蕴含的动态资源大小,调整该阈值即可保障动态资源的对外提供。

针对问题:本地模块应用近程模块时,图片申请地址为本地模块地址,导致 404 资源申请失败。
参考文档:build.assetsInlineLimit
vite.config.ts

export default defineConfig({
  ...
  build: {
    assetsInlineLimit: 40960, // 40kb
    ...
  }
})

最初,再来一波宣传,VUE Shenzhen Meetup 第二期来啦~ 10 月 28 日举办哟,欢送大家报名参加啊 ~ 快点动动手指报名吧~!
报名链接:Anthony Fu 线上交换访谈

退出移动版