国产精品嫩草99av在线_一区在线视频观看_欧美高清一区_欧美 日韩 国产 一区_99精品欧美一区二区三区_久久大香伊蕉在人线观看热2_一色屋精品视频在线观看网站_在线亚洲国产精品网站_亚洲区一区二区三区_你懂的视频一区二区

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

淺析 Parcel 的 Rust 打包算法 Demo

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

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

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

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

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

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

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

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

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

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

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

Rust 算法 demo

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

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

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

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

構建依賴圖

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

這里直接參考 build_graph 這個方法,例如這里項目中存在 html 、js 等不同類型的文件,首先會將這些資源文件作為節點添加到一個圖中,然后根據這些文件之間的引用關系去給對應的節點添加對應的邊,這樣就會形成一個比較初步完善的依賴圖。ZEO28資訊網——每日最新資訊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 方法中代碼邏輯都比較單一,因此上面貼的源碼中有一些重復邏輯例如資源的初始化等的省略。ZEO28資訊網——每日最新資訊28at.com

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

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

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

遍歷圖創建獨立的 bundle

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

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

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

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

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

這里的 bundle_id 對應前面 graph 圖中標記的序列號,例如 a.html 是 0,b.html 是 1。具體的實現參考以下邏輯:ZEO28資訊網——每日最新資訊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 入口文件,這里的情況也是類似的。ZEO28資訊網——每日最新資訊28at.com

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

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

以下為遍歷圖的主要代碼邏輯,具體可以參考 DfsEvent::TreeEdge(u, v) ,這里是遍歷圖中的各個相連子節點:ZEO28資訊網——每日最新資訊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 出來,對應的文件分別為:ZEO28資訊網——每日最新資訊28at.com

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

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

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

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

處理所有資源到對應 bundle

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

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

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

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

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

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

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

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

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

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

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

在完成可訪問節點依賴圖的構建之后,下一步開始利用這個依賴圖將所有的 asset 都處理進 bundle 中去。其中這里每個 asset 都會基于其和可訪問的 bundle_roots 之間的關系被放到單獨 bundle 中去,這么做的目的是為先創建盡可能沒有重復代碼的 bundle graph。ZEO28資訊網——每日最新資訊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),因此這里筆者并沒有貼完所有的代碼。但我這里會結合具體的代碼來舉例講解一下這一步的具體操作:ZEO28資訊網——每日最新資訊28at.com

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

圖片圖片ZEO28資訊網——每日最新資訊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 合并操作,可以參考如下代碼:ZEO28資訊網——每日最新資訊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 進行繼續的完善。ZEO28資訊網——每日最新資訊28at.com

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

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

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

合并小的公共 bundle

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

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

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

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

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

這一步的代碼處理如下:ZEO28資訊網——每日最新資訊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 中去。ZEO28資訊網——每日最新資訊28at.com

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

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

這一步的代碼處理可以參考如下:ZEO28資訊網——每日最新資訊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);        }      }    }  }

這里簡單對代碼邏輯做個講解,其實這里的邏輯也比較好理解:ZEO28資訊網——每日最新資訊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 給的資源樣例并沒有走最后的兩步:ZEO28資訊網——每日最新資訊28at.com

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

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

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

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

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

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

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

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

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

標簽:
  • 熱門焦點
Top 国产精品嫩草99av在线_一区在线视频观看_欧美高清一区_欧美 日韩 国产 一区_99精品欧美一区二区三区_久久大香伊蕉在人线观看热2_一色屋精品视频在线观看网站_在线亚洲国产精品网站_亚洲区一区二区三区_你懂的视频一区二区
欧美一区2区视频在线观看| 中文字幕日韩精品一区| 日本道精品一区二区三区| 国产精品免费一区二区三区在线观看 | 色综合久久天天| 国产乱码精品一区二区三区不卡| 亚洲精品国产精品国自产观看| 亚洲欧洲一区| 国产毛片一区| 欧美色综合天天久久综合精品| 欧美手机在线视频| 欧美大片顶级少妇| 久久精品一区二区三区不卡牛牛| 国产精品素人视频| 亚洲影院免费观看| 日本欧美在线观看| 国产另类ts人妖一区二区| 成人午夜激情影院| 欧美福利在线| 中文久久精品| 欧美日本乱大交xxxxx| 欧美大尺度电影在线| 中文成人av在线| 午夜免费久久看| 国产精品一级二级三级| 午夜久久久久| 久久综合狠狠综合久久综青草| 欧美色图一区二区三区| 精品国产成人在线影院| 亚洲女同女同女同女同女同69| 日韩成人av影视| kk眼镜猥琐国模调教系列一区二区 | 99麻豆久久久国产精品免费优播| 国产一区久久| 在线免费亚洲电影| 久久亚洲一区二区三区明星换脸| 亚洲精品国产成人久久av盗摄| 久草这里只有精品视频| 欧美在线三区| 欧美午夜影院一区| 国产精品久久久久久久裸模| 日本午夜一区二区| 你懂的视频一区二区| 色屁屁一区二区| 国产清纯白嫩初高生在线观看91| 亚洲国产中文字幕在线视频综合| 国产高清精品网站| 国产欧美一区二区三区国产幕精品| 欧美性受极品xxxx喷水| 国产精品久久久久aaaa樱花| 韩国视频一区二区| 日韩亚洲一区在线播放| 欧美xxxxx裸体时装秀| 亚洲综合激情网| 99久久er热在这里只有精品66| 久久国产福利| 国产精品无圣光一区二区| 国产一区免费电影| 国产精品美女xx| 国产亚洲精品资源在线26u| 蜜臀va亚洲va欧美va天堂| 国外精品视频| 精品国产电影一区二区| 欧美aⅴ一区二区三区视频| 激情久久久久久| 欧美刺激脚交jootjob| 日本免费新一区视频| 伊人久久av导航| 26uuu久久天堂性欧美| 精品写真视频在线观看| 在线综合亚洲| 国产精品久久看| 99精品久久99久久久久| 欧美剧在线免费观看网站| 亚洲狠狠爱一区二区三区| 很黄很黄激情成人| 久久久噜噜噜久噜久久综合| 国产精品一区二区果冻传媒| 日本乱人伦aⅴ精品| 亚洲黄色免费网站| 黄色工厂这里只有精品| 久久久91精品国产一区二区三区| 韩国欧美国产1区| 欧美伊人精品成人久久综合97| 一区二区三区资源| 亚洲激情不卡| 欧美国产日产图区| 色综合色狠狠天天综合色| 欧美草草影院在线视频| 激情深爱一区二区| 欧美区视频在线观看| 久久成人av少妇免费| 91国偷自产一区二区三区成为亚洲经典 | 欧美日韩一卡二卡| 美日韩一区二区三区| 色婷婷综合激情| 日韩av电影天堂| 欧美系列在线观看| 精品中文字幕一区二区小辣椒| 在线中文字幕一区| 久久99精品国产麻豆婷婷洗澡| 欧洲精品在线观看| 国内精品写真在线观看| 在线综合亚洲欧美在线视频| 国产乱人伦偷精品视频不卡| 制服丝袜中文字幕一区| 国产69精品久久久久777| 精品国产一区二区三区忘忧草| 成人黄色av电影| 久久久久国产精品麻豆| 亚洲无吗在线| 亚洲成人一二三| 欧美性猛交xxxx黑人交| 国产一区二区h| 精品国产网站在线观看| 欧美婷婷在线| 亚洲综合区在线| 欧美中文字幕亚洲一区二区va在线 | 久久网这里都是精品| 精品动漫3d一区二区三区免费| 亚洲免费在线播放| 91久久精品网| 成人高清免费在线播放| 国产精品免费久久| 麻豆九一精品爱看视频在线观看免费| 美日韩一区二区| 久久这里都是精品| 日韩午夜av在线| 精品在线播放午夜| 久久天天做天天爱综合色| 亚洲福利国产| 蜜臀av性久久久久av蜜臀妖精| 日韩精品一区二区三区在线| 伊人久久大香线蕉av超碰演员| 日本一区中文字幕| 欧美tickling网站挠脚心| 亚洲国产精品第一区二区| 美女在线观看视频一区二区| 精品理论电影在线| 国产精品一区二区在线观看| 国产一区二区伦理| 亚洲日本在线看| 91麻豆精品久久久久蜜臀 | 国产精品成人网| 欧美天堂一区二区三区| 欧美日韩亚洲一区二区三区在线观看| 午夜精品福利一区二区三区av| 日韩美女主播在线视频一区二区三区| 亚洲国产日韩在线| 国产激情91久久精品导航| 亚洲免费观看高清| 欧美变态tickle挠乳网站| 一区二区三区精品视频在线观看| 国产精品系列在线观看| 亚洲综合色在线| 久久久精品综合| 欧美日韩国产另类一区| 国产欧美日韩一区二区三区| 粉嫩高潮美女一区二区三区| 亚洲影视在线观看| 久久久国产精品麻豆| 欧美视频一二三区| 在线综合视频| 欧美承认网站| 国产精品1区2区3区| 天天影视网天天综合色在线播放| 国产亚洲短视频| 91精品国产全国免费观看| 国产美女一区| 激情一区二区三区| 风间由美中文字幕在线看视频国产欧美 | 紧缚奴在线一区二区三区| 夜夜夜精品看看| 中文字幕乱码亚洲精品一区| 69堂国产成人免费视频| 麻豆91精品| 国产精品综合色区在线观看| 91在线播放网址| 国产成人精品亚洲日本在线桃色 | 国产精品888| 精品在线观看免费| 美女www一区二区| 五月激情综合色| 亚洲一区二区三区不卡国产欧美| 亚洲国产精品成人综合色在线婷婷| 日韩欧美亚洲国产精品字幕久久久| 欧美性一级生活| 在线视频亚洲一区| 久久综合给合久久狠狠色| 亚洲一区二区免费看| 国产一区二区三区的电影| 亚洲精选一区| 99热免费精品在线观看| 日韩视频中文| 一本一本久久a久久精品综合妖精| 欧美日韩影院| 1024成人| 久久av一区| 色呦呦网站一区| 欧美色精品在线视频|