本篇文章来自淘系中后台架构前端团队-思忠
前语
icejs 是淘系中后台项目组通过近三年的演进沉积出的一个依据 React 的研制结构,现在已在淘系、飞猪等许多内部中后台业务项目里被广泛运用。从初步的工程构建东西演进成研制结构,这背面有着怎样的进程,以及新的结构又给我们供应了怎样的才干,这篇文章将N O = 6 F 0会给我们进行同享。
一起 alibaba/ice 项目自 2018 年初步已经在 GitHub 进步行开源,涵盖了前端工程、数据流方案、微前端、研制结构等开源项目,持续得到了社区宽广的注重,并被国内5 f z x N 9 % U Y许多个人与公司在其间后台项目中选用, GitHub 的 star 数不到 2 年的时间里达到了 15000 多,成为这个在范畴增加最快的阿里开源项目,感谢全部在项目中运用 ICE 技术体系,以及参与贡献和反响的同学。也欢迎更多的同学注重和运用 ICE。
- 注重我们的 GitHubn e (:github.com/alib9 n Uaba/ice
- 注重我们的 知乎专栏:zhuanlan.zhihu.com/p/142412204
开发痛点
在最近的几年时间里O q A 0 O前端技术开展迭代速度非常之快,在言语A 6 # Z 2 C V U上从 ECMAScript 到 TypeScript;在工程上有 Webpack、Rollup、Babel 等技术;在前端结构有 React、 Vue、 Angh J ( y @ #ular 三驾马车;以及盘绕这些结构的周边生态的技术方案更是数不胜数,即使是专业前端也需求通过不断的学习去树立自己的技术体系,关于非专业的前端开发来说,需求做前端开发门槛可想而知。假设每次开发项目都要去考虑这些问题,那么这无疑是非常苦楚的。~ 4 T K 0 K
开发群体
在淘系业务中,除了专业的前端开发者外,还存在许多后端,测验,外包等非专业的前端开发者,有着许多的中后台业务诉求,面向这些开发者怎X [ 0 k样下降研制门槛,解决前端环境配备Y l 8 Z m凌乱,开发门槛高级问题,让开发者不只可以快速上手业务9 f : 开发,还能更简略的写出更好更易维护的代码是我们尽力追求的政策。
演进之路
依据上述的问题,这儿我们先来回2 m U S顾下为什么会有 icejs 研制结构,下面是一张演进b l b { 5 , B }的大图:
刀耕火种
中心:工程 ice-scripts 1.0
定位:webpack 通用才干封装,支撑部分配备、黑盒逻辑
时间回到 2018, 乃至更前期的时分,常常干的一件作业就是将 webf n & j G q W x +pah 9 ? $ v | Xck 的配备直接全撸在项目里,假设需求开发其他一个项目时,依据原有项目一把梭的删掉业务代码然后初步撸新的项i 0 D V目。对与个人项目这样做或许没问题,可是到了大型团队多项目协作时这明显不是一个好的办法,工程配备无法复用,重复的配备导致维护本钱极大,因而通常的做法e 2 h d ~ V ~ q就是将常用的配备进Q y (行封装,然后供应一些可选的配备支撑不同的项目配备,这就是 ice-scripts@1.x 的工程才干。
随着时间和业务诉求的改动,发现每个业务对定制的诉求纷歧,ice-scripts 只能不断的去支撑各种才干供应配备项,虽然满足了业务的诉求,但导致其间心的完结代码越来越臃肿,配备项也越来越多,运用本钱开 1 p } } / y端上升以及全体的_ I ? N E C 0 M稳定性和f u . x E O @ D 9可维护性下降。
工业化
中心:工程东8 G z X 1西 ice-s( E C ] * * l 8 rcripts 2.D n g o K #0
定位:强大的插件内核、丰盛的插件生态、支撑自定义任何配备
时间来到 2019,+ ` B ? 8 7依据 ice-scripM 8 ) 8 wts@1.x 的布景原因,以及在阿里内部许多团队之间也遇到了相同的问题,每个团队都抽象沉积了一套团队基础的工程东西,但是确都很难同享到其他团队。因而在某种布景的推动下,初步对 ice-scripts@1.x 进行升级以满足更多的N r j N C团l 5 1 D – w J队和业务场4 P ~景的诉求,下降重复的才干建造和人力投入,供应一套共同的工* @ K ) 9 + p ^程方案。因而在阿里内部这套方案被重新命名为 buil6 a # )d-scripts 、而 ICE 对外开源的为 ice-scripts@2.x 版别,实质上是同一套方案, : e j R I仅仅命名的差异。其间心的才干包括_ { l : { A G C:
- 支撑多构建任务机制
- 完善活络的插件才干
- 丰盛的插件生态
- 活络的更改 webpack 配备
因; = 5 # ^此在这一阶段,ice-scripts@2.x 在工程层0 ? w ;已经做得非常好了,支撑了o k { A ? n m淘系以及集团其他许多 BU 的业务开发。
现& ^ _ , ) @ } 1代化
中心:研制结构 1.0
定位:最佳实践 + 运转时扩展 + 插件体系 = 规范化的m . C 5 v H z { &研制方式
时间来到 2020,虽然 build-scripts 已G h e H d r .经很好的解决了项目开发B B ; : d } R的工程问题_ v $ N ? m,但是它或许只占了项目开发的 20%,更多的 80% 则是在业务开发。在实践中我们发现,在淘系内部,不同的团队乃至有时在同一个团队,其项目选用的技术选型和项目开发规范` q ;也是不尽其同的,比如在情况处理方案,有的用 Redux、有的用 M{ @ $ [ ~ jobX 等等比如此的差异问题。
太多选择了…
- redux
- mobx
- immer
- rematch
- icestore
- …
- 1F V w % P 3 00+
没有约好的团队,交流和跨团队协作本钱,以及长期的维护本钱是非常高的,这时分共同一套开发方式就显得尤为重要。而 icejH G W Z p %s 正是在这种布景下诞生的,通过结构强捆绑和供应无缺的规范化的 R. $ |eactq B _ = { 运用开发方式和x 4 F D i x 1最佳实践。旨在使前端开发z U W 1 l d T愈加的方便、高效,以及收敛技术栈、屏蔽底层差异和共同开发领会,协助开发团队和开发人员下降开发和保y S m护N K a P + y A A本钱。
因而) o K在这一阶段,中心从工程体系的脚手架演进成无缺的规范化的 React 研制结构,在工程插件的基础之上支撑并扩展了运转时的才干。通过运转时的才干我们可以非常简略的将业务开发中的最佳实践方案通过结构的插件机制进行完结和强捆绑,并预设一套的默许的最佳实践的插件集。
下面e ] F 9是演进的差异比照图:
纬度版别 | icejs 1.x | ice-scripts 2.x^ L & # 4 / 7 | ice-scripts 1.x |
定位 | 研制结构D _ q ? ] i | 构建东西 | 构建东西` U R 2 0 e |
配备文件 | build.json | ice.config.js? V + r ` c | packd s { H Gage.json(buildConfig) |
发布时间 | 2020.02 | 2019.06 | 2018.02 |
可渐进升级性 | 好 | 欠好 | 欠好 |
插件才干 | 工程+运转时 | 工程 | 无 |
工程配备 | 强 | 强 | 弱 |
运转时配备 | 默许支撑 |
– |
– |
SPA |
默T ` r ^ I 4 n q许支撑 |
支撑 |
支撑 |
MPA |
一键打开 |
– |
– |
SSR |
一键打开 |
– |
– |
微前端 |
一键打开 |
– |
– |
Serverless 一体化 |
插件支撑 |
– |
– |
结构供应了哪些才干
一张大图
下面是一张 icejs 结构的大图,共分为四层从上到下依次是 基础设施
-> 研制规范
-> 功用领会
-> 研制方式
。
基础设施
基础设施首要包括依托的社区方案如 UI 结构 React 、路由库H ; j c b react-routeN B hr 、构建东西 webpack、测验东西 jest 等X V C J ; ^,以及依据 webpack 封装的上层工程构建东西 build-scripts 等。这一层首要为 icejs 的基础依托,一起我们依据这些基础依托做了必定程度上的封装,让业务不需求关心底层的细节以及版别变更等,运用 icejs 时即可开箱即用。
研制规范:
研制规范首要包括通过结构供应完! $ W T Q p好的规范化的 React 运用开发最佳实践,其间首要分为+ W Q ~ w H b:
- 规范化的研制流程:b / R v C d $ n N规划和捆绑了项目开发无缺流程,包括基础规范的目录结构、款式方案、代码规范n = ^ 1,路由规范、情况处理、以及大部分场景的业务方案等,毕竟构成无缺的规范化的一套最佳实践方案;
- 插件生态:供应插件机制[ – C和常用插件,D r C其才干依据工程构建东西 builb I u V 8 G ;d-scripts 封装,因而在插件才干t b )上也无缺继承G z 3 t J ` 3了 build-scrtips。除了通过插件定制工程才干以外,结构还为插件扩展了运转时定制的才干,这让插件拥有更多的想象空间;
- 自研体系:首要包括面向大型运用的微前端解决方案,简略友t q b爱的情况处理方案,场 0 g / f w 5景复用的 Hooks 东西库,这部分才干会作为基础才干集成在结构中,只需简略配备即可按需打开。
功3 L R $ n ~ [ v用领会
功用领会是衡量一个结构的重要目标,对结构而言。领会可以分为开发者领会和用户领会。其间开发者领会首要包括构建速度,差错调试,智能提示、开发文档等。对用户而言,则首要包括首屏烘托速度,预加载才干等,现在结构已支撑 SSR 、Code Splitting 用于优化功用。
业务场景
不同的业务场景又不同的技术诉求,结构已支撑 SPA、w w n S 7 r Z *MPA、{ ] VSSR、微前端、Serverless 一体化(仅c G K p ; u j a %限内部)等多种不同的业务场景。
配套模板
除了上述q G A U _ a才干,我们还为结构供应了高质量的不同类型] N q e的的配备模板,在实践生产中m h d 5 6 4 U可以依据业务情况进行运用。
更轻量的运用进口
结构只需求通过调用 createApp
即可发起一个包括路由、情况处理、数据央求等功用的 React 运用,一起在创建运用时你可以依据实践的业务需求进行自定义配备。比较传统的 React 运用开发而言,无需在手动去调用 ReactDOM.render()
、去创建路由 createHistory()
等繁琐而x W % 1 L重复的运用进口的配备~ a D 7 $ `。
import { createApp, IAppCe p T + r Eonfig } from 'ice';
// 运用配备
const appConfig: IAppConfig = {
// 发起项配备
app: {},
// 路由配备
router: { },
// 情况办 P + V 2 U理配备
store: { },
// 数据央求配备
request: {},
// 日志配备
logger: {}
};
create$ x z 7 f Q z {App(appConfig);
更活络的导出办法
在 React 项目开发中,比如 react-router 等第三方| ^ q ?依托是每个项目开发中都会重复出现的,因而在 icejs 中也进一步对这些依托进行了收敛,减少项目的依托项和版其他处理问题。一起还结构支撑了_ . B ! i k通过插件往 ice
包里边注册新的 API,这意味着你可以编写某个插件将业务的通用才干集成到结构之中,在多个项目之间都可运用,8 9 X a I } t ~ b且对业务开发者无感,这是a E T t b值得令人兴奋的。
import {
//插件扩展的接口
store,
request,
loggger,
config,
helpers,
...
// 内置组件
Link,3 ] g :
NavLink,
ErrorBoundary
...
// 内置 Hooks
usef t & r r 6 B iHistp Z _ Q S sory
useSearchParams
...
// etc
} from 'ice';
更简略的路由配备
路由是运用开发中必不C ^ _ } d可e $ 3 O )少一部分。从社区看 next.js、 umijs 等结构都在推重运用约好式路由,但通过实践后我们发现约好3 B } F式路由在一些简略的运用场景下是比较适宜的,依据目录自动生成路由可以减少路由配备的作业,但当运用凌乱的时分,需求= _ ^增加各种标识和目录结构来判断,如嵌套路由、动态参数运用 $
等,这时目录结构会变的很不优雅,对开发者来讲需求遵照约好式路由带来的新的规矩和学习本钱,因而在 iA Y m F D 5 / |cejs 结构规划中以及配套的模板里默许举荐运用配备式路由,但一起也支撑了无缺的约好式路由才干,这是* / X Z ) A可选的。
- 配备式路由(举荐):规范路由协议,了解直观易懂
co3 ? W T p $ 0 wnst routerConfig = [
// 分组路由,children 里的路由会将父节点的 component 作为布局组件
{
path: '/user',
component: UserLayout,
children: [
{
path: '/login',
exact: true,
component: UserLogin,
//j ~ b 配备路由的高阶组件
wrappers: [wrapperPage]
},
{
path: '/',
// 重定向
redirect: '/user/login',
},
{
// 404 没有匹配到的路由
component: NotFound,
},
],
},
// 非分组路由
{
path: '/about',
component: About,
},
];
- 配备式路由(可选):约好目录规范,无需配备自定生成路由。
更智能的情况处理
情况处理是运用开发和技术选型最重要的一环,从 React 生态来看,现在首要的情况处理方案大致可以分为三类,照应式情况处理方案类 MobX 系列、单向数据流类 Redux 系列、以及在 React Hooks 之后的依据 Hooks 系列,而在这些之上衍生的方案估约不下 100+,假设你有选择困难症,那么你可以选择( I Y h n # V运用 icejs,由于你不再需求关心目不暇接的情况库、概念繁复的 API、重复的模板代码等问题。
在做情况处理方案的时分,我们中心关心的是什么,这儿首要抽象四个维度:
- 怎样去定义一个模型
- 视图怎样跟模型绑定
- 视J ^ ?图怎样去消费模型
- 模型之间怎样取联动
在进一步抽象,其实实质就是 UI 视图 和 DATA 模型怎样交互,依据此p | d A @ 4 { w考虑在 icejs 中内置集成了自研的 icestore 情况处理方案,并在此基础进步一步遵照 “约好优于配备” 准则,进行抽象和封装,使得情况处理变得非常简略。
- 你只需求 定义模型 和 视图消费 即 R = . 6 |可,, R 4 G ; } r其他模型与视图绑定的作业v E h由结构去做。
- 你只需求 写更少且更优雅 的代码即可,其他重复的模板作业由结构去做。
模型定义
export default {
st! ~ f h L cate: 0,
reducers: {
increment:(prevStC H u !ate) => prevState + 1,
decrement:(prevState) =T y @ P i Y > prevState - 1,
},
effects: () => ({
async asyncDecreme| w o S ] F F Lnt() {
await delay(1000);
this.decrement();
},
})
};
视图消费
import { store } from 'ice';
function Counter() {
// model 名称即文件名称,如 src/models/counter.ts -} K # , { r ) i q> counter
coB d % V Rnst [ count, dispatch| , K . m `ers ] = useModel('counter');
const { increment, asyncDecrement } = dispatchers;
return (
<div>
&f y 4lt;span>{count}</z k b ; 9 v hspan>
<button type="button" onClick={increment}>+k d K { Z d E T</K : 1 R 6button>
&# & F * ! nlt;button type="button" onClick={asyncDecrement}>-</button&g~ v K E & M d {t;
</div>
);
}
更和睦G [ K : K / 4 V ~的工程配备
结构供应了绝大部分的工程配备项, 值得一提的是只需求运用 JSON 格式配备工程即可,比较 react-scripts 以及社区其他的工程方案,JSON 配备能{ } T s够极大的简化工程配备的凌乱度,也愈加简略。
{
"alias": {
"@": "src"
},
"out4 & A s g Q 0 |putDir": "dist",
"publicPath": "./",
"sourceMap": true,
"minify": false,
"proxy": {
"/**": {
"enable": true,
"6 @ { a { # O qtarget": "http://1H a T , &27.0.0.1:6001"
}
}
}
更简练的微前端接入
依据 icestark 微前端方案和 icejs 的插件机制,我们封装了 build-plugin-i: n ^ 6 r 5 *cestark
插件,通过该插件可以大大下降接入微p ? : ; & 7 A V前端方案的本钱。只需简练的配备即可接入。
- 配备结构运用
imC H )port { createApp } from 'ice'
const appConfig = {
icestark: {
+ type: 'fra! v Z /mework',
+ getApps: async () => {
+
+ },
+ appRouter: {
+
+ },
},
};
createApp(appConfig)
- 配备微运用
importg 7 3 i ? C a N { createApp } from 'ice'
const appConfig = {
icestark: {
+ type: 'child',
},
};
createApp(appConfig)
更多其他的才干
除了上述才干之外,结构还抽象供应以下常用的功用,详细请查看文档:
- 规范化的数据~ R i ` # a央求方案
- 通用场景的页面配备才干
- 简略的的环境构建/运转时配备
- 和睦的代码分割、差错鸿沟处理才干
- 支撑 SPA、MPA、SSR、微前端、Serverless6 ) t 等运用类型
结构是怎样规划的
结构架构
结构的中心架构首要包括 运转时 和 工8 E } ` % d p程才干 两部分:其间运y Y 6 Y 8 T v l行时部分首要供应扩展运转时才干,在 icejs 中称之为运转时模块,通过模块供应的 API 可以非常简略的将通用逻辑插件化,以便在不同的项目间进行抽象和复用, 如我们可以通过 AddProvider
在运用最顶层增加自定义的 ProviJ p r ,de2 i z b o a O 5 !r 才干,通过 wrapperRouter
为每个路由包装一个组件等等。而工程部分则首要供应插件扩展和生成器的职位。
插件机制
icejs 依据工程构建东西 build-scripts 封装,因而在插件才干上也无缺继承了 build-scrtips,插件供应了丰盛的 API:
- registerTask:注册多 webpack 任务
- registerUserConfig:注册
build.json
中的顶层配备字段 - registerCliOption:注册各指令上支撑的 cli 参数,比如 npm start –https 来打开 https
- onGetWebpackConfig:获取内置的配备,并对配备进行自定义批改
- onHook:通过 onHook 监听指令运转时工作,onHook 注册r j g d F的函数履行完结后才会履行后续操作,可以用于在指令运转半途刺进插件想做的操作
- …
官方插件
icejs 的中心完结就是一) D M g 5 @ g套官方的插件集,包括以下功B # B V #用:
- plugin-react-app:供应 React 运用开发的 webpack 配备
- plugin-core:供应结构的运转时才干和 API
- plugin-router:供应配备式和约好式路由的功用
- plugi, j & $ & M vn-store:供应情况处理的功用
- plugin-request :供应数据央求的功用
- plugin-config:供应环境配备的功用
- plugin-logger:供应/ F Q t @ 日志功用的功用
- plugin-helpers :供应协助函数的功用
- pg t W v : # G ?lugin-service:供应接口服务的功用
- pl3 j { p * Rugink J G M ` N | G-ssr:供应 SSR 的功用
- plugin-icestark:供应微前端的功d V /用
- plugin-mpa:供应 MPA的功用
插件开发
插件机制是 icejs 的中心之一,当时 icejs 的基础才干都是通过插件来完结。插件机制不光可以保证结构中心满足精简和稳定,还可以通过插件对运转时和编译时的才干进行封装复用,毕竟打造一个无缺的生态。
- 自定义扩展运转时才干:
// module.ts
export default ({ addProvidef V m -r, appConfig,^ . z . N / % % z ...rest }) => {
const StoreProvider = ({children}) => {
return (
<AppStore.Provid_ & A 2 [ Ver initialStates={appConQ l Z Nfig.initialStates}>
{children}
<& ) S 1 } k /;/; 4 J &AppStore.Provider>
);
};
// 通过 AddProvider 在运用顶层增加 Provider
addProvider(Store? k t $ / _ |Provider);
};
- 自定义扩展工程才干:通过插件供应的 API,可以便利拓展和自定义工程才干,该办法会接纳两个参数,第一个参数是插件供N / + , ~ Y给的 API 接口,举荐按照解构办法运用,第二个参数
options
是插件自定义的参数,由插件开发者决议供应哪些选项给用户配备。
// index.ts
export default async (api, options) => {
const { c^ R ( P ~ S A {ontext, onHook, onGetWeb, K Z 8 r N BpackConfig } = api;
// 通过 onGetWebpackConfig 获取 webpG s +ack-chain 方式的配备,并对[ v l - d 8配备进行自定义批改
onGe^ % 9 M _ V : (tWebpackConfig(config => {
})
// 通过 onHook 监听指令运转时工作,可以用于在指令运转半途刺进插件想做的操作
onHook('before.start.run', asy5 / - v | %nc () => {
})
}
下一步
icejs 结构现在已经在淘系、飞猪等许多业务中通过许多项目的实践,我们也将逐步向社区推行。一起在技术圈有一句话是:没有一个技术方案M , R Y D j 6 n是完美能掩盖到全部场景无瑕疵的,从 ice-scripts@1.x
-> ice-scrib * N d w A /pts@m 4 7 ? )2.x
->[ [ E; icejs
研制结构通过了三个大的版别改动,但这些版别改动都是结合淘系的业务实践以及用户诉求不断演进和探究的,在才干d & W _ A y 5和规范性上8 n G b @ ] ) q h也都在不断提高。接下来我们将首要从才干齐备、结构功用、开G w J f 0 x发领会三个方向进行优化和探究。
❤️ 最终
再次感谢全部在项目中运用 ICE 技术体系,{ * 0 E Z v U m以及参与贡献和反响的同学,是你们让 ICE 一直在不断的成长和完善。期望在未来依然能和和我们一起前行,一起让前端开发变得更简略更和睦。
假设觉得对本文对你全部协助,欢迎 star 或许点击我们的官O j P 5 ( L 0网了解更多,附上相关链接如下:
- GitHub:github.com/a3 1 D : [ $ + ulibaba/ice% ? ; 4
- 官网地址:ice.work/docs/guide/…
- 反响群:img.y N L # y 4 } J Xalicdn.com/tfs/TB1upVN…
最终的最终,假设你对飞冰体系的技术产品有爱1 S ! G h A d G &好,希L 2 T ~望参与我们一起搞作业,无妨钉钉扫一下下方的二维码,一起聊聊。