日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

淺析 Parcel 的 Rust 打包算法 Demo

來源: 責編: 時間:2023-08-09 23:02:42 445觀看
導讀Parcel 是一個類似于 Webpack 、Rollup 的構建工具,相較于這一類構建工具,Parcel 主打的賣點是零配置并開箱即用,雖然某種程度上這種零配置的方式會使得項目定制化變得很困難,但 Parcel 盡量提供了一套自身的構建最佳實踐

Parcel 是一個類似于 Webpack 、Rollup 的構建工具,相較于這一類構建工具,Parcel 主打的賣點是零配置并開箱即用,雖然某種程度上這種零配置的方式會使得項目定制化變得很困難,但 Parcel 盡量提供了一套自身的構建最佳實踐,以后有機會去單獨介紹一下 Parcel 的整體構造,這里不展開講解了。R8128資訊網——每日最新資訊28at.com

Parcel 在 2.8.0 的更新中提到使用了一個新的打包算法,相比較于之前速度提升了 2.7 倍,并且體積還減小了 2.5 倍。同時還有其他的比較夸張的性能提升,例如 6 倍的熱更新速度,增量構建的再次構建性能提升了10倍。R8128資訊網——每日最新資訊28at.com

圖片R8128資訊網——每日最新資訊28at.com

同時作者強調該算法是由來自 Atlassian 的團隊貢獻的,他們為此花了大約一年的時間使得其在 parcel v2.8.0 中成為默認的打包算法,該算法帶來了巨大的性能提升,并且通過更少的重復包以及更好的瀏覽器緩存來有效減少了包產物的體積:R8128資訊網——每日最新資訊28at.com

This results in both smaller bundles and much faster builds. *For a very large real-world project with over 60,000 assets, overall build time was reduced from over 25 minutes to 9 minutes (2.7x faster). The total bundle size for the whole project went from 952 MB *to 370 MB (2.5x smaller). For comparison, building the same app with webpack takes over 45 minutes.R8128資訊網——每日最新資訊28at.com

此處測試項目根據 twitter 信息為 jira。R8128資訊網——每日最新資訊28at.com

實際上這個算法在 landing 過程中,它是基于 Parcel 作者 Devon Govett 本身寫的一個打包算法原型,并對這個算法本身在 Parcel 的實際場景中做了一些優化。具體可以參考這個 PR: https://github.com/parcel-bundler/parcel/pull/6975 的一些內容:R8128資訊網——每日最新資訊28at.com

圖片R8128資訊網——每日最新資訊28at.com

在這篇文章中,我們先暫時不分析 Parcel 目前的具體的打包策略以及代碼邏輯,而是結合這個原型倉庫來了解一下 Parcel 最原始的打包算法的運行思路:R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

相比較于 Parcel 本身的打包算法,這個原型 Demo 可以要更簡單一點(不過由于代碼是 Rust,理解難度對筆者來說也沒有很簡單),在之后的文章中,我會單獨結合 Parcel 目前本身的打包算法(Default Bundlers)來做一下講解。R8128資訊網——每日最新資訊28at.com

Rust 算法 demo

我們可以根據上面 PR 中找到對應的算法原型倉庫,這個倉庫的地址是: https://github.com/devongovett/bundler-algorithm, 不過由于倉庫的一些內容已經實際落地了,因此作者后面將這個倉庫給歸檔了。R8128資訊網——每日最新資訊28at.com

這個倉庫是基于 rust 寫的,不過整體流程上而言并不是特別復雜,我們可以簡單調試一下(本地需要有 rust 環境):R8128資訊網——每日最新資訊28at.com

git clone https://github.com/devongovett/bundler-algorithmcd bundler-algorithm# 使用 nightly 版本的 cargorustup install nightly && rustup default nightly# 運行 democargo run

根據該倉庫源碼,我們可以將一次打包算法執行流程分為下面幾個步驟,注意以下步驟中的具體代碼存在一些細節省略的情況,如果你想了解更多的算法細節,可以參考倉庫源碼來進行閱讀。R8128資訊網——每日最新資訊28at.com

構建依賴圖

