2018-11-08
Metro 整体上采用 C/S 结构,它对外暴露一个 Metro Server 类。Server 会在构造的时候接受一个结构,用于配置它内部的行为,这个结构是我们操纵 Server 内部行为的主要途径。
因此想要正确控制打包器的话,首先要创建正确的配置结构,这个结构在 Metro 中称为 ConfigT。它定义于 metro-config 包的 configTypes.flow.js 中。
在这篇文章中,我们来梳理一下 ConfigT 结构。
ConfigT 非常复杂,从整体上,它分为以下几个子结构:
其中:
因此,configT 从整体上看结构如下:
{ resolver: { // ResolverConfigT }, server: { // ServerConfigT }, serializer: { // SerializerConfigT }, transformer: { // TransformerConfigT }, // MetalConfigT 在跟层展开 }
在 node_modules/metro-config/src/defaults/index.js 下有一份默认值。
MetalConfigT 包含以下字段:
| 字段 | 说明 |
|---|---|
| cacheStores | 缓存类 |
| cacheVersion | 缓存版本 |
| maxWorkers | 并发构建时数 |
| projectRoot | 项目根目录 |
| stickyWorkers | |
| transformerPath | transformer 路径 |
| reporter | 日志记录器 |
| resetCache | 是否清空缓存 |
| watchFolders | 源文件的全集 |
cacheStores 是缓存类,默认值是 FileStore。其中 FileStore 定义在 metro-cache 包中。
cacheVersion 是缓存版本,默认值是 '1.0'。
项目根目录,如果没指定的话,默认是从脚本运行 metro 所在的位置向上两个层级作为根目录。
用于 node_modules/metro/src/DeltaBundler/WorkerFarm.js 的 JestWorker。
默认是 metro/src/JSTransformer/worker.js。
transformer 的功能是编译源文件,内部通过调用 babel 来实现编译功能。
默认值是 TerminalReporter,位于 packages/metro/src/lib/TerminalReporter.js。
定义源文件的全集,打包构建工作将在这个全集中执行。
如果一个文件在构建时发现它不在 watchFolders 中,将会构建失败(sha-1 计算错误)。
ResolverConfigT 包含以下字段:
| 字段 | 说明 |
|---|---|
| assetExts | 资源文件扩展名 |
| blacklistRE | 模块路径的忽略规则 |
| extraNodeModules | |
| hasteImplModulePath | |
| platforms | 所解析的平台 |
| providesModuleNodeModules | |
| resolverMainFields | |
| resolveRequest | |
| sourceExts | 代码文件后缀 |
| useWatchman | 是否启用 Watchman |
资源文件扩展名,默认值为:
exports.assetExts = [
// Image formats
'bmp', 'gif', 'jpg', 'jpeg',
'png', 'psd', 'svg', 'webp',
// Video formats
'm4v', 'mov', 'mp4',
'mpeg', 'mpg', 'webm',
// Audio formats
'aac', 'aiff', 'caf',
'm4a', 'mp3', 'wav',
// Document formats
'html', 'pdf',
// Font formats
'otf', 'ttf',
];
如果一个文件的后缀在 assetExts 中,就被认为是一个资源文件。
模块路径的忽略规则,定义在 node_modules/metro-config/src/defaults/blacklist.js 中。
默认规则执行后的值为:
/\\(node_modules\\[\\\\/\\\\\\\\\\]react\\[\\\\/\\\\\\\\\\]dist\\[\\\\/\\\\\\\\\\]\\.\\*\\|website\\\\/node_modules\\\\/\\.\\*\\|heapCapture\\\\/bundle\\\\\\.js\\|\\.\\*\\\\/__tests__\\\\/\\.\\*\\)\\$/
默认值是 undefined。(在 react-native 的 local-cli/core/index.js 中,它指向路径 ../../jest/hasteImpl,即 react-native 的 jest/hasteImpl.js)。
所解析的平台。默认值:
exports.platforms = ['ios', 'android', 'windows', 'web'];
默认值:
exports.providesModuleNodeModules = ['react-native', 'react-native-windows'];
默认值:
resolverMainFields: ['browser', 'main']
默认值:null。
代码文件后缀。默认值:
exports.sourceExts = ['js', 'json', 'ts', 'tsx'];
ServerConfigT 包含以下字段:
| 字段 | 说明 |
|---|---|
| enableVisualizer | 是否启用可视化器(?还有这功能?) |
| enhanceMiddleware | 应用中间件(?还有这功能?) |
| useGlobalHotkey | (?) |
| port | 端口号 |
SerializerConfigT 包含以下字段:
| 字段 | 说明 |
|---|---|
| createModuleIdFactory | 创建 module id 的工厂 |
| experimentalSerializerHook | ? |
| getModulesRunBeforeMainModule | 指定在主模块前运行的模块 |
| getPolyfills | 获取 Polyfills |
| getRunModuleStatement | |
| polyfillModuleNames | |
| postProcessBundleSourcemap | |
| processModuleFilter |
创建 module id 的工厂,方法签名:
() => (path: string) => number
不知道干什么的,方法签名:
(graph: Graph<>, delta: DeltaResult<>) => mixed
指定在主模块前运行的模块,方法签名:
(entryFilePath: string) => Array<string>
默认值:
getModulesRunBeforeMainModule: () => []
获取 Polyfills,方法签名:
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>
默认值:
() => []
方法签名:
(number | string) => string
默认值:
getRunModuleStatement: moduleId => `__r(${JSON.stringify(moduleId)});`
默认值:[]。
默认值:
postProcessBundleSourcemap: ({code, map, outFileName}) => ({code, map})
默认值:
processModuleFilter: module => true,
SerializerConfigT 包含以下字段:
| 字段 | 说明 |
|---|---|
| assetPlugins | 资源插件 |
| assetRegistryPath | |
| asyncRequireModulePath | |
| babelTransformerPath | |
| dynamicDepsInPackages | |
| enableBabelRCLookup | |
| enableBabelRuntime | |
| minifierConfig | 压缩混淆器设置 |
| minifierPath | 压缩混淆器路径 |
| optimizationSizeLimit | |
| getTransformOptions | |
| postMinifyProcess | |
| transformVariants | |
| workerPath |
资源插件。默认值:[]。
默认值:'missing-asset-registry-path'
默认值:'metro/src/lib/bundle-modules/asyncRequire'
默认值:'metro/src/defaultTransformer'
默认值:'throwAtRuntime'
默认值:true。
默认值:true。
默认值:
minifierConfig: { mangle: { toplevel: false, }, output: { ascii_only: true, quote_style: 3, wrap_iife: true, }, sourceMap: { includeSources: false, }, toplevel: false, compress: { // reduce_funcs inlines single-use functions, which cause perf regressions. reduce_funcs: false, }, }
默认值:'metro-minify-uglify'
默认值:150 * 1024, // 150 KiB.
默认值:
getTransformOptions: async () => ({
transform: {experimentalImportSupport: false, inlineRequires: false},
preloadedModules: false,
ramGroups: [],
})
默认值:
postMinifyProcess: x => x
默认值:
transformVariants: {default: {}}
默认值:
workerPath: 'metro/src/DeltaBundler/Worker'
以上就是 ConfigT 的所有设置项,可以看出数量非常多,这也说明了 Metro Server 的工程是非常丰富的。
其中很多选项我还不知道他们的含义是什么,后续随着认识的加深,我会回来再继续补全。