Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

精读《Webpack5 新特性 - 模块联邦》 #239

Closed
ascoders opened this issue Mar 19, 2020 · 21 comments
Closed

精读《Webpack5 新特性 - 模块联邦》 #239

ascoders opened this issue Mar 19, 2020 · 21 comments

Comments

@ascoders
Copy link
Owner

ascoders commented Mar 19, 2020

我们知道 Webpack 可以通过 DLL 或者 Externals 做代码共享时 Common Chunk,但不同应用和项目间这个任务就变得困难了,我们几乎无法在项目之间做到按需热插拔。

模块联合是 Webpack5 新内置的一个重要功能,可以让跨应用间真正做到模块共享,所以这周让我们通过 webpack-5-module-federation-a-game-changer-in-javascript-architecture 这篇文章了解什么是 “模块联合” 功能。


精读《Webpack5 新特性 - 模块联邦》

@ascoders ascoders changed the title 精读《Webpack5 新特性 - 模块联合》 精读《Webpack5 新特性 - 模块联邦》 Mar 21, 2020
@HOUCe
Copy link

HOUCe commented Mar 23, 2020

其实是为了支持微前端发展的解决方案。对于非微前端架构来看,看不出来有多大的意义。
也许我理解错了。。请教导我!

@ascoders
Copy link
Owner Author

@HOUCe 目前来看是应用于多项目的解决方案,不过等到项目体积达到需要拆分打包时就会用上,所以应该说对大型项目是有意义的,也许未来这种微前端方案会成为一种大项目解决方案,会不会流行起来要继续观察。

@lileilei
Copy link

lileilei commented Apr 8, 2020

使用webpack 里面的 webpack_require.e 方法我尝试通过另一个项目的manifest.json 对应关系去加载另一个子项目的某个chunk页面,并且实验成功! 这样用webpack 原有的加载方法统一加载,看似解决了微服务难题。但是当某个chunk页面出现runtime-babel 的垫片后,我放弃了。因为入口系统无法完美的把子系统的垫片文件路径一一对应关系。于是我只能通过iframe隐藏加载子系统,通过子系统加载chunk模块的能力返回的模块对象,最后获取所需react模块的view渲染到我主系统的某个div内,又成功,但是我耗费大量经理去处理子系统样式,主系统子系统的react-redux公用一个store必须使用同一个connect的问题,以及window对象在子系统不能随便用必须使用parent.window来解决,并且大量兼容是子系统本身运行不受影响。等等等等,我想这就是webpack 5 模块联邦出来意义所在吧!

@chentianyuan
Copy link

chentianyuan commented Jul 3, 2020

@ascoders
对这种模块引用形式有点疑问
1、UMD+ Externals的方式共享的模块,除了享受不到tree shaking的功能还有其他缺点吗?
2、相比于外链+Externals的形式,这种模块引入方式是不是变相加深了父子应用的耦合程度,并且开发人员需要维护父子应用的打包顺序,依赖关系等?

期待回复(*❦ω❦)

@luxuryXu
Copy link

拿官方demo来说,app1共享依赖了app2的Button组件,app1怎么保证加载到最新的Button呢,浏览器有缓存

@joyz0
Copy link

joyz0 commented Jan 4, 2021

@chentianyuan 我觉得模块联邦和external不是互相替代的关系,定位不同。模块联邦使不同的webpack5项目之间能够运行时动态分享内部模块。

@xiaolu289
Copy link

xiaolu289 commented Jan 12, 2021

@JusTable 感觉模块联邦会不会是给我们用来把大系统做拆分用的?参考后端的微服务,我们前端会不会也可以用这个特性把页面组件按照功能拆分,每个系统独立编译,这样当我们编译大系统的时候就可以利用集群来加速了(多个机器分别编译不同模块)?external感觉主要针对公共库,而模块联邦针对业务代码,感觉是这样么?

@PeterChen1997
Copy link

PeterChen1997 commented Jan 18, 2021

拿官方demo来说,app1共享依赖了app2的Button组件,app1怎么保证加载到最新的Button呢,浏览器有缓存

同问

我有一个想法,不知道能不能用。就是每次发布后都会手动更改提供的 js 文件名,每个依赖的项目都需要手动更新 remotes 里依赖的文件名,然后过程是增量的,之前的 js 文件不删除,不影响线上项目正在跑的历史项目

@XiaofengXie16
Copy link

已经有人写了个plugin来解决这个问题,module-federation/module-federation-examples#566

@failedUser
Copy link

我可以不可以理解为dllPlugin + monorepo的结合? 独立构建 + 组件分发 ? 即避免了npm/monorepo二次构建的问题也避免了micro-fe包版本冲突的问题。 听起来是个不错的东西。

@ascoders
Copy link
Owner Author

ascoders commented Jan 29, 2021

@failedUser 我感觉可以类比理解为远程 dll。范围不止 node_modules,也可以是源码部分当作 dll。

@zzyjiaxinmomo
Copy link

