15

发布 UMI 3,插件化的企业级前端应用框架

 4 years ago
source link: https://github.com/sorrycc/blog/issues/92
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

YNN73ua.png!web

哈喽,好久不见!

Umi 2 发布已经是一年半之前的事了,在这段时间里,我们发现之前的架构正逐渐不能满足业务飞速发展的需要,于是我们重写了一遍 Umi。经过几个月的 “007 ” 研发,Umi 3 在今天正式和大家见面了,并调整 slogan 为“ 插件化的企业级前端应用框架 ”。

Umi 是什么?

有些朋友可能还不太了解 Umi。

Umi 是蚂蚁金服的底层前端框架,已直接或间接地服务了 3000+ 应用,包括 java、node、H5 无线、离线(Hybrid)应用、纯前端 assets 应用、CMS 应用等。他已经很好地服务了我们的内部用户,同时希望他也能服务好外部用户。

它包含以下特性:

  • :tada:可扩展 ,完整的生命周期,插件化,支持插件和插件集
  • :package:开箱即用 ,内置路由、构建、部署、测试等,仅需一个依赖即可上手
  • :tropical_fish:企业级 ,经蚂蚁内部 3000+ 项目以及阿里、优酷、网易、飞猪、口碑等公司项目的验证
  • :rocket:大量自研 ,微前端、组件打包、文档工具、请求库、hooks 库、数据流等
  • :palm_tree:完备路由 ,支持配置式路由和约定式路由,同时保持功能的完备性
  • :bullettrain_side:面向未来 ,一直在尝试新技术的探索,dll 提速、modern mode、webpack@5、自动化化 external、bundler less 等

Umi 3 改进了什么?

彻底重写的代码和文档,80%+ 的覆盖率,~100M 的尺寸

U3YbAzB.png!web

Umi 2 随着功能累加,Umi 越来越庞大。然后在 Umi 3 里,通过严格控制依赖包,以及统一依赖库,比如请求库只用 got,命令行参数解析库只用 yargs 等等,我们把尺寸控制在了 100M 左右。

IJBJ7fz.png!web

同时,我们在设计 Umi 3 的时候给予了更多分层,比如渲染器可以不是 react、打包工具也可以不是 webpack。为了和 webpack 解耦,连 Dev Server 都重新实现了一份。

官方插件、插件集和最佳实践

与 Umi 3 同步发布的还有我们为 React 应用准备的官方插件集 @umijs/preset-react ,他包含以下功能,

  • plugin-access ,权限管理
  • plugin-analytics ,统计管理
  • plugin-antd ,整合 antd UI 组件,新增一键切换暗色主题
  • plugin-crossorigin ,通常用于 JS 出错统计
  • plugin-dva ,整合 dva 数据流
  • plugin-initial-state ,初始化数据管理
  • plugin-layout ,配置启用 ant-design-pro 的布局
  • plugin-locale ,国际化能力
  • plugin-model ,基于 hooks 的简易数据流
  • plugin-request ,基于 umi-request 和 umi-hooks 的请求方案

不知大家是否记得之前在 《蚂蚁前端研发最佳实践》 聊到我们针对中台的一套 垂直领域框架 ?他就包含在里面。比如:

  • 建一个 models 文件夹,就拥有了数据流能力 ,如果你写的是 dva model,用 connect 使用,如果写的是 hooks 数据流,通过 useModel 使用
  • 建一个 locales 文件夹,就拥有了国际化能力 ,然后通过 useIntl 使用
  • 建一个 access 文件夹,就拥有了权限管理能力 ,然后通过 useAccess 使用
  • 在 app.ts 里导出 getInitialState 方法,就拥有了初始化数据管理能力 ,然后通过 useInitialState 使用
  • 等等

yeMNRb2.png!web

更智能

比如 CSS Modules 的自动识别 ,不用 .module.css 后缀,不会再配 disableCSSModulescssModulesWithAffix一个文件是否为 CSS Modules 由引用他的方式决定

