最近在做性能优化相关的事情,也比较好奇umi2,umi3这套机制是如何运行的,怎么更好的做性能优化。
umi2和umi3都是一个编译时框架,即先会对本地代码做预编译,然后再交给webpack等打包工具,不然webpack无法直接处理。支付宝小程序也是这样。
干货分享:蚂蚁金服前端框架和工程化实践语言 & 开发陈成_InfoQ精选文章 路由实现
整体是如何构成的
config/routes中写了路由的配置,支持树结构的嵌套- 预编译成
src/.umi/core/routes文件 - webpack打包,会打包出多个chunks,
- 运行时 👍👍 重点
几个问题
- 为什么上一个layout执行完,才会加载下一个命中层级的资源,背后的原理是什么?
- 猜测children是对原组件做了wrapper,背后包含loadable,即要子components先执行load(即去加载资源),执行完才初始化
TODO
- 弄清楚整个接口
- 弄清楚整个运行时结构
- 组件的dynamic代码,还是移动到运行时把?再详细和清晰化一些
- 为何Router不直接使用,有几种不同的runtime? 服务端的?
1 文件编写
pathwrapperscomponent// 即子路由routes
2 预编译
import { ApplyPluginsType, dynamic } from '/Users/admin/devspace/umiapp3/node_modules/umi/node_modules/@umijs/runtime';export function getRoutes() {const routes = [{"path": "/sub-page",// 父路由,一般定义layout// 注释定义了chunk name// dynamic则在下面的4运行时中讲"component": dynamic({ loader: () => import(/* webpackChunkName: 'p__layouts__index' */'/Users/admin/devspace/umiapp3/src/pages/layouts/index'), loading: LoadingComponent}),"name": "",// 子路由"routes": [{"path": """name":"component"}]}];// allow user to extend routesplugin.applyPlugins({key: 'patchRoutes',type: ApplyPluginsType.event,args: { routes },});return routes;}
umi.ts的运行核心
import { getRoutes } from './core/routes';const getClientRender = (args: { hot?: boolean; routes?: any[] } = {}) => plugin.applyPlugins({key: 'render',type: ApplyPluginsType.compose,initialValue: () => {const opts = plugin.applyPlugins({key: 'modifyClientRenderOpts',type: ApplyPluginsType.modify,initialValue: {routes: args.routes || getRoutes(),plugin,history: createHistory(args.hot),isServer: process.env.__IS_SERVER,dynamicImport: true,rootElement: 'root',},});return renderClient(opts);},args,});
dynamic({loader, loading});
dynamic -> Loadable -> createLoadableComponent(执行加载代码,并根据加载的状态,渲染loading或者加载完成的组件,或者null)
3 编译
component: dynamic({loader: function loader(){return Promise.all([__webpack_require__.e('chunk1'),__webpack_require__.e('chunk2'),__webpack_require__.e('p_chunkName'),]).then(__webpack_require__.bind(__webpack_require__, "./src/");},loading:})
4 运行时 如何递归树层级的路由 👍👍
- Layout
props.children即是子组件?是谁计算出来的?貌似是一个<Switch />组件
plugin-layout -> 递归方式,把路由配置的item.children赋值为item.routes,帮助在layout中执行props.children渲染子组件
- renderClient 负责组合umi的
opts和react-router-config,react-router中的Router提供__RouterContext以及@umi/runtime并在此变更document.title
使用了reactRouterConfig.matchRoutes并未使用renderRoutes
- React的树结构是如何的?通过Switch传入下个层次的route配置实现了递归。
<RouterComponent> 路由的根组件,只有一个<Router> runtime的router,也即来自react-router-dom的Router,自umi顶部传入进来的opts.history<Switch> /**props [Routes] 根据传入的routes,执行match,render不同的Component.**/<Route><runtime.__RouterContext.Consumer> // 消费上一级别的Consumer<runtime.__RouterContext.Provider> // 提供给下一级别的Provider,新的计算后的props,和Formily的每个层级有点像,基于上一层不断递进的层级。<xxxLayout> 第一层级,props.children为Switch组件<Switch><Route><xxxPage /> 第二层级,</Route></Switch></xxxLayout></runtime.__RouterContext.Provider></runtime.__RouterContext.Consumer></Route></Switch></Router></RouterComponent>
renderRoutes将路由的routes配置,转换为React的树结构
- 组件的动态加载,dynamic
dynamic有两个参数,loader和loading
dynamic作为一个组件,负责异步加载loader的加载,并且在不同状态时输出不同内容,loading的展示,包加载完成后则展示真正的组件。
component: dynamic({loader: function loader(){return Promise.all([__webpack_require__.e('chunk1'),__webpack_require__.e('chunk2'),__webpack_require__.e('p_chunkName'),]).then(__webpack_require__.bind(__webpack_require__, "./src/");},loading:})
整个umi的运行时核心和插件机制
.umi/umi.ts 入口文件@umijs/renderer-react 这个做什么?针对umi的opts配置来渲染,opts.routes等结合react-router-config.@umi/runtime
import { getRoutes } from './core/routes';const getClientRender = (args: { hot?: boolean; routes?: any[] } = {}) => plugin.applyPlugins({key: 'render',// 插件类型compose,还有modify,eventtype: ApplyPluginsType.compose,initialValue: () => {// 插件const opts = plugin.applyPlugins({key: 'modifyClientRenderOpts',type: ApplyPluginsType.modify,initialValue: {routes: args.routes || getRoutes(),plugin,history: createHistory(args.hot),isServer: process.env.__IS_SERVER,dynamicImport: true,rootElement: 'root',},});return renderClient(opts);},args,});const clientRender = getClientRender();export default clientRender();
有哪些插件事件?
- render
- modifyClientRenderOpts
一些插件的运行
- plugin-antd-icon-config -> patchRoutes 针对icon的配置,patch为antdIcon的element
- plugin-layout -> 递归方式,把路由配置的item.children赋值为item.routes,帮助在layout中执行props.children渲染子组件
插件哪几种组合类型?compose, modify, event
