# umi as a Compiler (Full Version)
## 大纲
* 以 [《Compilers are the new frameworks》](https://tomdale.net/2017/09/compilers-are-the-new-frameworks/) 文章为切入点,解释 umi 的理论基础
* Demo 演示,给大家更直观的印象
* [约定优于配置](https://en.wikipedia.org/wiki/Convention_over_configuration)
* umi 从源码到产物,解释相比传统的 webpack/roadhog 有啥区别
* 关于路由,umi 为什么要接管路由,以及接管了能做啥
* 不能满足?用插件爆改 umi !
* 关于部署,umi 如何和流程对接
* 还有时间的话,可以讲讲 duck dictory 的 dva 项目、PWA、umi-test、内置的性能优化、各种配置、未来规划、其他开发体验等等
## 内容
### Compilers are the new frameworks
![Pasted Image 2.png](https://shipusercontent.com/5af08fa4045b39c3110110510422527b/Pasted%20Image%202.png)
不知大家有没有看过这篇文章,[《Compilers are the new frameworks》](https://tomdale.net/2017/09/compilers-are-the-new-frameworks/),作者认为,**“Web 框架正在从运行库转变为优化编译器”**,我深表认同,最近新出的框架,比如 next.js、umijs、after.js、pri 等,都是这个思路的贯彻者。
之前,工具是编译时的,框架是运行时的,两者互不强依赖,相互独立。但是,我们发现,把两者结合起来会让框架更强大,对使用者也更友好。比如,我在 pages 目录下建立 404.js 的文件,然后他就变成了整个项目的 fallback 路由,这在工具和框架分离的情况下是很难做到的。
为了让大家有更直观的印象,我们来看一个 demo 演示。
### Demo
![](https://gw.alipayobjects.com/zos/rmsportal/GZyaiVtFlcXmflpZJGqT.gif)
> 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,目录路由
* ...
### 从源码到产物
![Pasted Image 1.png](https://shipusercontent.com/d6db39956d37f136055c2b4593d98353/Pasted%20Image%201.png)
这是使用 roadhog/webpack 的流程,没啥好讲的,大家都很熟悉了。
![Pasted Image 2.png](https://shipusercontent.com/3aa83270a160a32c1446bc4a423fa303/Pasted%20Image%202.png)
这是 umi 的流程,
* 首先,umi 是基于路由的,所以需要有一份路由配置,路由配置哪里来?可以是基于约定的,也可以是基于配置的,看个人喜好,推荐约定式的
* 然后,路由配置会用于生成路由文件,在这里 umi 做了很多复杂的事情,比如开发时按需编译、运行时按需加载、各种性能优化等等,这个路由的部分后面会展开讲下
* 然后,把入口文件交给 webpack 做打包
* 同时,会处理 HTML 的生成(可选)
* 最后是部署,和各种流程对接
举个具体的例子。
文件目录:
```
+ src
+ layouts/index.js
+ pages
- a.js
- b.js
- 404.js
```
这会生成路由配置,
```
{
component: 'layouts/index.js',
routes: [
{ path: '/a', exact: true, component: 'pages/a.js' },
{ path: '/b', exact: true, component: 'pages/b.js' },
{ component: 'pages/404.js' },
],
}
```
然后生成路由文件,
```js
const routes = {
component: require('layouts/index.js'),
routes: [
{ path: '/a', exact: true, component: require('pages/a.js') },
{ path: '/b', exact: true, component: require('pages/b.js') },
{ component: require('pages/404.js') },
],
};
export default () =>
<Router history={window.g_history}>
{ renderRoutes(routes) }
</Router>
```
从例子中可以看到,umi 完全接管了路由的部分工作,那么为什么要接管路由,以及接管了能做啥呢?
### 为什么要接管路由?
不管是单页应用还是多页应用,都是以路由为维度组织的。单页应用里是路由,多页应用里的页面其实也可以理解为路由。路由决定渲染时显示什么,跳转时用什么方式,切换时用什么动画等等。
所以,接管路由之后,框架就可以:
* 开发时按需编译
* 运行时按需加载,且可一键关闭
* 根据路由数进行智能的 common 提取 (阈值:pages.length/2)
* 根据路由生成静态页面
* preload
* 接管页面跳转方式,比如在容器里用 bridgex
* 定制路由切换动画
* 定制路由切换的 scroll 行为
* 定制 layout
* 定制权限路由
* SPA 应用的埋点
* 路由代码可选
* SSR 时在服务器处理数据请求(未实现)
* ...
这个列表我每列一次都会增加不少,说明这里其实还有很大的想象空间。
### 路由约定
* 基于配置 vs. 基于约定
* layout
* 嵌套路由
* 目录路由
* umi-plugin-routes
之前一直不敢推,是因为不支持嵌套路由,在 umi@1.1 支持了之后,大家在 PC 等路由复杂的场景下也可以放心用了。
### 插件机制
![](https://gw.alipayobjects.com/zos/rmsportal/IToXzyuDegLvsGuIDAmE.gif)
> 不能满足?用插件爆改 umi !
![Pasted Image 2.png](https://shipusercontent.com/3aa83270a160a32c1446bc4a423fa303/Pasted%20Image%202.png)
再看回这张图,umi 整体梳理过运行流程的每个环节,并且都预留了埋点,甚至 umi 内部的实现也是由插件组成,这点有些类似 webpack,api 接口设计参考了 vue-cli@3,这点之前 @皓默 也有分享过。
比如,每个环节都提供修改的接口:
* modifyRoutes
* modifyRouteComponent
* modifyHTML
* modifyWebpackConfig
* modifyEntryFile
* modifyRouterFile
更多参考 [umijs/umi#87](https://github.com/umijs/umi/issues/87)
[内置的插件](https://github.com/umijs/umi/tree/master/packages/umi-build-dev/src/plugins):
* hd
* service-worker
* layout
* 404
* mock
* ...
[umi-plugin-ali](https://gitlab.alipay-inc.com/umijs/umi-plugin-ali/tree/master/src/fns) 包含的插件:
* basement
* xmas-fastclick
* spm-bacon
还有些其他的:
* umi-plugin-routes
* umi-plugin-dva
* umi-plugin-dll
* umi-plugin-yunfengdie
### 关于部署
![](https://gw.alipayobjects.com/zos/rmsportal/SpSrxCrXNFynBKdIFmcC.gif)
> umi 如何和流程对接?
灵活运用 publicPath 和 router base 就可以支持各种应用了,详见 https://umijs.org/docs/zh-Hans/deploy.html
目前支持这些应用类型:
* 云凤蝶
* site + 离线包
* egg
计划支持:
* assets
* 凤蝶活动
换一个维度,支持:
* SPA
* 静态化
* 静态化 + htmlSuffix
* HTML 输出 publicPath
* 纯 JS + CSS(无 HTML)
* 本地 file 协议
注,部署 PWA 时需要对 service-worker.js 做特殊处理。
### Thanks
![](https://gw.alipayobjects.com/zos/rmsportal/hnadpFUKixdgegWibnvV.gif)