// 是 css modules
import styles from './a.css';

// 不是 css modules
import './a.css';

比如 约定式路由会自动识别 pages 下的文件是否为路由文件 ,** dva 插件会自动识别 models 下的文件是否为 dva model **, mock 目录下的文件会自动识别是否为有效的 mock 文件 ,可以避免我们之前在里面写一个 utils 或者其他文件时还需要配 exclude 规则的麻烦。

比如 Socket Server 无需再配置 ,之前如果配合 egg 或其他服务端时需要配回 umi 的 dev server;比如 cssPublicPath 无需再配置 ,会使用基于 css 文件的相对路径。

比如 配置和 MOCK 依赖文件的 ES6 支持 ,不仅仅是入口文件支持 es6,依赖的文件也会在运行时做编译以支持 ES6 的语法,让大家更少踩坑。

Import “所有” from Umi

简单说就是 所有能力都从 umi 中 import 获取

import Link from 'umi/link';
import withRouter from 'umi/withRouter';
import dynamic from 'umi/dynamic';
...

↓ ↓ ↓

import { Link, withRouter, dynamic } from 'umi';

但如果仅仅是这样,早就不是什么新鲜事了。我们还支持 通过插件扩展 import from umi 的能力 ,所以大家同时会看到很多这样的用法,

import {
  // 国际化
  useIntl, FormattedMessage, ...

  // dva
  connect, useDispatch, ...

  // 权限
  useAccess,

  // 请求
  useRequest,

  // 简易数据流
  useModel,
  
  // ...
} from 'umi';

一个 import 语句搞定 “所有” 功能,同时有 TypeScript 提示支持 ,并且通过 tree-shaking 保证尺寸不会无限增大

目前有些例外,比如 antd、react 还不可以,请静待之后的版本迭代。

改进的约定式路由

主要有以下改进点,

动态路由之前用的 $ 在 Bash 下有其他含义,容易混淆,所以 改用 [] 表示动态路由 ,比如 [users]/[id].tsx 代表 /:users/:id ,这也是 next.js 目前用的方式。

支持 通过路由组件的导出扩展属性 。比如按照以下这么写,路由上会多一个 title 属性,

function HomePage() {
  return <h1>Home Page</h1>;
}

HomePage.title = 'Home Page';

export default HomePage;

自动 exclude 掉非 React Component 的路由文件,这在前面以及介绍过了。

全新的插件体系

qIbINji.png!web

插件体系是 Umi 最重要的基建,因为包括 Umi 内部实现也是全部由插件构成。

  • 支持 presets 和 plugins 分层 ,通过分层,可以更好地支持垂直域,因为把一些插件组合在一起就可以应对一个垂直域
  • 底层异步化 ,每一个 hook 的扩展都既可以写同步,也可以写异步,这大大增加了灵活性,也不会在出现 Umi 2 中一些带 async 后缀的接口了,比如 onBuildSuccessAsync,onStartAsync,applyPluginsAsync 等等
  • 支持调整插件和 hook 的执行顺序 ,执行层基于 webpack 的 tappable,所以可以很好地支持通过 before 和 stage 调整顺序
  • 支持描述启用方式 ,由于场景的复杂性,一种启用方式已经不能满足需求了,所以我们在内核里支持描述插件是什么条件下启用的,有挂载插件即启用、配置启用,以及通过函数自定义启用时机的

还有不少,比如支持禁用插件,插件可感知其他插件等等,还有 Umi 3 的运行态插件也做了不少改进,插件体系是个很有意思的话题,篇幅有限,后续写篇文章展开介绍下。

值得一提的是,Umi 3 的底层能力全部收敛在 @umijs/core ,如果大家感兴趣,可以用相同的插件体系、配置、utils、日志等快速构建其他框架。

node_modules 走 babel 编译

