single-spa
在single-spa的角度,微前端就是"微模块加载器",它主要解决的是:如何实现前端的"微服务化",从而让应用、组件、逻辑都成为可共享的微服务,这从single-spa关于微前端的概述中可以看出:
乾坤
在乾坤的角度,微前端就是"微应用加载器",它主要解决的是:如何安全、快速的把多个分散的项目集中起来的问题,这从乾坤自身提点便可看出:
主要区别:
乾坤: 基于single-spa,加强了微应用集成能力,却抛弃了微模块的能力(微应用加载器:"微"的粒度是应用,也就是HTML(或main.js),它只能做到应用级别的分享)。
single-spa: 学习后端的微服务,实现前端的微服务化,让应用、组件以及逻辑都成为可共享的微服务,实现真正意义上的微前端,微模块加载器:"微"的粒度是模块,也就是JS模块,它能做到模块级别的分享。
在当前的微前端架构中,通过了许多技术手段如应用分离、JS沙箱、CSS隔离、预加载等实现了整个架构的使用。但对于公共依赖加载目前并没有非常好的解决方案。
举个比较现实的例子:你有2个antd3的项目,2个antd4的项目,还有一个宿主项目,如果你使用传统的微前端框架,你将只有一下两种选择:
- 宿主项目写
- 宿主项目写
webpack - Module Federation
module federation 模块联邦 它可以让多个独立构建的应用之间,动态的调用彼此的模块。这种运行机制,可以让我们轻松的拆分应用,真正做到跨应用的模块共享。
- entry 入口文件
module.exports = {
entry: './src/main.ts'
}
2. output(出口)
module.exports = {
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
}
}
3. loader webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。webpack 最出色的功能之一就是,除了引入 JavaScript,还可以通过 loader 或内置的 Asset Modules 引入任何其他类型的文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
module.exports = {
module: {
rules: [ { test: /.css$/i, use: ['style-loader', 'css-loader'], }, ],
},
}
4. plugins(插件)loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
module.exports = {
plugins: [ new HtmlWebpackPlugin({ title: '项目名称', }), ]
}
5. mode(模式)通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。
module.exports = {
mode: 'development'
}
6. webpack基本配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'none',
entry: './src/main.js',
output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), },
module: { rules: [ { test: /.css$/i, use: ['style-loader', 'css-loader'], }, ], },
plugins: [ new HtmlWebpackPlugin({ title: '项目名称' }) ],
};
7. 配置模块联邦
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
entry: './index.js',
output: {
filename: 'index.[hash].js'
},
mode: 'development',
plugins: [
new ModuleFederationPlugin({
name: 'projectA', // 项目B引用时的名字,很重要
filename: 'remoteA.js', // 暴露的模块统一打包在这个名字,可以任意取
exposes: {
'./index': './index.js', // 暴露的组件
}
})
]
};
优点:
- 使用npm包的形式管理库有时候会比较头疼,因为每次库更新,使用者都要重新走一遍:更新库 -> 项目重新编译 -> 项目重新发布这样的流程,但是如果我们的库以微模块的形式发布,而使用者通过运行时加载模块的方式使用库,那么每次库更新,我们只需要刷新一下浏览器即可
- 依赖共享: 之前的绝大部分微应用集成方式,不管是iframe,还是乾坤, 都没法很好的解决依赖共享的问题,它们唯一能做的就是用打包工具的externals功能进行vendor级别的共享,但是对于微应用内部的组件、模块是无法共享的。如果你的多个应用使用了共同的组件、模块,要么每个微应用都copy一份,要么以npm包的形式维护,都非常麻烦。
除此之外,更致命的是externals功能的羸弱。externals本质上就是让打包工具跳过vendor的编译,而是在运行时使用通过