现在使用上看,是不是module federation不能支持external了。因为毕竟要分享公共依赖,像react,vue这些第三方库

@AboyL
Copy link

AboyL commented Jun 22, 2021

被共享的这些依赖可以实现tree sharking吗?自己在测试的过程中感觉,同样使用了一个ui库,在A中引用了remote的组件跟ui库的组件,最后体积并没有想象中的那么小,不如只使用ui库的组件

@XiaofengXie16
Copy link

XiaofengXie16 commented Jun 22, 2021 via email

@Brandon-Ln
Copy link

所以这个方案原生是没有解决单个 Host 项目更新部署后,其他项目如何更新 Host 依赖的问题是么(因为看起来产物丧失了指纹)?从这点上看是不是还没有完全解决 umd 包上传 CDN 做公有依赖的缺陷?

@XiaofengXie16
Copy link

XiaofengXie16 commented Oct 16, 2022

所以这个方案原生是没有解决单个 Host 项目更新部署后,其他项目如何更新 Host 依赖的问题是么(因为看起来产物丧失了指纹)?从这点上看是不是还没有完全解决 umd 包上传 CDN 做公有依赖的缺陷?

不知道我有没有真的理解你的问题但是,一般现在作者推荐做法是加一个timestamp. 就是比如这样,来刷新指纹。

my_remote:`app1@http://app1.remoteEntry.js?v={new Date()}`

@Brandon-Ln
Copy link

所以这个方案原生是没有解决单个 Host 项目更新部署后,其他项目如何更新 Host 依赖的问题是么(因为看起来产物丧失了指纹)?从这点上看是不是还没有完全解决 umd 包上传 CDN 做公有依赖的缺陷?

不知道我有没有真的理解你的问题但是,一般现在作者推荐做法是加一个timestamp. 就是比如这样,来刷新指纹。

my_remote:`app1@http://app1.remoteEntry.js?v={new Date()}`

感谢回复。但如果就您举例说采取时间戳的方式做指纹,那如果 expose 的 appA 发版导致了文件指纹变更,此时线上 consume 的 appB 如果不跟随着 appA一起发版不会引起线上代码找不到资源的问题吗(因为此时根据旧的指纹 remote 已经找不到了)?还是说我的理解有问题?

@XiaofengXie16
Copy link

XiaofengXie16 commented Oct 22, 2022

这里有一点要注意的是,这个是更新remoteEntry.js 这个meta file的指纹。这个指纹会在每次用户刷新页面都会改变(不会被cache),从而每次都会去获取最新的remoteEntry file. 然后remoteEntry 包含了真正的bundle 里面asset的信息(e.g: my-bundle-123.js - 这个指纹应该是当你有真正的A code change才会被改变),webpack 会根据这个信息获取文件,浏览器会决定是否读取cache 还是从服务器获取。

回到你的问题,用户加载了A 的 remoteEntry file(但是还没有根据remoteEntry里面的信息去fetch A的bundle ),然后A 更新了,然后你的用户去了A的route,或者某些prefetch 触发了,那时候浏览器只会根据旧版的remoteEntry去fetch 旧版的A的bundle,这时候会触发failure。

反之,如果你的A 更新了,就算B 没更新,但是因为那个timestamp 每次加载/刷新都会更新,所以不会造成failure。

不知道这个解释能不能帮到你。

@Brandon-Ln
Copy link

这里有一点要注意的是,这个是更新remoteEntry.js 这个meta file的指纹。这个指纹会在每次用户刷新页面都会改变(不会被cache),从而每次都会去获取最新的remoteEntry file. 然后remoteEntry 包含了真正的bundle 里面asset的信息(e.g: my-bundle-123.js - 这个指纹应该是当你有真正的A code change才会被改变),webpack 会根据这个信息获取文件,浏览器会决定是否读取cache 还是从服务器获取。

回到你的问题,用户加载了A 的 remoteEntry file(但是还没有根据remoteEntry里面的信息去fetch A的bundle ),然后A 更新了,然后你的用户去了A的route,或者某些prefetch 触发了,那时候浏览器只会根据旧版的remoteEntry去fetch 旧版的A的bundle,这时候会触发failure。

反之,如果你的A 更新了,就算B 没更新,但是因为那个timestamp 每次加载/刷新都会更新,所以不会造成failure。

不知道这个解释能不能帮到你。

了解了,您解释的很清楚,相当于是 consumer app 只用保证每次请求加载到的 remote entry 是最新的即可,因为其根据 remote entry 来找 expose 模块映射的具体地址,而 remote entry 本身是跟随 host app 发版更新的。

@XiaofengXie16
Copy link

是的,你总结的很好!

@Shijf
Copy link

Shijf commented Dec 11, 2022

拿官方demo来说,app1共享依赖了app2的Button组件,app1怎么保证加载到最新的Button呢,浏览器有缓存

主应用可以动态设置 public——path 设置对应的版本

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests