React原理探索- @providesModule 模塊系統

@providesModule是什麼

react拋出組件化的概念後,對於開發者而言,爲了提升代碼的可讀性與結構性,經過文件目錄結構去闡述組件嵌套關係無疑是一個很好的辦法,可是目錄級別的加深,同時讓require的文件路徑讓人頭疼。絕大多數公司會使用本身定製的alias工具,在腳手架入口配置文件中給相應的filePath賦予別名,pack時,進行統一替換。node

#ykit.config

...
alias:{
'Common':'./src/util/index.js',
'Component':'src/components/index.js'
}
...

固然也能夠在文件中寫入惟一的標識位,pack時將該標識位與當前聲明標識位的filePath創建聯繫,facebook提供的@providesModule的就是這一策略。使用方法以下:react

#a.js
/**
* @providesModule Common
*/

export const isArray = () => {
...
}

export const isObject = () => {
...
}

#b.js

import { isArray } from 'Common'

isArray([])

如何實現@providesModule

fbjs-script/gulp:json

shared/provides-module.js中提供了這樣一段正則,用於匹配文件中是否有相似@providesModule的標識符gulp

module.exports = {
regexp: /\r?\n \* \@providesModule (\S+)(?=\r?\n)/,
};

modules-map.js 中:babel

transform函數調用如上正則對讀入文本進行解析,並將alias的別名與filePath創建映射關係ide

flush函數將前面拿到的映射表進行處理加上統一前綴,並導入到json文件中函數

function transform(file, enc, cb) {
if (file.isNull()) {
cb(null, file);
return;
}

if (file.isStream()) {
cb(new gutil.PluginError('module-map', 'Streaming not supported'));
return;
}

// Get the @providesModule piece of out the file and save that.
var matches = file.contents.toString().match(PM_REGEXP);
if (matches) {
var name = matches[1];
if (moduleMap.hasOwnProperty(name)) {
this.emit(
'error',
new gutil.PluginError(
PLUGIN_NAME,
'Duplicate module found: ' + name + ' at ' + file.path + ' and ' +
moduleMap[name]
)
);
}
moduleMap[name] = file.path;
}
this.push(file);
cb();
}

function flush(cb) {
// Keep it ABC order for better diffing.
var map = Object.keys(moduleMap).sort().reduce(function(prev, curr) {
// Rewrite path here since we don't need the full path anymore.
prev[curr] = prefix + path.basename(moduleMap[curr], '.js');
return prev;
}, {});
fs.writeFile(moduleMapFile, JSON.stringify(map, null, 2), 'utf-8', function() {
// avoid calling cb with fs.write callback data
cb();
});
}

最後導出以下json(以fbjs build爲例)工具

{
"BrowserSupportCore": "fbjs/lib/BrowserSupportCore",
"CSSCore": "fbjs/lib/CSSCore",
"CircularBuffer": "fbjs/lib/CircularBuffer",
"DOMMouseMoveTracker": "fbjs/lib/DOMMouseMoveTracker",
"DataTransfer": "fbjs/lib/DataTransfer",
"Deferred": "fbjs/lib/Deferred",
"ErrorUtils": "fbjs/lib/ErrorUtils",
"EventListener": "fbjs/lib/EventListener",
"ExecutionEnvironment": "fbjs/lib/ExecutionEnvironment",
"Heap": "fbjs/lib/Heap",
"IntegerBufferSet": "fbjs/lib/IntegerBufferSet",
"Keys": "fbjs/lib/Keys",
"Locale": "fbjs/lib/Locale",
"Map": "fbjs/lib/Map",
"PhotosMimeType": "fbjs/lib/PhotosMimeType",
"PrefixIntervalTree": "fbjs/lib/PrefixIntervalTree",
"Promise": "fbjs/lib/Promise",
"PromiseMap": "fbjs/lib/PromiseMap",
}

然後該作什麼你們也清楚了,要麼node腳本去把文件裏require 對應別名的進行路徑替換,要麼經過babel替換,固然,facebook是經過babel玩的組件化

題外話

其實對於alias system目前提供的兩種方法,各有利弊。fb提供的方法,使得使用上更加便利,可是因爲alias遍地存在,聲明衝突也變得屢見不鮮(固然能夠經過統一前綴解決)。傳統在腳手架配置文件中聲明的方法,雖然能讓你對alias的聲明一目瞭然,可是使用上也繁瑣不少ui

相關文章
相關標籤/搜索