背景
随着我们的业务不断的发展,我们的前端工程也变得越来越大,能否管理好这么一个庞大的前端项目,决定了后期维护以及迭代的成本。相信很多接触过大型前端项目的同学应该也接触过不少解决方案了,比如mononrepo、微前端、微服务、多仓库git管理,这些在社区都有比较成熟的方法了。这里我们就不进行细细的套路,今天是要解决的是怎么在这样的多项目中进行代码复用。
锦
常见方案解决
复制粘贴
这是自己开发的时候最喜闻乐见的形式了,业务初期,起步快,业务呈现小步快跑的趋势,我就是直接cv一把梭,这种方式优点很明显,就一个字:快!但是缺点也很明显那就是代码大量的冗余,对于一个有代码洁癖的人是不能接受的(如果你让我写,那就另当别论)。同时随着后续业务的迭代,多个项目中的代码难以保持一致,如果中途换了人进行维护,那么口口相传的方式很容易就引起bug,当然如果本来就在两个项目中有差异化的开发,那么这样的复制粘贴是一种上添花的方式。
npm发包
将对应需要复用的代码进行抽离成npm包,然后在需要进行使用的项目中进行安装,这种方式相信大家肯定也不陌生,优点也很明显:代码不冗余,同步管理好。缺点也是不少,比如需要有专门的人来进行专门的包管理,每次进行简单的修改就要进行重新的发包流程,你和热跟新已经告别了,别以为这样的时间可以忽略不记,当你遇到一个反复修改的产品经历时候,你今天的时间算是就是栽在这里了。
workspace
如果你有接触过monorepo的话,那么你对这一种方法一定不会陌生,workspace工作空间,最开始是yarn提供的一种monorepo依赖管理机制,可以在项目的根目录管理多个项目的依赖,随着技术的发展,现在yarn workspace已经退出舞台了,现在新的项目用pnpm workspace,这种方法可以说是非常的好用了,优点很明显:代码不冗余,同步管理好,需要热更新的话我们只需要设置好package.json的main字段即可:
{
"main": "src/index.tsx"
}
这样我们每次组件进行更改的时候,对应使用项目也可以进行热更新了,当然如果你这个包需要进行发布的话,就只需要改变路径为构建产物可以说是相当好用了。
{
"main": "dist/index.tsx"
}
说完了优点,我们来说一下缺点:如果复用的代码不在同一个仓库的话你就需要进行发包处理走方法二的老路。
module federation
首先来简单介绍一下什么是module federation(这也是本文需要重点介绍的方法,毕竟总是要讲出一点新东西才能骗到大家的赞嘛),这是webpack5推出的新特性:"一个应用可以由多个独立的构建组成,这些独立的构建之间没有依赖关系,他们可以进行独立的开发部署,这就是通常被认为的微前端,但是又不仅限于此",可以看出mf要做的是和微前端做的类似的东西,我这里不是对mf的进行深入的介绍,而是对我在使用mf时候遇到的痛点的对应解决办法进行一个分享。
mf主要分为host和remote也就是我们的消费方和提供方,对应的消费方通过配置将需要暴露的部分进行默认暴露,消费方通过合适的配置就可以进行消费使用。这样讲有点抽象,我给个抽象的例子来抽象你们一下啊:
提供方
new ModuleFederationPlugin({
name: 'app2',
...
exposes: {
'./Hello': './src/Hello',
},
}
消费方
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
...
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'app1RemoteEntry.js',
remotes: {
'app2': 'app2@http://127.0.0.1:8002/app2RemoteEntry.js',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
})
]
}
经过上面的配置我们就可以在消费方使用提供方暴露的代码片段了像这样:
import React from 'react';
import App2Hello from 'app2/Hello';
const RootComponent = () => {
return (
<div>
<div>app1</div>
<App2Hello />
</div>
);
};
export default RootComponent;