源码 umi 版本为 version-4.0.0-rc.15
基础知识
预编译 与 father
father 内置了 预编译;
如何集成 preset-umi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
super({ ...opts, env: process.env.NODE_ENV, cwd, defaultConfigFiles: DEFAULT_CONFIG_FILES, frameworkName: FRAMEWORK_NAME, presets: [require.resolve('@umijs/preset-umi'), ...(opts?.presets || [])], plugins: [ existsSync(join(cwd, 'plugin.ts')) && join(cwd, 'plugin.ts'), existsSync(join(cwd, 'plugin.js')) && join(cwd, 'plugin.js'), ].filter(Boolean), });
|
如何集成 bundler-webpack
1 2 3 4 5 6 7 8 9 10
| const bundlerWebpack= lazyImportFromCurrentPkg('@umijs/bundler-webpack'); if (enableVite) { await bundlerVite.dev(opts); } else { await bundlerWebpack.dev(opts); }
|
这里的 bundlerWebpack.dev 其实就是
1 2 3 4 5 6 7
| import webpack from '../compiled/webpack';
mfsu = new MFSU({ implementor: webpack as any,
});
|
如上可知,配置了 @umijs/preset-umi
进行打包,那么就会只用 webpack 5 进行打包;
如何集成 webpack 5
参考以上
如何 webpack 5项目结合 MFSU
1 2 3 4 5
| const mfsu = new MFSU({ implementor: webpack, buildDepWithESBuild: true, });
|
webpack-5-chain 如何结合 webpack 5
1 2 3 4 5
| import Config from '../../compiled/webpack-5-chain';
const config = new Config();
|
babel配置如何集成
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
|
const webpackConfig = await getConfig({ cwd: opts.cwd, rootDir: opts.rootDir, env: Env.development, entry: opts.entry, userConfig: opts.config, babelPreset: opts.babelPreset, extraBabelPlugins: [ ...(opts.beforeBabelPlugins || []), ...(mfsu?.getBabelPlugins() || []), ...(opts.extraBabelPlugins || []), ], extraBabelPresets: [ ...(opts.beforeBabelPresets || []), ...(opts.extraBabelPresets || []), ], extraBabelIncludes: opts.config.extraBabelIncludes, extraEsbuildLoaderHandler: mfsu?.getEsbuildLoaderHandler() || [], chainWebpack: opts.chainWebpack, modifyWebpackConfig: opts.modifyWebpackConfig, hmr: true, analyze: process.env.ANALYZE, cache: opts.cache, });
|
1 2 3 4 5
|
await addJavaScriptRules(applyOpts);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
rule .use('babel-loader') .loader(require.resolve('../../compiled/babel-loader')) .options({ sourceType: 'unambiguous', babelrc: false, cacheDirectory: false, targets: userConfig.targets, presets: [ opts.babelPreset || [ require.resolve('@umijs/babel-preset-umi'), ... ], plugins: [ ... ] });
|
.umirc 配置的读取和使用
概述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| async run(opts: { name: string; args?: any }) { const configManager = new Config({ cwd: this.cwd, env: this.env, defaultConfigFiles: this.opts.defaultConfigFiles, specifiedEnv: process.env[`${prefix}_ENV`.toUpperCase()], });
this.userConfig = configManager.getUserConfig().config; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
getUserConfig() { const configFiles = Config.getConfigFiles({ mainConfigFile: this.mainConfigFile, env: this.opts.env, specifiedEnv: this.opts.specifiedEnv, }); return Config.getUserConfig({ configFiles: getAbsFiles({ files: configFiles, cwd: this.opts.cwd, }), }); }
|
关于 this.opts.defaultConfigFiles
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
import { DEFAULT_CONFIG_FILES } from '../constants';
constructor(opts?: any) { process.env.UMI_DIR = dirname(require.resolve('../../package')); const cwd = getCwd(); super({ ... defaultConfigFiles: DEFAULT_CONFIG_FILES, ... }); }
|
1 2 3 4 5 6 7
| export const DEFAULT_CONFIG_FILES = [ '.umirc.ts', '.umirc.js', 'config/config.ts', 'config/config.js', ];
|
如何生成.umi
packages\preset-umi\src\commands\dev\dev.ts
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
| async fn() { async function generate(opts: { isFirstTime?: boolean; files?: any }) { await api.applyPlugins({ key: 'onGenerateFiles', args: { files: opts.files || null, isFirstTime: opts.isFirstTime, }, }); } await generate({ isFirstTime: true, });
const pluginFiles: string[] = [ join(api.cwd, 'plugin.ts'), join(api.cwd, 'plugin.js'), ]; pluginFiles.forEach((filePath: string) => { watch({ path: filePath, addToUnWatches: true, onChange() { logger.event(`${basename(filePath)} changed, restart server...`); api.restartServer(); }, }); });
if (enableVite) { await bundlerVite.dev(opts); } else { await bundlerWebpack.dev(opts); } },
|
preset-umi\src\features\tmpFiles\tmpFiles.ts
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
| api.onGenerateFiles(async (opts) => { .... api.writeTmpFile({ noPluginDir: true, path: 'core/history.ts', tplPath: join(TEMPLATES_DIR, 'history.tpl'), context: { rendererPath, }, }); });
api.register({ key: 'onGenerateFiles', fn: async () => { ... api.writeTmpFile({ noPluginDir: true, path: 'exports.ts', content: exports.join('\n'), }); }, stage: Infinity, });
|
tpl
各种tpl模板位置如: packages\preset-umi\templates\history.tpl
关键的commands\dev\dev.ts
从这个例子看出,整个项目的初始化,以及各种文件生成 等等 事情,都可以在 packages\preset-umi\src\commands\dev\dev.ts
找到答案。
umi dev 过程源码分析
过程概述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| packages\umi\bin\umi.js packages\umi\src\cli\cli.ts packages\umi\src\cli\dev.ts packages\umi\src\cli\fork.ts packages\umi\bin\forkedDev.js
packages\umi\src\cli\forkedDev.ts
packages\umi\src\service\service.ts
packages\core\src\service\service.ts packages\preset-umi\src\commands\dev\dev.ts packages\bundler-webpack\src\dev.ts packages\bundler-webpack\src\server\server.ts
|
下面介绍详细过程:
准备过程
1 2 3 4 5
| packages\umi\bin\umi.js packages\umi\src\cli\cli.ts packages\umi\src\cli\dev.ts packages\umi\src\cli\fork.ts packages\umi\bin\forkedDev.js
|
packages\umi\src\cli\forkedDev.ts
在这里引入下面的service.ts
1 2 3 4 5 6 7 8 9 10
|
import { Service } from '../service/service';
const service = new Service(); await service.run2({ name: DEV_COMMAND, args, });
|
packages\umi\src\service\service.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
import { Service as CoreService } from '@umijs/core'; super({ ...opts, env: process.env.NODE_ENV, cwd, defaultConfigFiles: DEFAULT_CONFIG_FILES, frameworkName: FRAMEWORK_NAME, presets: [require.resolve('@umijs/preset-umi'), ...(opts?.presets || [])], plugins: [ existsSync(join(cwd, 'plugin.ts')) && join(cwd, 'plugin.ts'), existsSync(join(cwd, 'plugin.js')) && join(cwd, 'plugin.js'), ].filter(Boolean), });
|
如上 在这个js上,除了在构造函数上初始化 umi/core 的service外,还在其 run2 方法中调用 了core 的run 方法:
1 2 3 4 5 6
|
async run2(opts: { name: string; args?: any }) { return await this.run({ ...opts, name }); }
|
到此,所有的工作都转给 核心 @umijs/core 的 service 类来处理了。
(核心@umijs/core) packages\core\src\service\service.ts
在 packages\core\src\service\service.ts
中执行 run 方法,方法内部执行 注册的 dev 命令
1 2 3 4 5
| packages\core\src\service\service.ts async run(opts: { name: string; args?: any }) { let ret = await command.fn({ args }); }
|
packages\preset-umi\src\commands\dev\dev.ts
此 dev命令 在这里注册:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const bundlerWebpack = lazyImportFromCurrentPkg('@umijs/bundler-webpack'); api.registerCommand({ name: 'dev', description: 'dev server for development', details: ` umi dev
# dev with specified port PORT=8888 umi dev `, async fn() { if (enableVite) { await bundlerVite.dev(opts); } else { await bundlerWebpack.dev(opts); } }, });
|
packages\bundler-webpack\src\dev.ts
因此执行来到 bundlerWebpack.dev(opts)
,此方法定义在 packages\bundler-webpack\src\dev.ts
dev 会执行 createServer 方法,
packages\bundler-webpack\src\server\server.ts
createServer 的逻辑在 packages\bundler-webpack\src\server\server.ts
createServer 主要做 webpack 编译 以及 然后通过 express 创建 dev server,
这里可以打印相关的 webpack 配置等等。
1 2
| const compiler = webpack(configs);
|
至此整个 umi dev 的流程走完了。
要点对象
Service
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
| Service { appData: {}, args: { _: [], '$0': '' }, commands: {}, generators: {}, config: {}, configSchemas: {}, configDefaults: {}, configOnChanges: {}, hooks: {}, name: '', paths: {}, plugins: {}, keyToPluginMap: {}, pluginMethods: {}, skipPluginIds: Set(0) {}, stage: 0, userConfig: {}, configManager: null, pkg: {}, pkgPath: '', cwd: 'D:\\git\\umi\\travel-um', env: 'development', opts: { env: 'development', cwd: 'D:\\git\\umi\\travel-um', defaultConfigFiles: [ '.umirc.ts', '.umirc.js', 'config/config.ts', 'config/config.js' ], frameworkName: 'umi', presets: [ 'D:\\git\\umi\\travel-um\\node_modules\\@umijs\\preset-umi\\dist\\index.js' ], plugins: [] } }
|
webpack.config
见《umi系列(end) – webpack.config》