首先在 main.rs 文件中的第一步,會先根據項目各文件之間的引用關系構建一個依賴圖出來。R8128資訊網——每日最新資訊28at.com

這里直接參考 build_graph 這個方法,例如這里項目中存在 html 、js 等不同類型的文件,首先會將這些資源文件作為節點添加到一個圖中,然后根據這些文件之間的引用關系去給對應的節點添加對應的邊,這樣就會形成一個比較初步完善的依賴圖。R8128資訊網——每日最新資訊28at.com

// 創建 graph 并獲取對應的 entries 對象let (g, entries) = build_graph();fn build_graph<'a>() -> (Graph<Asset<'a>, Dependency>, Vec<NodeIndex>) {  let mut g = Graph::new();  let mut entries = Vec::new();  let html = g.add_node(Asset {    name: "a.html",    asset_type: AssetType::HTML,    size: 10  });    let js = g.add_node(Asset {    name: "a.js",    asset_type: AssetType::JavaScript,    size: 10  });  // ...一些資源的初始化過程,這里節省篇幅跳過...  g.add_edge(html, js, Dependency {    is_async: false  });    entries.push(html);  entries.push(html2);  return (g, entries);}

由于 build_graph 方法中代碼邏輯都比較單一,因此上面貼的源碼中有一些重復邏輯例如資源的初始化等的省略。R8128資訊網——每日最新資訊28at.com

最后這個方法會構建出一個如下圖所示的依賴圖出來,注意下面還有一些依賴之間的引用關系是異步的(這里可以理解為動態導入),同時這里我們給每個靜態資源都標注一個唯一的 asset_id,下文圖中標注的序號均與此處對應:R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

其中 Parcel 會默認將 Html 資源作為依賴圖的入口文件,因此我們可以看到這張圖是存在兩個入口的,分別為 a.html 和 b.html 。R8128資訊網——每日最新資訊28at.com

遍歷圖創建獨立的 bundle

這里的 bundle 可以簡單理解為最終 打包 輸出的文件,這個可以結合 Webpack 里面的一些概念來理解。R8128資訊網——每日最新資訊28at.com

在圖創建完成之后,這一步主要目的則是根據 graph 提供的一些信息(例如 entry、資源之間的依賴關系等)去創建出最后打包出來的 bundle 類型,這里首先會對下面三種情況創建一個單獨的 bundle:R8128資訊網——每日最新資訊28at.com

  • 入口文件,例如這里的 html 入口文件
  • 不同類型資源之間的引用,例如當 js 文件中引用到圖片資源的時候
  • 資源之間存在 異步 引用時,例如某個 js 動態導入另外的 js 文件

下面我將根據這幾種情況來展開講講:R8128資訊網——每日最新資訊28at.com

首先是根據入口文件去創建 bundle,這里邏輯很簡單,遍歷一下前面 build_graph 方法生成的 entries 數組(實際上這里是個 Rust 的 Vec ),然后往 bundle_roots 中插入對應的 entry 以及 bundle_id 。R8128資訊網——每日最新資訊28at.com

這里的 bundle_id 對應前面 graph 圖中標記的序列號,例如 a.html 是 0,b.html 是 1。具體的實現參考以下邏輯:R8128資訊網——每日最新資訊28at.com

let mut bundle_roots = HashMap:new();let mut reacheable_bundles = HashSet::new();let mut bundle_graph = Graph::new();// 遍歷 entries,往 bundle_roots 中插入對應的 entry 信息以及對應的 bundle_idfor entry in &entries {  let bundle_id = bundle_graph.add_node(Bundle::from_asset(*entry, &g[*entry]));  bundle_roots.insert(*entry, (bundle_id, bundle_id));}

這里應用到實際開發中的場景可以聯想到我們開發一個單頁(SPA) 或者多頁應用(MPA)時,在打包產物中通常會出現一個或者多個 html 入口文件,這里的情況也是類似的。R8128資訊網——每日最新資訊28at.com

添加完 html 入口文件之后,接下來就會用深度優先搜索算法(DFS)去遍歷整個圖,對以下兩種情況再生成單獨的 bundle:R8128資訊網——每日最新資訊28at.com

  • 不同類型資源之間的引用,對被引用的資源創建一個 bundle,例如 a.js 中引用到了 style.css 那么 style.css 會被處理成一個單獨的 bundle
  • 資源之間存在 異步 引用時,對被引用的資源創建一個 bundle,例如 a.js 是異步引用的 async.js ,那么 async.js 會被處理成一個單獨的 bundle

以下為遍歷圖的主要代碼邏輯,具體可以參考 DfsEvent::TreeEdge(u, v) ,這里是遍歷圖中的各個相連子節點:R8128資訊網——每日最新資訊28at.com

let mut stack = LinkedList::new();depth_first_search(&g, entries, |event| {  match event {    // ...    DfsEvent::TreeEdge(u, v) => {      let asset_a = &g[u];      let asset_b = &g[v];      // 當資源類型發生變化的時候,創建一個新的 bundle      if asset_a.asset_type != asset_b.asset_type {        // 舉個例子,這里 a.js -> style.css        // 這里 bundle_group_id 是 a.html,asset_b 是 style.css        // style.css 是不同類型的資源被引用,會被拆單獨的 bundle 出來        let (_, bundle_group_id) = stack.front().unwrap();        let bundle_id = bundle_graph.add_node(Bundle::from_asset(v, asset_b));        bundle_roots.insert(v, (bundle_id, *bundle_group_id));        bundle_graph.add_edge(*bundle_group_id, bundle_id, 0);        return      }            // 當存在異步依賴的時候,創建一個新的 bundle      let dependency = &g[g.find_edge(u, v).unwrap()];      // 舉個例子,這里 a.js -> async.js 是異步依賴導入      if dependency.is_async {        // 因此這里 async.js(這里的 asset_b) 會被處理成一個單獨的 bundle        let bundle_id = bundle_graph.add_node(Bundle::from_asset(v, asset_b));        bundle_roots.insert(v, (bundle_id, bundle_id));        for (b, _) in &stack {            let a = &g[*b];            if a.asset_type != asset_b.asset_type {              break            }            reachable_bundles.insert((*b, v));          }      }    }    // ...  }});

在經歷過上面兩次操作之后,我們最開始的 Graph 就會創建以下幾個單獨的 bundle 出來,對應的文件分別為:R8128資訊網——每日最新資訊28at.com

  • 作為入口文件的 a.html 和 b.html 各自形成一個 bundle
  • 被 html 文件引用的 a.js 和 b.js 以及被 js 文件引用的 style.css 各自形成一個 bundle
  • 被異步引用的 async.js 文件形成一個 bundle

并且這些 bundle 之間還存在一定的依賴關系(這里的序號為最初始依賴圖中的序號):R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

上面 async2.js 這個異步的 js 資源沒有形成單獨的 bundle 原因在于其是被其它 js 資源同步引入的。R8128資訊網——每日最新資訊28at.com

處理所有資源到對應 bundle

在上一接中利用初始化的依賴圖分析出了最初的幾個獨立的 bundle,當然還剩下一些其它的資源還沒處理,例如 async2.js 和 shared.js 這兩個 asset。R8128資訊網——每日最新資訊28at.com

因此這一步的操作就是將這些還沒有處理成 bundle 的 assets 全部都處理到 bundles 中去,同時整合一下上一步遍歷出來的獨立 bundle。R8128資訊網——每日最新資訊28at.com

首先這一步會根據前面的到的獨立 bundle 去建立一個可訪問的節點,這里會用輪詢上一節生成的獨立 bundle,也就是這里的 bundle_roots ,同時根據每個 bundle_root 在最原始的依賴圖中進行一次 DFS 查找,然后剪枝掉一些同樣是 bundle_root 的 bundle,把剩余不是 asset 的添加到可訪問節點中來,實現參考如下:R8128資訊網——每日最新資訊28at.com

let mut reachable_nodes = HashSet::new();for (root, _) in &bundle_roots {  depth_first_search(&g, Some(*root), |event| {    if let DfsEvent::Discover(n, _) = &event {      if n == root {        return Control::Continue      }      // 如果命中了 bundle_root 就跳過      if bundle_roots.contains_key(&n) {        return Control::<()>::Prune;      }      // 否則就添加到可訪問節點中來      reachable_nodes.insert((*root, *n));    }      Control::Continue    });}

舉個例子來說,例如 a.js 是個單獨的 bundle,但他同步引用了一個同類型的 async2.js ,同時 async2.js 又引用了一個 shared.js 。R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

那么這里就會形成一個以a.js為根節點,分別到 async2.js 和 shared.js 的兩個可訪問節點。R8128資訊網——每日最新資訊28at.com

同理,在上圖中我們還能找到這樣的一些訪問節點:R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

拿到這些可訪問節點以后,這里會像最開始的的時候,根據這些節點再去形成一個可訪問節點的依賴圖,這個圖對比最初的依賴圖會小一點,這個依賴圖的主要作用是為了幫助后續決定具體哪些 asset 被放到哪個 bundle 中去,這個過程可以和 Webpack 的拆包策略一起理解。R8128資訊網——每日最新資訊28at.com

// 根據拿到的可訪問的節點,構造一個 graph 出來let reachable_graph = Graph::<(), ()>::from_edges(&reachable_nodes);

這里的依賴圖實際上就是個去除一些 root_bundle 后的縮減版的依賴圖:R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

在完成可訪問節點依賴圖的構建之后,下一步開始利用這個依賴圖將所有的 asset 都處理進 bundle 中去。其中這里每個 asset 都會基于其和可訪問的 bundle_roots 之間的關系被放到單獨 bundle 中去,這么做的目的是為先創建盡可能沒有重復代碼的 bundle graph。R8128資訊網——每日最新資訊28at.com

// 用于存儲 entry asset id 到 bundle id 的映射let mut bundles: HashMap<Vec<NodeIndex>, NodeIndex> = HashMap::new();for asset_id in g.node_indices() {  let reachable: Vec<NodeIndex> = reachable_graph.neighbors_directed(asset_id, Incoming).collect();  let reachable: Vec<NodeIndex> = reachable.iter().cloned().filter(|b| {      (&reachable).into_iter().all(|a| !reachable_bundles.contains(&(*a, *b)))  }).collect();    // 根據上面的每個 asset 對應的可訪問的 bundle 去生成一個 bundle_graph  if let Some((bundle_id, _)) = bundle_roots.get(&asset_id) {    // 如果 asset 是個 bundle_root,那么會給這個 bundle_root 在 bundle_graph 添加上對應的節點    bundles.entry(vec![asset_id]).or_insert(*bundle_id);    for a in &reachable {      if *a != asset_id {          bundle_graph.add_edge(bundle_roots[a].1, *bundle_id, 0);       }    }  } else if reachable.len() > 0 {    // 如果 asset 對于多個 bundle 都是可訪問的    // 例如 shared.js(6) 就同時被 a.js(2) 和 b.js(5) 可訪問    // 這里可以根據這些 asset 組合為該 asset 創建一個新的 bundle   }}

這一步的代碼邏輯會很復雜(從 rust 代碼的角度來看,畢竟筆者不是很懂 rust QAQ),因此這里筆者并沒有貼完所有的代碼。但我這里會結合具體的代碼來舉例講解一下這一步的具體操作:R8128資訊網——每日最新資訊28at.com

首先這一步會遍歷所有的 asset ,找到那些目前還沒有形成 bundle 的 asset,如下圖所示,經歷過前一個步驟之后,沒有形成 root bundle 的 asset 的只有 async2.js 以及 shared.js 。R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

這里針對 async2.js 這個 asset 來講解一下,對于 async2.js 來說,對它可訪問的 bundle 有 a.js (2)、async.js (3)。這里由于 async2.js 對于 async.js 的父 bundle a.js 同樣是可訪問的,這里在處理可訪問節點的時候會把 async.js 這個 asset 給移除掉,相當于 async2.js 實際可訪問的 bundle 只有 a.js ,這里這樣處理的作用是為了后續 asset 的 bundle 合并操作,可以參考如下代碼:R8128資訊網——每日最新資訊28at.com

for asset_id in g.node_indices() {  let reachable: Vec<NodeIndex> = reachable_graph.neighbors_directed(asset_id, Incoming).collect();  // 這一步篩掉 async2.js  let reachable: Vec<NodeIndex> = reachable.iter().cloned().filter(|b| {      (&reachable).into_iter().all(|a| !reachable_bundles.contains(&(*a, *b)))  }).collect();}

在這一步篩出對應可訪問的 bundle 之后(reachable),接下來就開始去繼續構造 bundle_graph ,這上一步中 bundle_graph 中先暫時放置了一些 root_bundle 以及他們之間的引用關系。在這一步,對于沒有形成 bundle 的 asset 進行繼續的完善。R8128資訊網——每日最新資訊28at.com

這一步代碼比較復雜,筆者并沒有完全貼出來,大概處理過程分成了兩個分支:R8128資訊網——每日最新資訊28at.com

  • 處理成 root_bundle 的 bundle,需要將他們有依賴關系的 bundle_graph 添加上對應的邊
  • 處理 root_bundle 之外的 asset 為 bundle(async2.js 和 shared.js)
  • 如果這個 asset 被多個 root_bundle 依賴(可訪問),那么會給這個 asset 創建一個單獨的 bundle 并給它在 bundle_graph 加上對應的邊,例如這里的 shared.js
  • 如果這個 asset 只被一個 root_bundle 依賴(可訪問),那么會直接把這個 asset 添加到 root_bundle 的 bundle 中組成一個新的 bundle,例如這里的 async2.js
for asset_id in g.node_indices() {   // ... 省略掉獲取 reacheable 這個變量過程   if let Some((bundle_id, _)) = bundle_roots.get(&asset_id) {     // 1. 處理 root_bundle,這一步可以理解為給 bundle_graph 添加邊     bundles.entry(vec![asset_id]).or_insert(*bundle_id);      for a in &reachable {        if *a != asset_id {          bundle_graph.add_edge(bundle_roots[a].1, *bundle_id, 0);        }      }   } else if reachable.len() > 0 {     // 2. 處理 root_bundle 之外的 asset 為 bundle     let source_bundles = reachable.iter().map(|a| bundles[&vec![*a]]).collect();            let bundle_id = bundles.entry(reachable.clone()).or_insert_with(|| {        let mut bundle = Bundle::default();        bundle.source_bundles = source_bundles;        bundle_graph.add_node(bundle)      });      let bundle = &mut bundle_graph[*bundle_id];      bundle.asset_ids.push(asset_id);      bundle.size += g[asset_id].size;      // 處理完 asset 為 bundle 之后,同樣要添加邊      for a in reachable {        if a != *bundle_id {          bundle_graph.add_edge(bundle_roots[&a].1, *bundle_id, 0);        }      }    }   }}

這一步最后會形成一個這樣的 bundle_graph ,這里對每個 bundle 進行了重新標號,不同于之前的 asset_idR8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

這里我們可以看到, 其中 async2.js 和 root_bundle a.js 形成了一個新的 bundle,而另外一個 asset shared.js 也形成了一個單獨的 bundle,它們之間的依賴關系(graph 的邊)也能比較清晰的看到。R8128資訊網——每日最新資訊28at.com

合并小的公共 bundle

上一步我們基本上將所有的 asset 都全處理成了 bundle 并成功構建出了一個 bundle_graph ,實際上現在構建大頭基本上已經完成了。R8128資訊網——每日最新資訊28at.com

下面的步驟基本就是對 bundle_graph 做一個優化,這一步的處理對比前面的步驟就很簡單了。R8128資訊網——每日最新資訊28at.com

在開始介紹代碼之前,這一步我們可以理解為 webpack 的 chunkSplit 配置中的 splitChunks.minSize 。在 Parcel 也有對應的配置可以參考,這里的 minBundleSize:R8128資訊網——每日最新資訊28at.com

{  "@parcel/bundler-default": {    "minBundles": 1,    "minBundleSize": 3000,    "maxParallelRequests": 20  }}

大致就是對小于 minBundleSize 的 shared_bundle 合并到對應的可訪問 bundle 中去,這里的副作用是可能會導致在多個 bundle 中存在一定體積的重復 asset 體積(重復代碼),但帶來的好處則是可以減少請求數量,這里具體的取舍開始看用戶自身的配置。R8128資訊網——每日最新資訊28at.com

這一步的代碼處理如下:R8128資訊網——每日最新資訊28at.com

for bundle_id in bundle_graph.node_indices() {    let bundle = &bundle_graph[bundle_id];    // 當這個 bundle 本身為 commen bundle 且他本身的體積小于某個值,這里的 demo 默認寫為了 10    // 那么則可以合并掉這個 bundle    if bundle.source_bundles.len() > 0 && bundle.size < 10 {      remove_bundle(&g, &mut bundle_graph, bundle_id);    }  }    fn remove_bundle(    asset_graph: &Graph<Asset, Dependency>,    bundle_graph: &mut Graph<Bundle, i32>,    bundle_id: NodeIndex  ) {    // 在 bundle_graph 中刪除掉對應的 bundle    let bundle = bundle_graph.remove_node(bundle_id).unwrap();    // 并將該 bundle 合并對其有引用關系的 bundle 中去    for asset_id in &bundle.asset_ids {      for source_bundle_id in &bundle.source_bundles {        let bundle = &mut bundle_graph[*source_bundle_id];        bundle.asset_ids.push(*asset_id);        bundle.size += asset_graph[*asset_id].size;    }  }

合并超出并行請求限制的公共 bundle

在上一步處理完了最小體積的公共 bundle,這一步要處理的則是最大并行請求的 chunk,舉個例子,例如這里拆出來了 a.js 、common-a.js 、common-b.js 這三個 bundle,并且 a.js 同時依賴 common-a.js 和 common-b.js,但現在用戶配置允許的最大 bundle 并行請求數目是 2,那么這里就會合并掉這兩個 common bundle 中的其中一個到 a.js 中去。R8128資訊網——每日最新資訊28at.com

這里在 Parcel 中也有對應的配置項,參考下面的 maxParallelRequests 這個參數:R8128資訊網——每日最新資訊28at.com

{  "@parcel/bundler-default": {    "minBundles": 1,    "minBundleSize": 3000,    "maxParallelRequests": 20  }}

這一步的代碼處理可以參考如下:R8128資訊網——每日最新資訊28at.com

// demo 這里默認的限制請求數量為 3let limit = 3;for (_, (bundle_id, bundle_group_id)) in bundle_roots {    if bundle_id != bundle_group_id {      continue;    }    let mut neighbors: Vec<NodeIndex> = bundle_graph.neighbors(bundle_group_id).collect();    if neighbors.len() > limit {      neighbors.sort_by(|a, b| bundle_graph[*a].size.cmp(&bundle_graph[*b].size));      // Remove bundles until the bundle group is within the parallel request limit.      for bundle_id in &neighbors[0..neighbors.len() - limit] {        // Add all assets in the shared bundle into the source bundles that are within this bundle group.        let source_bundles: Vec<NodeIndex> = bundle_graph[*bundle_id].source_bundles.drain_filter(|s| neighbors.contains(s)).collect();        for source in source_bundles {          for asset_id in bundle_graph[*bundle_id].asset_ids.clone() {            let bundle_id = bundles[&vec![source]];            let bundle = &mut bundle_graph[bundle_id];            bundle.asset_ids.push(asset_id);            bundle.size += g[asset_id].size;          }        }        // Remove the edge from this bundle group to the shared bundle.        bundle_graph.remove_edge(bundle_graph.find_edge(bundle_group_id, *bundle_id).unwrap());        // If there is now only a single bundle group that contains this bundle,        // merge it into the remaining source bundles. If it is orphaned entirely, remove it.        let count = bundle_graph.neighbors_directed(*bundle_id, Incoming).count();        if count == 1 {          remove_bundle(&g, &mut bundle_graph, *bundle_id);        } else if count == 0 {          bundle_graph.remove_node(*bundle_id);        }      }    }  }

這里簡單對代碼邏輯做個講解,其實這里的邏輯也比較好理解:R8128資訊網——每日最新資訊28at.com

  • 先找到對應的 root_bundle(即這里的 bundle_group_id ),因為一般只有 root_bundle 會有多個 bundle 并行請求。
  • 找出包括 root_bundle (不包括 root_bundle)依賴的所有 bundle(即 neighbors 變量),如果這個數目大于這里設置的限制(即 limit),按照neighbors 中的所有 bundle 體積大小,把體積小的 bundle 都合并到其對應的 source_bundles 中去(這里不一定只有 root_bundle,還可能會有其他的 bundle)。一直合并到這些 bundle 的數目小于請求限制即可。
  • 合并到最后,如果只剩下唯一一個 bundle 了,那么直接把這個 bundle 也給合并進剩下的 source_bundle 中去。

總結

在走完最后兩步的 bundle 合并之后,那么整個 Rust Demo 的打包算法流程就結束了,實際上這個 Demo 給的資源樣例并沒有走最后的兩步:R8128資訊網——每日最新資訊28at.com

圖片圖片R8128資訊網——每日最新資訊28at.com

按照最后的 bundle_graph 生成的圖我們可以看到:R8128資訊網——每日最新資訊28at.com

  • 最小的 common_bundle 體積也要大于 10
  • 最大的并行請求數目也并沒有超過 3 個(圖中的幾個 bundle 之間還有依賴關系,這里并沒有標注出來)

實際上最后生成的整體 bundle 也大概如圖所示。R8128資訊網——每日最新資訊28at.com

不過這整個流程由于筆者對于 Rust 代碼并不是很熟悉,因此中間可能會有些疏漏,不過整體流程來看應該并沒有特別大的出入。R8128資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-5144-0.html淺析 Parcel 的 Rust 打包算法 Demo

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 字節客戶端也瘋狂拷打基礎!

下一篇: Golang 中的 IO 包詳解:單字節操作接口

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
国产欧美日韩三区| 免费看成人av| 亚洲午夜精品一区二区| 亚洲一区二区三区免费观看| 午夜国产精品视频| 久久久久久**毛片大全| 欧美国产先锋| 国产精品一区二区女厕厕| 国内成人精品视频| 亚洲精品免费一二三区| 亚洲视频导航| 久久久国产成人精品| 欧美国产精品v| 国产精品美女在线| 国产一区久久| 亚洲免费观看高清完整版在线观看熊| 亚洲一区二区精品| 久久久综合激的五月天| 欧美人与性动交a欧美精品| 国产乱码精品一区二区三区av| 亚洲福利视频在线| 亚洲深夜福利视频| 麻豆精品在线观看| 国产精品视频内| 91久久综合| 午夜电影亚洲| 欧美精品在线视频观看| 国产亚洲综合在线| 夜夜爽99久久国产综合精品女不卡 | 欧美国产欧美亚洲国产日韩mv天天看完整 | 在线欧美不卡| 亚洲午夜国产一区99re久久| 久久琪琪电影院| 国产精品成人免费| 亚洲黄色尤物视频| 午夜精品久久99蜜桃的功能介绍| 蜜桃av一区二区三区| 国产精品一区二区你懂得| 亚洲日本中文字幕| 久久蜜桃资源一区二区老牛| 国产精品久久999| 亚洲精品视频啊美女在线直播| 欧美一区二区三区婷婷月色| 欧美日产一区二区三区在线观看| 国产一区二区三区在线观看免费| 国产精品99久久99久久久二8 | 国产精品天天看| 亚洲美女性视频| 麻豆精品网站| 国产一区二区三区奇米久涩| 亚洲一卡久久| 欧美日韩一本到| 亚洲精品极品| 麻豆精品在线视频| 国产综合自拍| 欧美在线播放| 国产精品视频一二三| 一区二区高清视频在线观看| 欧美 亚欧 日韩视频在线| 国内精品伊人久久久久av影院| 亚洲影院免费| 欧美午夜在线一二页| 亚洲精品女av网站| 美女主播一区| 怡红院精品视频在线观看极品| 欧美一区日本一区韩国一区| 国产精品美女久久久久久2018| 一本一道久久综合狠狠老精东影业 | 久久久久久久性| 国产欧美一区二区三区久久| 亚洲伊人第一页| 欧美三级日本三级少妇99| 亚洲精品人人| 欧美精品97| 亚洲美女视频| 欧美三级特黄| 国产精品99久久久久久久女警 | 亚洲一区二区三区中文字幕| 欧美视频日韩视频| 一区二区三区www| 欧美日韩一区高清| 一区二区三区偷拍| 欧美日韩中文另类| 一区二区三区四区蜜桃| 欧美午夜精品理论片a级大开眼界| 日韩视频一区二区三区在线播放免费观看 | 亚洲精品久久久蜜桃| 欧美黄色大片网站| 亚洲美女少妇无套啪啪呻吟| 欧美人与性动交cc0o| 一本到12不卡视频在线dvd| 欧美日韩综合视频| 亚洲欧美美女| 国产日韩欧美一区二区| 久久国产婷婷国产香蕉| 国内外成人在线视频| 久久久久久9999| 亚洲春色另类小说| 欧美韩日亚洲| 中日韩美女免费视频网址在线观看 | 99国产精品久久| 欧美日韩在线观看一区二区| 亚洲亚洲精品三区日韩精品在线视频 | 国产精品草草| 先锋资源久久| 伊人久久大香线| 欧美极品aⅴ影院| 在线视频你懂得一区二区三区| 国产精品一区二区三区久久| 久久久久免费视频| 亚洲精品在线视频| 国产精品区一区二区三区| 久久久777| 亚洲乱码日产精品bd| 国产精品成人在线| 久久精品道一区二区三区| 亚洲国产日韩欧美在线动漫| 欧美三级在线播放| 久久精品国产成人| 亚洲三级性片| 国产精品一区二区三区免费观看 | 欧美福利影院| 亚洲欧美日韩在线观看a三区| 激情伊人五月天久久综合| 欧美精品v日韩精品v国产精品| 亚洲欧美另类国产| 一区视频在线| 欧美性大战久久久久| 久久er99精品| 亚洲免费电影在线观看| 国产喷白浆一区二区三区| 欧美成人精品在线观看| 亚洲男人第一网站| 91久久精品久久国产性色也91 | 亚洲最新视频在线播放| 国产一区二区看久久| 欧美精品午夜| 久久不射电影网| 99天天综合性| 尤物yw午夜国产精品视频| 欧美亚韩一区| 美女主播精品视频一二三四| 亚洲欧美日韩天堂| 亚洲精品系列| 国内精品模特av私拍在线观看| 欧美日韩三级| 美女免费视频一区| 香蕉乱码成人久久天堂爱免费| 亚洲片区在线| 韩国精品一区二区三区| 欧美午夜一区二区福利视频| 久久一区二区三区四区| 亚洲欧美不卡| 日韩一二三在线视频播| 激情欧美国产欧美| 国产精品永久免费视频| 欧美日韩另类字幕中文| 老司机精品视频一区二区三区| 亚洲欧美日韩国产中文| 日韩视频免费观看| 一区二区视频欧美| 国产美女扒开尿口久久久| 欧美精选一区| 另类国产ts人妖高潮视频| 欧美一级视频精品观看| 亚洲午夜一区二区| 日韩视频中文字幕| 亚洲国产天堂久久综合网| 国外精品视频| 国产麻豆成人精品| 欧美亚洲成人精品| 欧美日韩国产亚洲一区| 免费在线看成人av| 久久久久免费视频| 久久精品水蜜桃av综合天堂| 亚洲在线一区| 中日韩视频在线观看| 亚洲欧洲日本一区二区三区| 精品69视频一区二区三区| 国产欧美精品日韩精品| 国产精品久线观看视频| 欧美午夜剧场| 欧美天天影院| 欧美日韩一区二区视频在线| 欧美激情精品久久久久久| 免费观看成人| 老司机精品导航| 久久日韩粉嫩一区二区三区| 久久精品日韩| 久久国产主播精品| 香港久久久电影| 香港成人在线视频| 午夜精品一区二区三区在线| 亚洲一区三区视频在线观看| 中日韩视频在线观看| 亚洲视频在线免费观看| 一区二区三区视频在线观看| 99国产精品99久久久久久| 9色精品在线| 艳妇臀荡乳欲伦亚洲一区| 亚洲深夜影院| 亚洲免费人成在线视频观看| 亚洲欧美日韩国产一区二区三区| 午夜精品久久久久久久99樱桃|