React Native Metro 学习笔记 2 -- Metro ConfigT

2018-11-08

介绍

Metro 整体上采用 C/S 结构,它对外暴露一个 Metro Server 类。Server 会在构造的时候接受一个结构,用于配置它内部的行为,这个结构是我们操纵 Server 内部行为的主要途径。

因此想要正确控制打包器的话,首先要创建正确的配置结构,这个结构在 Metro 中称为 ConfigT。它定义于 metro-config 包的 configTypes.flow.js 中。

提示:在看 Metro 代码的时候,最好从 GitHub 上 clone 一份,而不要去 React Native APP 工程下的 node_modules/metro 中查看,因为后者是经过构建之后的,可读性已经大大降低了。

在这篇文章中,我们来梳理一下 ConfigT 结构。

整体结构

ConfigT 非常复杂,从整体上,它分为以下几个子结构:

  • MetalConfigT
  • ResolverConfigT
  • ServerConfigT
  • SerializerConfigT
  • TransformerConfigT

其中:

  • MetalConfigT 在 configT 中是展开在最顶层的
  • 后面三个则有对应的子结构

因此,configT 从整体上看结构如下:

{
    resolver: {
        // ResolverConfigT
    },
    server: {
        // ServerConfigT
    },
    serializer: {
        // SerializerConfigT
    },
    transformer: {
        // TransformerConfigT
    },
    // MetalConfigT 在跟层展开
}

在 node_modules/metro-config/src/defaults/index.js 下有一份默认值。

MetalConfigT

MetalConfigT 包含以下字段:

字段说明
cacheStores缓存类
cacheVersion缓存版本
maxWorkers并发构建时数
projectRoot项目根目录
stickyWorkers
transformerPathtransformer 路径
reporter日志记录器
resetCache是否清空缓存
watchFolders源文件的全集

cacheStores & cacheVersion

cacheStores 是缓存类,默认值是 FileStore。其中 FileStore 定义在 metro-cache 包中。

cacheVersion 是缓存版本,默认值是 '1.0'。

projectRoot

项目根目录,如果没指定的话,默认是从脚本运行 metro 所在的位置向上两个层级作为根目录。

stickyWorkers

用于 node_modules/metro/src/DeltaBundler/WorkerFarm.js 的 JestWorker。

transformerPath

默认是 metro/src/JSTransformer/worker.js。

transformer 的功能是编译源文件,内部通过调用 babel 来实现编译功能。

reporter

默认值是 TerminalReporter,位于 packages/metro/src/lib/TerminalReporter.js。

watchFolders

定义源文件的全集,打包构建工作将在这个全集中执行。

如果一个文件在构建时发现它不在 watchFolders 中,将会构建失败(sha-1 计算错误)。

ResolverConfigT

ResolverConfigT 包含以下字段:

字段说明
assetExts资源文件扩展名
blacklistRE模块路径的忽略规则
extraNodeModules
hasteImplModulePath
platforms所解析的平台
providesModuleNodeModules
resolverMainFields
resolveRequest
sourceExts代码文件后缀
useWatchman是否启用 Watchman

assetExts

资源文件扩展名,默认值为:

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 中,就被认为是一个资源文件。

blacklistRE

模块路径的忽略规则,定义在 node_modules/metro-config/src/defaults/blacklist.js 中。

默认规则执行后的值为:

/\\(node_modules\\[\\\\/\\\\\\\\\\]react\\[\\\\/\\\\\\\\\\]dist\\[\\\\/\\\\\\\\\\]\\.\\*\\|website\\\\/node_modules\\\\/\\.\\*\\|heapCapture\\\\/bundle\\\\\\.js\\|\\.\\*\\\\/__tests__\\\\/\\.\\*\\)\\$/

hasteImplModulePath

默认值是 undefined。(在 react-native 的 local-cli/core/index.js 中,它指向路径 ../../jest/hasteImpl,即 react-native 的 jest/hasteImpl.js)。

platforms

所解析的平台。默认值:

exports.platforms = ['ios', 'android', 'windows', 'web'];

providesModuleNodeModules

默认值:

exports.providesModuleNodeModules = ['react-native', 'react-native-windows'];

resolverMainFields

默认值:

resolverMainFields: ['browser', 'main']

resolveRequest

默认值:null。

sourceExts

代码文件后缀。默认值:

exports.sourceExts = ['js', 'json', 'ts', 'tsx'];

ServerConfigT

ServerConfigT 包含以下字段:

字段说明
enableVisualizer是否启用可视化器(?还有这功能?)
enhanceMiddleware应用中间件(?还有这功能?)
useGlobalHotkey(?)
port端口号

SerializerConfigT

SerializerConfigT 包含以下字段:

字段说明
createModuleIdFactory创建 module id 的工厂
experimentalSerializerHook?
getModulesRunBeforeMainModule指定在主模块前运行的模块
getPolyfills获取 Polyfills
getRunModuleStatement
polyfillModuleNames
postProcessBundleSourcemap
processModuleFilter

createModuleIdFactory

创建 module id 的工厂,方法签名:

() => (path: string) => number

experimentalSerializerHook

不知道干什么的,方法签名:

(graph: Graph<>, delta: DeltaResult<>) => mixed

getModulesRunBeforeMainModule

指定在主模块前运行的模块,方法签名:

 (entryFilePath: string) => Array<string>

默认值:

getModulesRunBeforeMainModule: () => []

getPolyfills

获取 Polyfills,方法签名:

getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>

默认值:

() => []

getRunModuleStatement

方法签名:

(number | string) => string

默认值:

getRunModuleStatement: moduleId => `__r(${JSON.stringify(moduleId)});`

polyfillModuleNames

默认值:[]

postProcessBundleSourcemap

默认值:

postProcessBundleSourcemap: ({code, map, outFileName}) => ({code, map})

processModuleFilter

默认值:

processModuleFilter: module => true,

TransformerConfigT

SerializerConfigT 包含以下字段:

字段说明
assetPlugins资源插件
assetRegistryPath
asyncRequireModulePath
babelTransformerPath
dynamicDepsInPackages
enableBabelRCLookup
enableBabelRuntime
minifierConfig压缩混淆器设置
minifierPath压缩混淆器路径
optimizationSizeLimit
getTransformOptions
postMinifyProcess
transformVariants
workerPath

assetPlugins

资源插件。默认值:[]

assetRegistryPath

默认值:'missing-asset-registry-path'

asyncRequireModulePath

默认值:'metro/src/lib/bundle-modules/asyncRequire'

babelTransformerPath

默认值:'metro/src/defaultTransformer'

dynamicDepsInPackages

默认值:'throwAtRuntime'

enableBabelRCLookup

默认值:true。

enableBabelRuntime

默认值:true。

minifierConfig

默认值:

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,
    },
}

minifierPath

默认值:'metro-minify-uglify'

optimizationSizeLimit

默认值:150 * 1024, // 150 KiB.

getTransformOptions

默认值:

getTransformOptions: async () => ({
    transform: {experimentalImportSupport: false, inlineRequires: false},
    preloadedModules: false,
    ramGroups: [],
})

postMinifyProcess

默认值:

postMinifyProcess: x => x

transformVariants

默认值:

transformVariants: {default: {}}

workerPath

默认值:

workerPath: 'metro/src/DeltaBundler/Worker'

总结

以上就是 ConfigT 的所有设置项,可以看出数量非常多,这也说明了 Metro Server 的工程是非常丰富的。

其中很多选项我还不知道他们的含义是什么,后续随着认识的加深,我会回来再继续补全。