按目前社区的约定,大部分工具都是不编译 node_modules 的,因为 node_modules 都默认为 es5 格式,但是有些库不这么做,所以带来了一些问题,

  1. 压缩问题 ,uglifyjs 不识别 es6 语法,会报错
  2. 旧版本浏览器兼容问题 ,虽然上 terserjs 不报错了,但由于 es6 语法未编译,在只能跑 es5 的旧版本浏览器里运行时会报错

为此,我们还曾整理了 es5-imcompatible-versions 来区分哪些依赖库是 es6 的,但总会有漏网之鱼,并且每次都是出现了事后补充,也带来了不少的答疑量。

所以,要彻底地解决此问题,就需要让 node_modules 下也走 babel 编译。这会让整体编译速度变慢,但由于针对 node_modules 下的 babel 插件是定制的,所以也不会慢多少。

除了能彻底解决上述问题,还能带来更多可能性。比如基于按需打补丁方案,之前没有编译 node_modules,特性收集肯定是不全的,现在就可以纳入考虑范围了。

全面拥抱 TypeScript

Umi 3 是基于 TypeScript 重写的,很多类型定义通过打包就直接导出了。

然后,如果你编写插件,现在也有了完整的 TypeScript 提示;对于最终用户来说,如果你想在写配置时也有提示,可以通过 umi 的 defineConfig 方法来定义配置,

NVbuamb.png!web

其他

还有一些其他改进,

  • 大量依赖升级 ,基于全部升级到最新,[email protected],core-js@3,css-loader@2 等等
  • 默认无全局变量侵入 ,不再有 g_historyg_appg_plugin 等等,这对于微前端来说,更容易被接入
  • 自研的 DevServer ,更多可能性,比如之后某些配置项更改后只需重置 compiler 而无需重启 dev server,比如和 webpack 解绑后可接入更多构建工具
  • 一键接入 webpack 5 ,通过插件 @umijs/plugin-webpack-5 实现,可临时切换到 webpack 5 尝鲜,启用物理缓存后,二次启动快地飞起
  • 统一路径查起点 ,不管是 componentlayout 还是 wrappers ,全部从 pages 目录开始找起
  • ** 命令 umi inspect 更名为 umi webpack**,可 inspect webpack 项目配置
  • 新增 umi plugin 命令 ,可以对插件进行 list 等操作
  • 集中管理 babel 插件 ,通过 @umijs/babel-preset-umi 完成,包含所有常用 babel 插件,通过配置满足不同需求

One more thing! DUMI, 嘟米,React 生态的文档工具

显然,这个命名已经透露了它和 Umi 的关系。dumi(嘟米)是基于 Umi 打造、为组件开发场景而生的文档工具,用大实话讲, dumi 就是可以用来写文档、官网和组件库 Demo 的 Umi。

qqERvae.png!web

没错,dumi 基于 Umi 3 打造,Umi 3 的官网基于 dumi 搭建,Umi 生态中又新添一名成员 :P

但 dumi 从发芽到现在,也不过百日,仍然有许多成长的空间,而且我们未来还有许多的想法计划将组件开发这件事做得更深,例如将 dumi 的 Demo 生产端和 Umi UI 的资产消费链路打通、为移动端组件开发打造移动端模式等等,倘若我们志同道合, 真诚地邀请你和我们一起将 dumi 变得更好https://github.com/umijs/dumi

三分钟上手 Umi

手动创建文件,

# 创建目录
$ mkdir myapp && cd myapp

# 安装依赖
$ yarn add umi@next

# 创建页面
$ npx umi g page index --typescript --less

# 启动开发
$ npx umi dev

或者 通过脚手架快速上手

升级到 Umi 3

参考文档 《升级到 Umi 3》

反馈

钉钉群

2u2aU3m.png!web

微信群

vmqqUjb.png!web

最后

感谢所有参与贡献 umi 以及在项目中使用了 umi 的内外部同学。umi 需要大家的使用和贡献,很希望你能一起来完善他,我们 Github 见! :wave:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK