# 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)