Federación de módulos de paquetes web
Creo que puedes encontrarlo aquí, lo que indica que Webpack module federation
ya tienes una cierta comprensión. Hay muchos tutoriales en la comunidad que te dicen cómo usarlo y qué hace. Si no sabes mucho al respecto, primero podrías echar un vistazo a la documentación oficial .
Genesis2.0 es actualmente el único marco soportado por Vue2 en términos de SSR Webpack module federation
¿Por qué? Es porque cuando Génesis 1.0, se propuso el concepto de un componente remoto, que permite que diferentes servicios llamen a páginas de otros servicios. Cuando vemos el concepto que propone Webpack module federation
, ya nos hemos puesto a pensar en la iteración de Génesis 2.0.
Hasta hoy, finalmente hemos completado todo el desarrollo de funciones, y el proyecto en la empresa comenzó a actualizarse, y los componentes remotos se modificaron para module federation
llamarlos de una nueva manera. En este proceso, solucionamos con éxito el module federation
problema de no admitir el tipo TS y podemos module federation
almacenar en caché todos los archivos de entrada.
Uso del complemento MF
MF es module federation
un acrónimo de un acrónimo, y un servicio puede ser un host
lado o un remote
lado.
Es necesario prestar especial atención al uso de MF
complementos, y ya no se admite la extracción de archivos CSS externos, ya que cuando host
se usa el terminal, es imposible conocer remote
la información de los archivos CSS externos extraídos por el terminal. . Para resolver este problema, renderer.renderHtml
se agrega un nuevo parámetro a la función styleTagExtractCSS
Cuando el valor es el valor true
, el contenido de la etiqueta de estilo representada por la página actual se extraerá en un archivo CSS separado. Debido a que el contenido de la etiqueta de estilo representada por cada página es diferente, es posible que se generen muchos archivos CSS. Considérelo antes de habilitarlo.
Haga clic aquí para ver la documentación de la API de MF
lado anfitrión
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'
}
]
});
复制代码
control remoto
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')
});
复制代码
module federation
Principio de implementación del lado del nodo
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中讨论