Föderation des Webpack-Moduls
Ich glaube, Sie können es hier finden, was darauf hinweist, dass Sie Webpack module federation
bereits ein gewisses Verständnis haben. Es gibt viele Tutorials in der Community, die Ihnen erklären, wie Sie es verwenden und was es tut. Wenn Sie nicht viel darüber wissen, können Sie sich auch zuerst die offizielle Dokumentation ansehen.
Genesis2.0 ist derzeit das einzige Framework, das von Vue2 in Bezug auf SSR unterstützt wird.Warum Webpack module federation
? Denn bei Genesis 1.0 wurde das Konzept einer Remote-Komponente vorgeschlagen, die es verschiedenen Diensten ermöglicht, Seiten anderer Dienste aufzurufen. Wenn wir das von Webpack vorgeschlagene Konzept sehen module federation
, haben wir bereits begonnen, über die Iteration von Genesis 2.0 nachzudenken
Bis heute haben wir endlich die gesamte Funktionsentwicklung abgeschlossen, und das Projekt im Unternehmen wurde mit der Aktualisierung begonnen, und die Remote-Komponenten wurden geändert, module federation
um auf neue Weise aufgerufen zu werden. In diesem Prozess haben wir erfolgreich das module federation
Problem gelöst, dass der TS-Typ nicht unterstützt wird, und wir können module federation
alle Eingabedateien stark zwischenspeichern
Verwenden des MF-Plugins
MF ist module federation
ein Akronym für ein Akronym, und ein Dienst kann entweder eine host
Seite oder eine remote
Seite sein.
Der Verwendung von Plug-Ins muss besondere Aufmerksamkeit geschenkt werden MF
, und die Extraktion externer CSS-Dateien wird nicht mehr unterstützt, da es bei Verwendung des host
Terminals unmöglich ist, remote
die Informationen der vom Terminal extrahierten externen CSS-Dateien zu kennen . Um dieses Problem zu lösen, renderer.renderHtml
wird der Funktion ein neuer Parameter hinzugefügt styleTagExtractCSS
: Wenn der Wert der Wert ist true
, wird der Inhalt des Style-Tags, der von der aktuellen Seite gerendert wird, in eine separate CSS-Datei extrahiert. Da der Inhalt des Style-Tags, der von jeder Seite gerendert wird, unterschiedlich ist, werden möglicherweise viele CSS-Dateien generiert. Bitte berücksichtigen Sie dies, bevor Sie es aktivieren.
Klicken Sie hier, um die API-Dokumentation von MF anzuzeigen
Host-Seite
import { MF, SSR } from '@fmfe/genesis-core';
/**
* 创建一个 SSR 实例
*/
export const ssr = new SSR({
name: 'ssr-mf-home',
build: {
/**
* 使用了MF,这个值必须设置为false
*/
extractCSS: false
}
});
/**
* 创建MF实例
*/
export const mf = new MF(ssr, {
/**
* 共享依赖
*/
shared: {
/**
* 注意!!!
* Vue需要设置单例,否则页面会出现异常
*/
vue: {
singleton: true
},
'vue-router': {
singleton: true
},
'vue-meta': {
singleton: true
}
},
remotes: [
{
/**
* 服务名称
*/
name: 'ssr-mf-about',
/**
* 客户端的远程模块下载源,程序会自动拼接:http://localhost:3002/[服务名称]/node-exposes/[文件名]
*/
clientOrigin: 'http://localhost:3002',
/**
* 服务端的远程模块下载源,程序会自动拼接:http://localhost:3002/[服务名称]/node-exposes/[文件名]
*/
serverOrigin: 'http://localhost:3002'
}
]
});
复制代码
entfernt
import { MF, SSR } from '@fmfe/genesis-core';
/**
* 创建一个 SSR 实例
*/
export const ssr = new SSR({
name: 'ssr-mf-about',
build: {
/**
* 使用了MF,这个值必须设置为false
*/
extractCSS: false
}
});
/**
* 创建MF实例
*/
export const mf = new MF(ssr, {
/**
* 配置当前模块导出的文件,建议导出的命名和原模块的命名一致,这样就能和TS类型生成的文件名一致,获得更好的类型支持
*/
exposes: {
'./src/vue-use': './src/vue-use.ts',
'./src/common-header.vue': './src/common-header.vue'
},
/**
* 共享依赖
*/
shared: {
/**
* 注意!!!
* Vue需要设置单例,否则页面会出现异常
*/
vue: {
singleton: true
},
'vue-router': {
singleton: true
},
'vue-meta': {
singleton: true
}
},
/**
* 读取本地生成的类型文件,生成给其它的远程模块调用
*/
typesDir: path.resolve('./types')
});
复制代码
Knotenseitiges module federation
Implementierungsprinzip
module federation
在纯粹的CSR
项目中比较容易实现,但是在SSR
项目中需要在服务端运行一个Node程序,目前Webpack
对此并没有一个好的解决方案,所以Genesis自己实现了Node端module federation
下载和执行过程
编译阶段
如果你的项目导出了远程模块,在编译阶段client
的文件夹中会多出一个node-exposes
文件夹,让我们来看看这几个文件的作用都是什么吧。
-
manifest.json
这是一个清单文件,告诉了当前模块导出的基本信息{ "c": "9fed5146", "s": "e608c015", "d": 1, "t": 1645870356360 } 复制代码
c
是指客户端module federation
入口文件的版本号s
是指服务端module federation
入口文件的版本号d
是用来判断当前服务是否生成了dts,1是生成t
是当前构建完成的时间戳,如果本地已经下载过远程模块,MF
发送请求的时候会把这个t
的参数带过去,通过比较两个不同的t
值来判断是否发布了新的版本
-
e608c015.zip
这是将构建出来的server
目录下的全部内容,打包成的一个zip
文件,放到client/node-exposes
的目录中,方便其它的服务请求下载运行 -
e608c015-dts.zip
如果你给MF
插件指定了类型文件生成的目录,插件便会生成一个zip
文件,这样其它服务端在开发阶段,程序会下载e608c015-dts.zip
这个文件,并且解压到node_modules
目录中,就能得到完整的TS类型支持
运行阶段
在程序运行的时候,MF
就会去下载远程模块的代码,你会在控制台看到类似这样的一个日志
远程模块下载完成后,会解压zip
文件,就能看到上图Hot update
的字样,程序会热重载最新的代码执行。整个SSR
渲染的程序,会放在一个Node VM
中运行,能够有效的解决服务热重载时的内存泄漏问题。当然了,如果像定时器这种,如果不清理,还是会发生内存泄漏的,因为在ssr.sandboxGlobal
注入了全局的定时器相关的函数。
轮询阶段
在服务运行的过程中,如果远程服务发布了新的版本,这个时候我们就需要热更新了,MF
提供了轮询的方法,所以在host端
代码看起来像是下面这样的
/**
* 拿到渲染器后,启动应用程序
*/
export const startApp = (renderer: Renderer) => {
/**
* 初始化远程模块
*/
mf.remote.init(renderer);
/**
* 轮询远程模块
*/
mf.remote.polling();
/**
* 使用默认渲染中间件进行渲染,你也可以调用更加底层的 renderer.renderJson 和 renderer.renderHtml 来实现渲染
*/
app.use(renderer.renderMiddleware);
/**
* 监听端口
*/
app.listen(3001, () => console.log(`http://localhost:3001`));
};
复制代码
为了保证模块的及时性,MF
默认的轮询间隔时间为1000ms
,这个轮询的时间太频繁了,所以我们也可以在remote
端适当的延长响应的时间,避免请求过于频繁
/**
* 重写 manifest.json 的响应逻辑,注意要在静态服务的请求之前添加处理函数
* 如果1分钟内有更新,则立即往下执行,响应请求
* 如果1分钟内没有更新,则再结束请求,避免对方太频繁轮询
*/
app.get(mf.manifestRoutePath, async (req, res, next) => {
// host端传过来的编译时间
const t = Number(req.query.t);
// 最大等待时间
const maxAwait = 1000 * 60;
// 尝试等待manifest.json新的文件
await mf.exposes.getManifest(t, maxAwait);
// 继续往下执行,读取真实的静态资源文件
next();
});
复制代码
如果你不喜欢使用轮询的方式,可以自己编写接口,等其它服务发布完成后,手动触发服务的热更新接口,并且返回状态判断是否更新成功
app.get('/hot-reload/:name', async (req, res, next) => {
const result = await mf.remote.fetch(req.params.name);
res.send({ ok: !result.includes(false) });
});
复制代码
安装依赖时,自动下载远程模块的类型文件
在项目目录创建postinstall.ts
,调用mf.remote.fetch
方法拉取远程模块和类型
import { mf } from './genesis';
/**
* 下载远程模块,如果远程模块有类型文件提供,则会一并下载
*/
mf.remote.fetch();
复制代码
为了在依赖安装完成后,能执行这个代码,我们还需要在package.json
配置执行的命令
{
"scripts": {
"postinstall": "genesis-try-ts-node --project=./tsconfig.node.json postinstall.ts"
}
}
复制代码
小知识
:
为什么是使用genesis-try-ts-node
命令而不是genesis-ts-node
命令,是因为安装生产依赖时,@fmfe/genesis-compiler
不会被安装,genesis-ts-node
也就不存在,为了避免报错,所以又包装了一个genesis-try-ts-node
命令来防止安装生产依赖时报错
最后
到这里,我们已经完整实现了MF
的全部功能,如果你觉得还有疑问的地方,欢迎在issues中讨论