umi as a Compiler (Full Version)
大纲
- 以 《Compilers are the new frameworks》 文章为切入点,解释 umi 的理论基础
- Demo 演示,给大家更直观的印象
- 约定优于配置
- umi 从源码到产物,解释相比传统的 webpack/roadhog 有啥区别
- 关于路由,umi 为什么要接管路由,以及接管了能做啥
- 不能满足?用插件爆改 umi !
- 关于部署,umi 如何和流程对接
- 还有时间的话,可以讲讲 duck dictory 的 dva 项目、PWA、umi-test、内置的性能优化、各种配置、未来规划、其他开发体验等等
内容
Compilers are the new frameworks

不知大家有没有看过这篇文章,《Compilers are the new frameworks》,作者认为,“Web 框架正在从运行库转变为优化编译器”,我深表认同,最近新出的框架,比如 next.js、umijs、after.js、pri 等,都是这个思路的贯彻者。
之前,工具是编译时的,框架是运行时的,两者互不强依赖,相互独立。但是,我们发现,把两者结合起来会让框架更强大,对使用者也更友好。比如,我在 pages 目录下建立 404.js 的文件,然后他就变成了整个项目的 fallback 路由,这在工具和框架分离的情况下是很难做到的。
为了让大家有更直观的印象,我们来看一个 demo 演示。
Demo

copy & paste
- pages
- antd
- 404
- layouts
- mock
- test
- 按需加载 & 按需编译
不知大家有没有发现,Demo 里有大量基于约定的编码方式,这是如何实现的呢?
约定优于配置
设计不好的框架通常需要多个配置文件,每一个都有许多设置。 – 不知道是谁说的
尽管 umi 仍然有不少配置,但在文件组织上,我们尽量选择约定的方式。"约定优于配置"是 umi 遵循的另一设计规则,我最早是在 Ruby on Rails 里感受到的,他能让框架在被使用时少做决定,但仍不失扩展性。
比如:
- layouts/index.js,全局路由
- global.js,写 polyfill 的地方
- global.css,写全局样式,覆盖 antd 等
- .global.css(计划)
- hd.js,自定义的高清方案
- document.ejs,定制 HTML 默认
- 404.js,404 路由
- mock/,基于 express 的 mock
- .test.js,测试用例
- loading.js(计划)
- _routes.json,配置路由
- _layout.js,嵌套路由
- page.js,目录路由
- …
从源码到产物

这是使用 roadhog/webpack 的流程,没啥好讲的,大家都很熟悉了。

这是 umi 的流程,
- 首先,umi 是基于路由的,所以需要有一份路由配置,路由配置哪里来?可以是基于约定的,也可以是基于配置的,看个人喜好,推荐约定式的
- 然后,路由配置会用于生成路由文件,在这里 umi 做了很多复杂的事情,比如开发时按需编译、运行时按需加载、各种性能优化等等,这个路由的部分后面会展开讲下
- 然后,把入口文件交给 webpack 做打包
- 同时,会处理 HTML 的生成(可选)
- 最后是部署,和各种流程对接
举个具体的例子。
文件目录:
这会生成路由配置,
然后生成路由文件,
从例子中可以看到,umi 完全接管了路由的部分工作,那么为什么要接管路由,以及接管了能做啥呢?
为什么要接管路由?
不管是单页应用还是多页应用,都是以路由为维度组织的。单页应用里是路由,多页应用里的页面其实也可以理解为路由。路由决定渲染时显示什么,跳转时用什么方式,切换时用什么动画等等。
所以,接管路由之后,框架就可以:
- 开发时按需编译
- 运行时按需加载,且可一键关闭
- 根据路由数进行智能的 common 提取 (阈值:pages.length/2)
- 根据路由生成静态页面
- preload
- 接管页面跳转方式,比如在容器里用 bridgex
- 定制路由切换动画
- 定制路由切换的 scroll 行为
- 定制 layout
- 定制权限路由
- SPA 应用的埋点
- 路由代码可选
- SSR 时在服务器处理数据请求(未实现)
- …
这个列表我每列一次都会增加不少,说明这里其实还有很大的想象空间。
路由约定
- 基于配置 vs. 基于约定
- layout
- 嵌套路由
- 目录路由
- umi-plugin-routes
之前一直不敢推,是因为不支持嵌套路由,在 umi@1.1 支持了之后,大家在 PC 等路由复杂的场景下也可以放心用了。
插件机制

不能满足?用插件爆改 umi !

再看回这张图,umi 整体梳理过运行流程的每个环节,并且都预留了埋点,甚至 umi 内部的实现也是由插件组成,这点有些类似 webpack,api 接口设计参考了 vue-cli@3,这点之前 @皓默 也有分享过。
比如,每个环节都提供修改的接口:
- modifyRoutes
- modifyRouteComponent
- modifyHTML
- modifyWebpackConfig
- modifyEntryFile
- modifyRouterFile
更多参考 umijs/umi#87
内置的插件:
- hd
- service-worker
- layout
- 404
- mock
- …
umi-plugin-ali 包含的插件:
- basement
- xmas-fastclick
- spm-bacon
还有些其他的:
- umi-plugin-routes
- umi-plugin-dva
- umi-plugin-dll
- umi-plugin-yunfengdie
关于部署

umi 如何和流程对接?
灵活运用 publicPath 和 router base 就可以支持各种应用了,详见 https://umijs.org/docs/zh-Hans/deploy.html
目前支持这些应用类型:
计划支持:
换一个维度,支持:
- SPA
- 静态化
- 静态化 + htmlSuffix
- HTML 输出 publicPath
- 纯 JS + CSS(无 HTML)
- 本地 file 协议
注,部署 PWA 时需要对 service-worker.js 做特殊处理。
Thanks
