本篇对 doc 项目生成的.umi 进行分析。
待研究
参考 《## umi.ts》
参考 《### renderClient》
.umi 的项目结构
执行 pnpm doc:dev
命令后会生成 .umi 目录 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $ treer -d .umi -e aa.txt .umi ├─exports.ts #这个文件比较特殊 exports 是 umi 的别名,通过 import mui,指向的就是这个文件 ├─umi.ts #入口文件 ├─plugin-terminal | └index.ts ├─plugin-docs | ├─index.ts | └Layout.tsx ├─core | ├─EmptyRoute.tsx | ├─history.ts | ├─plugin.ts | ├─pluginConfig.d.ts | ├─polyfill.ts | └route.tsx
|
umi.ts
介绍
umi.ts 对应的 tpl 模板是 packages\preset-umi\templates\umi.tpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
import './core/polyfill';
import { renderClient } from 'D:/git/umi/umi-next/packages/renderer-react'; import { getRoutes } from './core/route'; import { createPluginManager } from './core/plugin'; import { createHistory } from './core/history'; import { ApplyPluginsType } from 'umi';
async function render() { const pluginManager = createPluginManager(); const { routes, routeComponents } = await getRoutes(pluginManager);
await pluginManager.applyPlugins({ key: 'patchRoutes', type: ApplyPluginsType.event, args: { routes, routeComponents, }, });
return (pluginManager.applyPlugins({ key: 'render', type: ApplyPluginsType.compose, initialValue() { const contextOpts = pluginManager.applyPlugins({ key: 'modifyContextOpts', type: ApplyPluginsType.modify, initialValue: {}, }); const context = { routes, routeComponents, pluginManager, rootElement: document.getElementById('root'), history: createHistory({ type: 'browser', }), basename: contextOpts.basename || '/', }; return renderClient(context); }, }))(); }
render();
|
其大致的原理 从 getRoutes 中获取 路由组件,以及菜单数据;
然后使用 renderer-react 的 renderClient 将页面渲染出来。
下面对上述过程中涉及的进行说明。
renderer-react 的 renderClient
todo
这个方法挺好,可以以此看下整个 packages\renderer-react\src\browser.tsx 。
看下这个router 以及 顶层如何设计的。
exports.ts
概述
这个文件比较特殊 exports 是 umi 的别名,通过 import mui,指向的就是这个文件。
1 2 3 4 5 6 7 8 9 10 11 12
|
export { createBrowserHistory, createHashHistory, createMemoryHistory, createSearchParams, Link, matchPath, matchRoutes, Navigate, NavLink, Outlet, useLocation, useMatch, useNavigate, useOutlet, useParams, useResolvedPath, useRoutes, useSearchParams, useAppData, renderClient, useRouteData } from 'D:/git/umi/umi-next/packages/renderer-react';
export { ApplyPluginsType, PluginManager } from 'D:/git/umi/umi-next/packages/umi/client/client/plugin.js'; export { history, createHistory } from './core/history';
export { FeatureItem, Features, Hero, Message } from 'D:/git/umi/umi-next/.umi/plugin-docs'; export { terminal } from 'D:/git/umi/umi-next/.umi/plugin-terminal';
|
exports的别名是umi
1 2 3 4 5 6 7 8 9 10 11
| alias: { umi: '@@/exports', react:..., },
memo.alias = { ...memo.alias, '@': args.paths.absSrcPath, '@@': args.paths.absTmpPath, };
|
通过preset-umi tmpFiles生成
exports.ts 是通过下面这个文件编译出来的:
1
| packages\preset-umi\src\features\tmpFiles\tmpFiles.ts
|
其实.umi下大部分文件,都是通过上面这个文件生成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
api.register({ key: 'onGenerateFiles', fn: async () => { const rendererPath = winPath( await api.applyPlugins({ key: 'modifyRendererPath', initialValue: dirname( require.resolve('@umijs/renderer-react/package.json'), ), }), );
const exports = []; const exportMembers = ['default']; exports.push('// @umijs/renderer-*');
exports.push( `export { ${( await getExportsAndCheck({ path: join(rendererPath, 'dist/index.js'), exportMembers, }) ).join(', ')} } from '${rendererPath}';`, );
exports.push('// umi/client/client/plugin'); const umiDir = process.env.UMI_DIR!; const umiPluginPath = winPath(join(umiDir, 'client/client/plugin.js')); exports.push( `export { ${( await getExportsAndCheck({ path: umiPluginPath, exportMembers, }) ).join(', ')} } from '${umiPluginPath}';`, ); exports.push(`export { history, createHistory } from './core/history';`); checkMembers({ members: ['history', 'createHistory'], exportMembers, path: '@@/core/history.ts', }); exports.push('// plugins'); const plugins = readdirSync(api.paths.absTmpPath).filter((file) => { if ( file.startsWith('plugin-') && (existsSync(join(api.paths.absTmpPath, file, 'index.ts')) || existsSync(join(api.paths.absTmpPath, file, 'index.tsx'))) ) { return true; } }); for (const plugin of plugins) { let file: string; if (existsSync(join(api.paths.absTmpPath, plugin, 'index.ts'))) { file = join(api.paths.absTmpPath, plugin, 'index.ts'); } if (existsSync(join(api.paths.absTmpPath, plugin, 'index.tsx'))) { file = join(api.paths.absTmpPath, plugin, 'index.tsx'); } const pluginExports = await getExportsAndCheck({ path: file!, exportMembers, }); if (pluginExports.length) { exports.push( `export { ${pluginExports.join(', ')} } from '${winPath( join(api.paths.absTmpPath, plugin), )}';`, ); } } exports.push('// plugins types.d.ts'); for (const plugin of plugins) { const file = winPath(join(api.paths.absTmpPath, plugin, 'types.d.ts')); if (existsSync(file)) { const noSuffixFile = file.replace(/\.ts$/, ''); exports.push(`export * from '${noSuffixFile}';`); } } api.writeTmpFile({ noPluginDir: true, path: 'exports.ts', content: exports.join('\n'), }); }, stage: Infinity, });
|
通过源码可知,生成的文件包含以下部分:
- @umijs/renderer-react 生成的
- umi/client/client/plugin 生成的
- @@/core/history.ts 生成的
- plugins 通过插件生成的 这部分是重点
- plugins types.ts
通过plugin-docs生成的部分
exports.ts 中,如下部分是哪里生成的,可能会疑惑,其实这部分就是上述 packages\preset-umi\src\features\tmpFiles\tmpFiles.ts
代码中的 【- plugins 通过插件生成的 这部分是重点】 部分生成的。
1 2 3
|
export { FeatureItem, Features, Hero, Message } from 'D:/git/umi/umi-next/.umi/plugin-docs';
|
插件定义的组件在这里被集成
由上可知
1 2
| export { FeatureItem, Features, Hero, Message } from 'D:/git/umi/umi-next/.umi/plugin-docs';
|
1 2
| export { FeatureItem, Features, Hero, Message } from 'D:/git/umi/umi-next/packages/plugin-docs/client/theme-doc/index.ts';
|
/core下的文件哪里生成的
都是preset-umi内生成
1 2 3 4 5 6 7
| ├─core | ├─EmptyRoute.tsx | ├─history.ts | ├─plugin.ts | ├─pluginConfig.d.ts | ├─polyfill.ts | └route.tsx
|
上面这些文件的模板都在 目录下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| $ treer -e cc.txt D:\git\umi\umi-next\packages\preset-umi\templates ├─apiRoute.tpl ├─history.tpl ├─middlewares.tpl ├─plugin.tpl ├─route.tpl ├─umi.tpl ├─generate | ├─api.ts.tpl | ├─mock.ts.tpl | ├─page | | ├─index.less.tpl | | └index.tsx.tpl | ├─component | | ├─component.tsx.tpl | | └index.ts.tpl
|
或者没有单独写模板文件,直接写在features\tmpFiles\tmpFiles.ts
中:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
api.writeTmpFile({ noPluginDir: true, path: 'core/EmptyRoute.tsx', content: ` import { Outlet } from 'umi'; export default function EmptyRoute() { return <Outlet />; } `, });
|
其文件的模板要么在上述的tpl内,要么就在features\tmpFiles\tmpFiles.ts
中