React Native官方拆包之metro bundle
快速入門
安裝
安裝metro-core依賴主要有兩種方式:npm和yarn。npm安裝的命令如下:
npm install --save-dev metro metro-core
1
yarn方式的安裝命令如下:
yarn add --dev metro metro-core
1
運行
metro bundle支持使用CLI腳手架方式運行和通過程序的編程調用它來運行。
在程序中使用metro需要先導入它,導入的方式如下:
const Metro = require("metro");
1
方法
metro提供了很多有用的函數,這些函數包括:
runMetro(config)
此方法用於給定配置,請求特定的服務。此時,您可以使用processRequest方法來hook HTTP(S)請求。例如:
"use strict";
const http = require("http");
const Metro = require("metro");
// We first load the config from the file system
Metro.loadConfig().then(config => {
const metroBundlerServer = Metro.runMetro(config);
const httpServer = http.createServer(
metroBundlerServer.processRequest.bind(metroBundlerServer),
);
httpServer.listen(8081);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
為了與Express apps兼容,當請求無法被Metro bundler處理時,processRequest也會調用它的第三個參數。這允許您將服務與現有服務集成,或者擴展一個新的服務。
const httpServer = http.createServer((req, res) => {
metroBundlerServer.processRequest(req, res, () => {
// Metro does not know how to handle the request.
});
});
1
2
3
4
5
如果您正在使用Express,可以將processRequest作為中間件進行使用。
const express = require("express");
const app = express();
app.use(
metroBundlerServer.processRequest.bind(metroBundlerServer),
);
app.listen(8081);
1
2
3
4
5
6
7
8
runServer(Config, Options)
runServer基於給定的配置和選項啟動開發服務,並返回服務。我們建議使用runMetro而不是runServer。runServer可選的參數有:
host (string):服務的host駐留地址。
onReady (Function):在服務已經準備好時服務請求時被調用。
secure (boolean):服務是否需要運行在https上,而不是http上。
secureKey (string):使用https訪問時secureKey。
secureCert (string):用於https訪問的安全證書。
hmrEnabled (boolean):是否開啟熱更新功能。
runBuild(Config, Options)
此函數用於,給定一個配置和一組通常傳遞給伺服器的選項,以及一組特定於包本身的選項,並用於構建一個包。runBuild支持的選項有:
dev (boolean):構建一個開發版本。例如,process.env.NODE_ENV = 『development』。
entry (string):指向要綁定的條目文件。
onBegin (Function):綁定開始時被調用。
onComplete (Function): 綁定完成後調用。
onProgress (Function):在包期間調用,每次有關於模塊計數/進度的新信息時被調用。
minify (boolean): 是否縮小bundle。
out (string):輸出包的路徑。
platform (『web』 | 『android』 | 『ios』): 指定打包的平台。
sourceMap (boolean):是否生成源映射。
sourceMapUrl (string): 源映射的URL匹配,它默認為與包相同的URL,只是將擴展名從.bundle更改為.map。
可用選項
有關配置選項的詳細信息,可用參考下面的連接:Configuring Metro
URL與 bundle 請求
Assets
為了獲取Assets資源,您可以使用require方法來獲取一個js文件,伺服器將根據特定的require請求返回js文件的路徑。當請求Assets資源時通常會原樣返回。
除此之外,伺服器還可以根據平台和請求的大小返回特定的Assets資源。指定平台的方法是通過點後綴(例如.ios)和at後綴(例如@2x)方式來進行的。
Bundle
任何js文件都可以作為bundle來請求根文件,這個文件將被看作是項目的根目錄,根目錄將包含所有遞歸在內的文件。為了請求bundle包,只需將擴展名從.js更改為.bundle即可。構建包的選項有:
dev: 是否以開發模式來構建包。
platform: 平台請求包,可以是ios或android。
minify: 代碼是否應該縮小。
excludeSource: 源碼是否應該包含在源映射中。
例如,請求http://localhost:8081/foo/bar/baz.bundle?dev=true&platform=ios將創建一個foo/bar/baz包,js為iOS開發模式。
Source maps
通過使用與包相同的URL為每個包構建源映射,只有當inlineSourceMap設置為false時才會工作。您傳遞給包的所有選項將被添加到源映射URL;否則,它們就不匹配。
JavaScript transformer
JavaScript transformer被用來進行JS代碼轉換,適用於訪問Babel。這個transformer可以導出兩種方法:
transform(module)
此方法主要用於轉換代碼。接收到的對象將會被轉換為包含一個ast鍵代碼。默認的轉換器僅能完成將代碼解析為AST,以此來完成最低限度的工作:
const babylon = require("@babel/parser");
module.exports.transform = (file: {filename: string, src: string}) => {
const ast = babylon.parse(code, {sourceType: "module"});
return {ast};
};
1
2
3
4
5
6
7
如果您想要使用babel插件,您可以通過將代碼傳遞給它來實現:
const {transformSync} = require("@babel/core");
module.exports.transform = file => {
return transformSync(file.src, {
// Babel options...
});
};
1
2
3
4
5
6
7
getCacheKey()
此方法用於返迴轉換器緩存。當使用不同的轉換器時,這允許正確地將轉換後的文件綁定到轉換它的轉換器,且方法的結果必須是一個字元串。
概念
Metro是一個JavaScript的打包工具。它接收選項、一個條目文件,返回一個包含所有JavaScript的文件。Metro綁定程序主要涉及三個階段:
Resolution
Transformation
Serialization
Resolution
Metro需要從入口點構建所需的所有模塊的圖,要從另一個文件中找到所需的文件,需要使用Metro解析器。在現實開發中,這個階段與Transformation階段是並行的。
Transformation
所有模塊都要經過Transformation階段,Transformation負責將模塊轉換成目標平台可以理解的格式(如React Naitve)。模塊的轉換是基於擁有的核心數量來進行的。
Serialization
所有模塊一經轉換就會被序列化,Serialization會組合這些模塊來生成一個或多個包,包就是將模塊組合成一個JavaScript文件的包。
Modules
Metro被劃分為多個模塊,每個模塊對應於流程中的每個步驟,每個模塊都有自己的職責。這意味著我們每個模塊可以根據您的需要進行交換。
構建
綁定時,每個模塊都會被分配一個數字id,這意味著不支持動態需求。require通過數字版本更改、模塊以不同的格式存儲。支持三種不同的捆綁形式:
Plain bundle
這是一種標準的打包方式,在這種方式中,所有文件都用函數調用包裝,然後添加到全局文件中,這對於只需要JS包(例如瀏覽器)的環境非常有用。只需要具有.bundle擴展名的入口點就可以完成它的構建。
Indexed RAM bundle
這種打包方式會將包打包成二進位文件,其格式包括以下部分:
一組數字:用於驗證文件。uint32必須位於文件的開頭,值為0xFB0BD1E5。
偏移表:該表是一個由32對uint32對組成的序列,帶有一個表頭。
其他子模塊,由一個空位元組( )完成。例如:
` 0 1 2 3 4 5 6
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic number | Header size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Startup code size | Module 0 offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module 0 length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ ... +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Module n offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module n length | Module 0 code | Module 0 code | ... | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module 1 code | Module 1 code | ... | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ ... +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Module n code | Module n code | ... | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+`
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
這種結構對於同時載入內存中所有代碼的環境來說是最優的:
通過使用偏移表,可以在固定的時間內載入任何模塊,其中模塊x的代碼位於文件[(x + 3) * sizeof(uint32)]。由於有一個空字元( )分隔所有模塊,通常不需要使用長度,模塊可以直接作為ASCIIZ字元串載入。
啟動代碼總是可以在文件[sizeof(uint32)]中找到。
Indexed RAM bundle通常被用於iOS分包。
File RAM bundle
每個模塊都會被存儲為一個文件,例如,名稱為js-modules/${id},創建了一個名為UNBUNDLE的額外文件,它唯一的內容是一個數字0xFB0BD1E5。注意,解包文件是在根目錄下創建的。
Android通常使用這種方式分包,因為包內容是壓縮的,而且訪問壓縮文件要快得多。如果使用索引方式(Indexed RAM bundle),則應立即解壓縮所有綁定,以獲取對應模塊的代碼。
緩存
Metro具有多層緩存,您可以設置多個緩存供Metro使用,而不是一個緩存。下面來看看Motro的多層緩存是如何工作的。
為什麼要緩存
緩存提供了很大的性能優勢,它們可以將打包的速度提高十倍以上。然而,許多系統使用的是非持久緩存。對於Metro來說,我們有一種更複雜的層系統緩存方式。例如,我們可以在伺服器上存儲緩存,這樣,連接到同一伺服器的所有打包都可以使用共享緩存。因此,CI伺服器和本地開發的初始構建時間顯著降低。
我們希望將緩存存儲在多個位置,以便緩存可以執行回退操作。這就是為什麼有一個多層緩存系統。
緩存的請求與緩存
在Metro中,系統使用了一個排序機制來決定使用哪個緩存。為了檢索緩存,我們從上到下遍歷緩存,直到找到結果;為了保存緩存,我們同樣遍歷緩存,直到找到具有緩存的存儲。
假設您有兩個緩存存儲:一個在伺服器上,另一個在本地文件系統上。那麼,你可以這樣指定:
const config = {
cacheStores: [
new FileStore({/*opts*/}),
new NetworkStore({/*opts*/})
]
}
1
2
3
4
5
6
當我們檢索緩存時,Metro將首先查看本地文件存儲,如果不能找到緩存,它將檢查NetworkStore。最後,如果沒有緩存,它將生成一個新的緩存。一旦緩存生成,Metro將再次從上到下在所有存儲中存儲緩存。如果找到緩存,也會進行存儲。例如,如果Metro在NetworkStore中找到緩存,它也會將其存儲在FileStore中。
API
API
Methods
metro提供了如下一些方法:
loadConfig()
async runMetro(config)
async runBuild(config, )
async runServer(config, )
createConnectMiddleware(config, )
使用
編譯文件:
const config = await Metro.loadConfig();
await Metro.runBuild(config, {
entry: "index.js",
out: "bundle.js",
});
1
2
3
4
5
6
運行服務並監視文件系統的更改:
const config = await Metro.loadConfig();
await Metro.runServer(config, {
port: 8080,
});
1
2
3
4
5
Reference
下面公開的所有函數都會接受一個附加的配置選項,即metro.config.js,如意要使用它,你可以使用Metro.loadConfig來獲得它。
loadConfig()
Basic options: config, cwd
載入Metro配置,如果指定,可以從選項中的config載入,也可以從cwd到根目錄遍歷直到找到一個文件(默認metro.config.js)。返回的配置將與Metro的默認值合併。
async runMetro(config)
基於配置創建一個Metro伺服器並返回它,您可以將其用作現有伺服器中的中間件。
async runBuild(config, )
綁定給定平台的條目,並將其保存到外部位置。如果設置了sourceMap,還會生成一個源映射。源映射將被內聯,除非還定義了sourceMapUrl。在後一種情況下,將使用sourceMapUrl參數的basename生成一個新文件。
async runServer(config, )
啟動一個完整的Metro HTTP服務,它將偵聽指定的host:port,然後可以查詢它以檢索各種入口點的包。如果提供了安全系列選項,伺服器將通過HTTPS公開。如果設置了hmrEnabled,伺服器還將公開websocket伺服器內容,並將HMR客戶機注入生成的包中。
createConnectMiddleware(config, )
與其創建完整的伺服器不同,此函數用於創建一個連接中間件來響應包請求。然後可以將此中間件插入您自己的伺服器,埠參數是可選的,僅用於日誌記錄。
Metro配置
Metro配置可以通過以下三種方式創建:
metro.config.js
metro.config.json
The metro field in package.json
您還可以通過調用CLI時指定自定義文件,文件的格式為:
--config <path/to/config>
1
結構
每個模塊都有一個單獨的配置選項,Metro中常見的配置結構如下:
module.exports = {
resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
}
/* general options */
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可用的選項參數可以參考下面的鏈接:General Options
Option Type Description
cacheStores Array<CacheStore<TransformResult<>> 列出存儲緩存的位置
cacheVersion string 將使整個metro緩存生成一個鍵
projectRoot string 項目的根文件
watchFolders Array < string> 指定任何額外的監視文件夾
transformerPath string 轉換器路徑
watch boolean 是否監視所有文件
reporter {update: () => void} 是否監視打包過程中的狀態
resetCache boolean 是否在啟動構建時重置緩存
stickyWorkers boolean 創建的worker是否應該基於文件名
maxWorkers number 把序列化的包串聯起來
伺服器選項可以參考下面的鏈接:Server Options
Option Type Description
port number 監聽的埠
useGlobalHotkey boolean 是否打開熱更新快捷鍵,快捷鍵為CMD+R
enhanceMiddleware (Middleware, Server) => Middleware 添加自定義中間件
enableVisualizer boolean 啟用metro-visualizer中間件
Metro的轉化器選項如下:Transformer Options
Option Type Description
asyncRequireModulePath string 處理非同步請求模塊
babelTransformerPath string 使用自定義babel轉換器
dynamicDepsInPackages string (throwAtRuntime or reject) 發現動態依賴的處理動作
enableBabelRCLookup boolean (default: true) 是否使用.babelrc配置文件
enableBabelRuntime boolean (default: true) 是否使用@babel/transform/runtime插件
enableBabelRuntime boolean (default: true) 是否使用.babelrc配置文件
未完待續!!!!!
---------------------
作者:code_xzh
原文:https://blog.csdn.net/xiangzhihong8/article/details/85054279
打開今日頭條,查看更多圖片※linux內核設計與實現筆記
※為什麼去中心化存儲也能保證數據不丟失
TAG:程序員小新人學習 |