<dfn id="hx5t3"><strike id="hx5t3"><em id="hx5t3"></em></strike></dfn>

    <thead id="hx5t3"></thead><nobr id="hx5t3"><font id="hx5t3"><rp id="hx5t3"></rp></font></nobr>

    <listing id="hx5t3"></listing>

    <var id="hx5t3"></var>
    <big id="hx5t3"></big>

      
      

      <output id="hx5t3"><ruby id="hx5t3"></ruby></output>
      <menuitem id="hx5t3"><dfn id="hx5t3"></dfn></menuitem>

      <big id="hx5t3"></big>

        瘋狂的技術宅

        瘋狂的技術宅 查看完整檔案

        北京編輯  |  填寫畢業院校  |  填寫所在公司/組織 blog.yidengxuetang.com/ 編輯
        編輯

        資深技術宅,愛好廣泛,興趣多變。博覽群書,喜歡扯淡。十八種語言樣樣稀松。想要了解更多,請關注微信公眾號:充實的腦洞。

        個人動態

        瘋狂的技術宅 收藏了文章 · 今天 09:38

        2020 年 Node.js 全球下載情況報告

        作者:Liz Parody & Marian Villa

        翻譯:瘋狂的技術宅

        原文:https://nodesource.com/blog/n...

        2020 年是充滿了變化和挑戰的的一年。NodeSource Node.js 二進制文件下載量逐月增加,為全球數百萬用戶提供了Node.js的強大功能。

        Nodesource 為 Linux 環境打包和分發 Node.js 已有 7 年了。包括所有主要和次要版本以及安全更新。我們看到下載量每年都在大量增加,而 2020 年是迄今為止下載量最大的一年。盡管我們并不分發每個版本的 Node.js,但 Linux 環境中的大多數下載都是由我們提供的。

        你是否想過有多少人還在使用 Node.js 0.1 版和其他過時的版本?幾百還是幾百萬?哪些國家和地區的 Node.js 下載量最大?最受歡迎的版本和發行版是哪個?每個月 Node.js 的下載量是多少?是在增加還是人們正在轉向其他技術?快來找出答案吧!

        在本文中,你可以找到有趣的數據,這些數據涉及 2020 年全球 Node.js 的使用情況,流行的運行時的趨勢,下載峰值和使用率,這些流行的運行時可為數百萬個應用程序提供支持。

        概念

        首先了解什么是 “Node.js 二進制文件分發”,簡單來說,這是一種將軟件(在本例中為 Node.js)以編譯形式提供給公眾使用的方法。默認情況下,二進制文件包允許它們從使用 Debian 和 EL 發行版的存儲庫中進行訪問。使安裝、分發和卸載更加容易。

        換句話說,它代表了一種在 Linux 環境中管理 Node.js 的更有效的方法。

        NodeSource 是 Linux 環境中 Node.js 二進制文件的主要分發者,你可以在 https://github.com/nodesource... 中找到存儲庫。在 Linux 中打包文件的方式主要有兩種:rpmdeb。 .rpm 文件主要由基于 Redhat發行版的 Fedora,CentOS,RHEL使用。 .deb 文件用于從 Debian 派生的 Linux 發行版(Ubuntu,Linux Mint等)。

        2020 年的 Node.js 版本下載

        image.png

        2020 年總共有 9,890 萬 次 NodeSource Node.js 二進制文件下載。正如我們在上圖中所看到的,rpm 格式下載最多的 Node.js 版本是v 10,下載量超過 1170 萬。對于 deb 格式,v 12 的下載量為 1760 萬。這意味著與 rpm 用戶相比,deb 用戶傾向于使用最新版本進行更新。

        這并不奇怪,因為 v10 和 v12 都是 2020 年的 LTS,但是仍然有大量用戶下載過時的版本。Node.js v0.1 的下載量為 65K 次。從 v4 到 v9(不再支持的版本)的 rpm 和 deb 均為 3750 萬次。

        可以在下面找到對不受支持版本使用情況的更完整分析。

        此外于 2020 年發布的 Node.js v14 和 v15 分別有 410 萬和 190 百萬的下載。

        RPM 與 DEB

        image.png

        隨著市場的趨勢,deb 包比 rpm 包更受歡迎。在這種情況下,有62.6%的下載量是 deb,而 37.4% 的下載量是 rpm。最受歡迎的 4 種 Linux 發行版中的 MX Linux、Manjaro、Mint 是基于 Debian 的,最后一種是 Arch。

        這是由市場因素造成的,因為 rpm 更新周期為每 5 年更新一次,而 deb 每年更新一次,因此 deb 更受歡迎。

        每月下載量

        image.png

        在 2020 年 1 月之間,下載量之間存在明顯的差距,分別為 150 萬和 730萬(相差580萬),但到今年年底(2020年),rpm 下載量急劇增加,分別超過了 deb 的下載量:610 萬和 550 萬。

        注意到 RPM 軟件包的增長主要是因為在我們的 CentOS 存儲庫(主要在中國)中存在用于分發我們的軟件包的鏡像。

        地理位置

        按國家

        image.png

        美國是 Node.js Binary 上下載量最多的國家,第二位是德國。俄羅斯,愛爾蘭和法國。印度以1.5%的下載量排名第10位(與流行觀點在此民意調查中的看法相反,世界其他國家和地區則占 45% 的下載。

        查看 StackOverflow 調查,主要訪問者也來自美國,其次是印度,德國,英國和加拿大。這展示了大多數開發人員位于何處,以及哪里使用 Node.js 最多。北美和歐洲處于領先地位。

        image.png

        通過 Octoverse 調查,可以看到相同的相關性,美國處于領先地位,其次是中國,德國,印度和俄羅斯。

        image.png

        按地區

        image.png

        美洲的下載量最多,其次是歐洲,下載量為 45.4M。美洲和歐洲的下載量占了 88%。亞洲有 840 萬,世界其他地方有 360 萬。

        如果我們看一下 Octoverse 調查,則亞洲用戶參與率更高,達到 30.7%。美洲和歐洲有 65.7%。

        image.png

        不受維護的版本的下載

        image.png

        在 2020 年受支持的 LTS 版本是 Node.js v10,v12 和 v14,這是推薦使用的版本。 這三個版本占下載量的55.4%,而不受支持的版本占下載量的 38%。 這主要原因可能是仍然有許多遺留項目,而向較新版本的遷移可能會帶來問題。 但是建議始終使用 LTS 版本,如果在遷移時遇到問題,可以聯系用于舊版應用程序遷移的NodeSource。

        峰值

        版本與下載峰值之間存在相關性。 11 月 16 日發布了一個安全版本,導致當天的下載量大增(457 K)。

        這是一個好習慣,因為始終建議將其更新到最新版本并部署安全發布。

        結論

        • 2020年 NodeSource Node.js 的下載量近 1 億次 ,這是一個令人興奮的里程碑。我們預計 2021 年將繼續增長。
        • 正如預期的那樣,deb 發行版比 rpm 發行版下載量更多。
        • rpm 下載最多的版本是 v 10,deb 是v12。
        • 過時的版本下載量仍然令人吃驚(38%)——應該升級了!
        • 下載主要集中在美洲和歐洲(88%),并且某些地區的樣本量嚴重不足,例如非洲和中東。

        NodeSource 為將 Node.js 分發到全世界而感到自豪,我們強烈建議使用 LTS 版,支持 Node.js 生態系統并為之做出貢獻。

        參考

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        瘋狂的技術宅 發布了文章 · 今天 08:30

        2020 年 Node.js 全球下載情況報告

        作者:Liz Parody & Marian Villa

        翻譯:瘋狂的技術宅

        原文:https://nodesource.com/blog/n...

        2020 年是充滿了變化和挑戰的的一年。NodeSource Node.js 二進制文件下載量逐月增加,為全球數百萬用戶提供了Node.js的強大功能。

        Nodesource 為 Linux 環境打包和分發 Node.js 已有 7 年了。包括所有主要和次要版本以及安全更新。我們看到下載量每年都在大量增加,而 2020 年是迄今為止下載量最大的一年。盡管我們并不分發每個版本的 Node.js,但 Linux 環境中的大多數下載都是由我們提供的。

        你是否想過有多少人還在使用 Node.js 0.1 版和其他過時的版本?幾百還是幾百萬?哪些國家和地區的 Node.js 下載量最大?最受歡迎的版本和發行版是哪個?每個月 Node.js 的下載量是多少?是在增加還是人們正在轉向其他技術?快來找出答案吧!

        在本文中,你可以找到有趣的數據,這些數據涉及 2020 年全球 Node.js 的使用情況,流行的運行時的趨勢,下載峰值和使用率,這些流行的運行時可為數百萬個應用程序提供支持。

        概念

        首先了解什么是 “Node.js 二進制文件分發”,簡單來說,這是一種將軟件(在本例中為 Node.js)以編譯形式提供給公眾使用的方法。默認情況下,二進制文件包允許它們從使用 Debian 和 EL 發行版的存儲庫中進行訪問。使安裝、分發和卸載更加容易。

        換句話說,它代表了一種在 Linux 環境中管理 Node.js 的更有效的方法。

        NodeSource 是 Linux 環境中 Node.js 二進制文件的主要分發者,你可以在 https://github.com/nodesource... 中找到存儲庫。在 Linux 中打包文件的方式主要有兩種:rpmdeb。 .rpm 文件主要由基于 Redhat發行版的 Fedora,CentOS,RHEL使用。 .deb 文件用于從 Debian 派生的 Linux 發行版(Ubuntu,Linux Mint等)。

        2020 年的 Node.js 版本下載

        image.png

        2020 年總共有 9,890 萬 次 NodeSource Node.js 二進制文件下載。正如我們在上圖中所看到的,rpm 格式下載最多的 Node.js 版本是v 10,下載量超過 1170 萬。對于 deb 格式,v 12 的下載量為 1760 萬。這意味著與 rpm 用戶相比,deb 用戶傾向于使用最新版本進行更新。

        這并不奇怪,因為 v10 和 v12 都是 2020 年的 LTS,但是仍然有大量用戶下載過時的版本。Node.js v0.1 的下載量為 65K 次。從 v4 到 v9(不再支持的版本)的 rpm 和 deb 均為 3750 萬次。

        可以在下面找到對不受支持版本使用情況的更完整分析。

        此外于 2020 年發布的 Node.js v14 和 v15 分別有 410 萬和 190 百萬的下載。

        RPM 與 DEB

        image.png

        隨著市場的趨勢,deb 包比 rpm 包更受歡迎。在這種情況下,有62.6%的下載量是 deb,而 37.4% 的下載量是 rpm。最受歡迎的 4 種 Linux 發行版中的 MX Linux、Manjaro、Mint 是基于 Debian 的,最后一種是 Arch。

        這是由市場因素造成的,因為 rpm 更新周期為每 5 年更新一次,而 deb 每年更新一次,因此 deb 更受歡迎。

        每月下載量

        image.png

        在 2020 年 1 月之間,下載量之間存在明顯的差距,分別為 150 萬和 730萬(相差580萬),但到今年年底(2020年),rpm 下載量急劇增加,分別超過了 deb 的下載量:610 萬和 550 萬。

        注意到 RPM 軟件包的增長主要是因為在我們的 CentOS 存儲庫(主要在中國)中存在用于分發我們的軟件包的鏡像。

        地理位置

        按國家

        image.png

        美國是 Node.js Binary 上下載量最多的國家,第二位是德國。俄羅斯,愛爾蘭和法國。印度以1.5%的下載量排名第10位(與流行觀點在此民意調查中的看法相反,世界其他國家和地區則占 45% 的下載。

        查看 StackOverflow 調查,主要訪問者也來自美國,其次是印度,德國,英國和加拿大。這展示了大多數開發人員位于何處,以及哪里使用 Node.js 最多。北美和歐洲處于領先地位。

        image.png

        通過 Octoverse 調查,可以看到相同的相關性,美國處于領先地位,其次是中國,德國,印度和俄羅斯。

        image.png

        按地區

        image.png

        美洲的下載量最多,其次是歐洲,下載量為 45.4M。美洲和歐洲的下載量占了 88%。亞洲有 840 萬,世界其他地方有 360 萬。

        如果我們看一下 Octoverse 調查,則亞洲用戶參與率更高,達到 30.7%。美洲和歐洲有 65.7%。

        image.png

        不受維護的版本的下載

        image.png

        在 2020 年受支持的 LTS 版本是 Node.js v10,v12 和 v14,這是推薦使用的版本。 這三個版本占下載量的55.4%,而不受支持的版本占下載量的 38%。 這主要原因可能是仍然有許多遺留項目,而向較新版本的遷移可能會帶來問題。 但是建議始終使用 LTS 版本,如果在遷移時遇到問題,可以聯系用于舊版應用程序遷移的NodeSource。

        峰值

        版本與下載峰值之間存在相關性。 11 月 16 日發布了一個安全版本,導致當天的下載量大增(457 K)。

        這是一個好習慣,因為始終建議將其更新到最新版本并部署安全發布。

        結論

        • 2020年 NodeSource Node.js 的下載量近 1 億次 ,這是一個令人興奮的里程碑。我們預計 2021 年將繼續增長。
        • 正如預期的那樣,deb 發行版比 rpm 發行版下載量更多。
        • rpm 下載最多的版本是 v 10,deb 是v12。
        • 過時的版本下載量仍然令人吃驚(38%)——應該升級了!
        • 下載主要集中在美洲和歐洲(88%),并且某些地區的樣本量嚴重不足,例如非洲和中東。

        NodeSource 為將 Node.js 分發到全世界而感到自豪,我們強烈建議使用 LTS 版,支持 Node.js 生態系統并為之做出貢獻。

        參考

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        贊 2 收藏 2 評論 0

        瘋狂的技術宅 收藏了文章 · 3月9日

        資源:15 個優秀的響應式 CSS 框架

        響應式 Web 設計旨在為各種設備(從臺式機顯示器到手機)提供最佳的瀏覽體驗。本文匯總了一些優秀的響應式 Web 設計 HTML 和 CSS 框架。這些框架都是開源的并免費的。

        對響應式 Web 框架進行比較并不那么容易。有的框架適合設計更快、更精簡網站的某些功能,而有些可能提供了大量功能、插件和附加組件,但是可能體積會比較龐大并且上手較難。

        1. Bootstrap

        image.png

        Bootstrap 是最流行的 HTML、CSS 和 JS 框架,用于在 Web 上開發響應式、移動優先項目。 Bootstrap 使前端開發更快、更輕松。他們提供了大量的文檔、示例和演示,可以幫你快速進行響應式 Web 開發。在 Bootstrap 5 中做了一些重大更改,例如隨意使用 jQuery 并添加了 RTL 支持,再加上現成的組件和工具類,使 Bootstrap 成為 Web 開發人員的最佳選擇之一。

        你還可以找到許多免費的高級 bootstrap 模板UI 工具包,這使你的開發過程更加輕松。

        官網:https://getbootstrap.com/

        2. Tailwind CSS

        image.png

        Tailwind 提供了一種基于實用工具的現代方法來構建響應站點。它有大量的實用工具類,無需編寫 CSS 即可構建現代網站。它與其它框架的不同之處在于需要通過開發設置來縮小最終 CSS 的大小,因為如果使用默認值,最終將會得到一個很大的 CSS 文件。Tailwind 能夠快速將樣式添加到 HTML 元素中,并提供了大量的開箱即用的設計樣式。這里有大量的 Tailwind CSS 資源: https://superdevresources.com...。

        官網:https://tailwindcss.com/

        3. Tachyons

        image.png

        Tachyons 也是一個基于實用工具的 CSS 庫,它提供了許多即裝即用的復雜功能,無需自己編寫大量 CSS。這樣做的好處是 Tachyons 的開箱即用樣式很輕巧,不需要其他設置。如果需要的話,仍然可以通過一些方法來減小尺寸。如果你需要易用的實用工具庫,那么這應該是一個不錯的選擇。

        官網:https://tachyons.io/

        4. Foundation

        image.png

        Foundation 是由產品設計公司 ZURB 制作的自適應前端框架。這個框架是他們自 1998 年來構建 Web 產品和服務的結果。Foundation 是最先進的響應式前端框架,并且提供了許多自定義功能。

        官網:http://foundation.zurb.com/

        5. Material Design for Bootstrap (MDB)

        image.png

        MDB 建立在 Bootstrap 之上,并提供了開箱即用的材料設計外觀。它具有出色的 CSS 庫,并且與大多數流行的 JavaScript 框架(如 jQuery、Angular、React 和。Vue.js)兼容。其核心庫是完全免費使用的。

        官網:https://mdbootstrap.com/

        6. UIkit

        image.png

        UIkit 是一個輕量級的模塊化前端框架,用于開發快速且強大的 Web 界面。 UIkit 提供了 HTML、CSS 和 JS 組件的全面集合,這些組件易于使用,易于定制和擴展。 UIkit 采用移動優先的方法,可提供從手機、平板電腦到臺式機的一致體驗。

        官網:http://getuikit.com/

        7. Pure CSS

        image.png

        Pure.css 是一組小型的響應式 CSS 模塊,可以用在任何一個 Web 項目中。Pure 的體積小的簡直過分。比如完整的時鐘模塊最小化壓縮版本僅為 4.0 KB。 Pure 基于 Normalize.css 構建,并提供原聲 HTML 元素以及最常見的 UI 組件的布局和樣式。 Pure 具有開箱即用的響應能力,所以元素在所有屏幕尺寸上都看起來不錯。

        官網:http://purecss.io/

        8. Material Design Lite Framework (MDL)

        image.png

        Google 的 Material Design Lite 框架是最流行的 CSS 框架之一,可為你的網站添加 Material Design 外觀。它不依賴任何 JavaScript 框架,可以跨設備使用,并且可以針對較舊的瀏覽器進行降級。它的構建充分考慮了可訪問性,并提供了豐富的文檔和入門模板。

        官網:https://getmdl.io/

        9. Materialize

        image.png

        Materialize 是基于 Material Design 的現代響應式前端框架。 Google的材料設計是一種流行的設計趨勢,涉及卡片、陰影和動畫。

        官網:http://materializecss.com/

        10. Skeleton

        image.png

        如果你要開發較小的項目,或者只是覺得自己不需要大型框架的所有實用工具,則可以試試 Skeleton。 Skeleton 僅設置了少量標準 HTML 元素的樣式,并包含一個網格。

        Skeleton 中的網格是一個 12 列的流體網格,最大寬度為 960px,隨著瀏覽器或設備的縮小而縮小??梢杂靡恍?CSS 更改最大寬度,并且所有列的大小都會相應進行調整。其語法很簡單,使響應式編碼更加容易。

        官網:http://getskeleton.com/

        11. Bulma

        image.png

        Bulma 是基于 flexbox 的現代 CSS 框架。它提供了響應式設計和移動設備優先的 UI 組件,并具有模塊化結構,可讓你只導入要包含在 Web 設計中的內容。Bulma 還提供了一個基于 flexbox 的現代網格系統。

        官網:http://bulma.io/

        12. Semantic UI

        image.png

        Semantic UI 是一個高級 CSS 框架,提供了 50 多個 UI 元素,300 多個 CSS 變量用于自定義,并通過 EM 值構建以用于響應式設計。它也已準備好 Flexbox。

        Semantic 是可用于生產環境的 CSS 框架,并能與 React、Angular、Meteor 和 Ember 等框架整合,你可以通過與這些框架中進行集成將 UI 層與應用邏輯組織在一起。

        官網:https://semantic-ui.com/

        13. Milligram

        image.png

        Milligram 是一個極簡的 CSS 框架,不依賴 JavaScript。它通過最少的樣式設置用來快速、干凈的創建響應式網站。它還提供了一個基于 flexbox 的網格系統。

        官網:https://milligram.github.io/

        14. Spectre.css

        image.png

        Spectre.css 是一個輕量級的庫,它提供了開箱即用的,基于 flexbox 的響應式和移動友好型布局。以在它的基礎上根據自己的需要添加樣式和響應實用工具。

        官網:https://picturepan2.github.io...

        15. Base CSS Framework

        image.png

        Base 也是一個輕量級 CSS 框架,可用于構建響應式網站。它為網站項目提供了免費的基礎入門軟件以及許多付費的現成模板。

        官網:https://getbase.org/

        如果對這些框架進行比較,你會發現 Bootstrap、Tailwind 和 Foundation 的流行度遠遠領先于其他框架。

        設計機構通常選擇 Bootstrap,因為它提供了開箱即用的組件,并且易于定制。這就是 Bootstrap 主題和庫數量眾多的原因.

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        瘋狂的技術宅 發布了文章 · 3月9日

        資源:15 個優秀的響應式 CSS 框架

        響應式 Web 設計旨在為各種設備(從臺式機顯示器到手機)提供最佳的瀏覽體驗。本文匯總了一些優秀的響應式 Web 設計 HTML 和 CSS 框架。這些框架都是開源的并免費的。

        對響應式 Web 框架進行比較并不那么容易。有的框架適合設計更快、更精簡網站的某些功能,而有些可能提供了大量功能、插件和附加組件,但是可能體積會比較龐大并且上手較難。

        1. Bootstrap

        image.png

        Bootstrap 是最流行的 HTML、CSS 和 JS 框架,用于在 Web 上開發響應式、移動優先項目。 Bootstrap 使前端開發更快、更輕松。他們提供了大量的文檔、示例和演示,可以幫你快速進行響應式 Web 開發。在 Bootstrap 5 中做了一些重大更改,例如隨意使用 jQuery 并添加了 RTL 支持,再加上現成的組件和工具類,使 Bootstrap 成為 Web 開發人員的最佳選擇之一。

        你還可以找到許多免費的高級 bootstrap 模板UI 工具包,這使你的開發過程更加輕松。

        官網:https://getbootstrap.com/

        2. Tailwind CSS

        image.png

        Tailwind 提供了一種基于實用工具的現代方法來構建響應站點。它有大量的實用工具類,無需編寫 CSS 即可構建現代網站。它與其它框架的不同之處在于需要通過開發設置來縮小最終 CSS 的大小,因為如果使用默認值,最終將會得到一個很大的 CSS 文件。Tailwind 能夠快速將樣式添加到 HTML 元素中,并提供了大量的開箱即用的設計樣式。這里有大量的 Tailwind CSS 資源: https://superdevresources.com...。

        官網:https://tailwindcss.com/

        3. Tachyons

        image.png

        Tachyons 也是一個基于實用工具的 CSS 庫,它提供了許多即裝即用的復雜功能,無需自己編寫大量 CSS。這樣做的好處是 Tachyons 的開箱即用樣式很輕巧,不需要其他設置。如果需要的話,仍然可以通過一些方法來減小尺寸。如果你需要易用的實用工具庫,那么這應該是一個不錯的選擇。

        官網:https://tachyons.io/

        4. Foundation

        image.png

        Foundation 是由產品設計公司 ZURB 制作的自適應前端框架。這個框架是他們自 1998 年來構建 Web 產品和服務的結果。Foundation 是最先進的響應式前端框架,并且提供了許多自定義功能。

        官網:http://foundation.zurb.com/

        5. Material Design for Bootstrap (MDB)

        image.png

        MDB 建立在 Bootstrap 之上,并提供了開箱即用的材料設計外觀。它具有出色的 CSS 庫,并且與大多數流行的 JavaScript 框架(如 jQuery、Angular、React 和。Vue.js)兼容。其核心庫是完全免費使用的。

        官網:https://mdbootstrap.com/

        6. UIkit

        image.png

        UIkit 是一個輕量級的模塊化前端框架,用于開發快速且強大的 Web 界面。 UIkit 提供了 HTML、CSS 和 JS 組件的全面集合,這些組件易于使用,易于定制和擴展。 UIkit 采用移動優先的方法,可提供從手機、平板電腦到臺式機的一致體驗。

        官網:http://getuikit.com/

        7. Pure CSS

        image.png

        Pure.css 是一組小型的響應式 CSS 模塊,可以用在任何一個 Web 項目中。Pure 的體積小的簡直過分。比如完整的時鐘模塊最小化壓縮版本僅為 4.0 KB。 Pure 基于 Normalize.css 構建,并提供原聲 HTML 元素以及最常見的 UI 組件的布局和樣式。 Pure 具有開箱即用的響應能力,所以元素在所有屏幕尺寸上都看起來不錯。

        官網:http://purecss.io/

        8. Material Design Lite Framework (MDL)

        image.png

        Google 的 Material Design Lite 框架是最流行的 CSS 框架之一,可為你的網站添加 Material Design 外觀。它不依賴任何 JavaScript 框架,可以跨設備使用,并且可以針對較舊的瀏覽器進行降級。它的構建充分考慮了可訪問性,并提供了豐富的文檔和入門模板。

        官網:https://getmdl.io/

        9. Materialize

        image.png

        Materialize 是基于 Material Design 的現代響應式前端框架。 Google的材料設計是一種流行的設計趨勢,涉及卡片、陰影和動畫。

        官網:http://materializecss.com/

        10. Skeleton

        image.png

        如果你要開發較小的項目,或者只是覺得自己不需要大型框架的所有實用工具,則可以試試 Skeleton。 Skeleton 僅設置了少量標準 HTML 元素的樣式,并包含一個網格。

        Skeleton 中的網格是一個 12 列的流體網格,最大寬度為 960px,隨著瀏覽器或設備的縮小而縮小??梢杂靡恍?CSS 更改最大寬度,并且所有列的大小都會相應進行調整。其語法很簡單,使響應式編碼更加容易。

        官網:http://getskeleton.com/

        11. Bulma

        image.png

        Bulma 是基于 flexbox 的現代 CSS 框架。它提供了響應式設計和移動設備優先的 UI 組件,并具有模塊化結構,可讓你只導入要包含在 Web 設計中的內容。Bulma 還提供了一個基于 flexbox 的現代網格系統。

        官網:http://bulma.io/

        12. Semantic UI

        image.png

        Semantic UI 是一個高級 CSS 框架,提供了 50 多個 UI 元素,300 多個 CSS 變量用于自定義,并通過 EM 值構建以用于響應式設計。它也已準備好 Flexbox。

        Semantic 是可用于生產環境的 CSS 框架,并能與 React、Angular、Meteor 和 Ember 等框架整合,你可以通過與這些框架中進行集成將 UI 層與應用邏輯組織在一起。

        官網:https://semantic-ui.com/

        13. Milligram

        image.png

        Milligram 是一個極簡的 CSS 框架,不依賴 JavaScript。它通過最少的樣式設置用來快速、干凈的創建響應式網站。它還提供了一個基于 flexbox 的網格系統。

        官網:https://milligram.github.io/

        14. Spectre.css

        image.png

        Spectre.css 是一個輕量級的庫,它提供了開箱即用的,基于 flexbox 的響應式和移動友好型布局。以在它的基礎上根據自己的需要添加樣式和響應實用工具。

        官網:https://picturepan2.github.io...

        15. Base CSS Framework

        image.png

        Base 也是一個輕量級 CSS 框架,可用于構建響應式網站。它為網站項目提供了免費的基礎入門軟件以及許多付費的現成模板。

        官網:https://getbase.org/

        如果對這些框架進行比較,你會發現 Bootstrap、Tailwind 和 Foundation 的流行度遠遠領先于其他框架。

        設計機構通常選擇 Bootstrap,因為它提供了開箱即用的組件,并且易于定制。這就是 Bootstrap 主題和庫數量眾多的原因.

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        贊 12 收藏 11 評論 0

        瘋狂的技術宅 收藏了文章 · 3月8日

        淺析 JavaScript 中的方法鏈

        方法鏈是一種流行的編程方法,可以幫助你寫出更簡潔易讀的代碼。在本文中我們一起學習 JavaScript 中的方法鏈是什么,以及它是怎樣工作的。另外我們還會探討如何使用方法鏈接來提高代碼的質量和可讀性。

        JavaScript 中方法鏈

        你一定曾經用過 jQuery 之類的庫,可能看到過類似的東西。在進行級聯時主要有兩種方法:一種是一個接一個的執行方法,另一種是在同一行上。在純 JavaScript 中這種做法也很普遍。你可以在數組、字符串和 promise 看到它。

        在這些情況下所有的過程都是相同的。首先引用要使用的對象。然后根據需要使用多種方法。但不是單獨使用這些方法,而要一個接一個地使用?;旧鲜前阉鼈冩溄釉谝黄?。先看一些例子。

        方法鏈的例子

        在處理字符串時有兩種方法。第一個種不用方法鏈,這要求必須在字符串上分別使用每個方法,這樣必須每次都引用這個字符串。

        第二種方式是用方法鏈。這時可以用所有想要的字符串方法。寫出的代碼也可以是單行或多行,這取決于你的習慣。而且只需要引用一次字符串。盡管結果相同,但是代碼量卻有很大的差異。

        // 在字符串上使用方法鏈的例子
        let myStr = ' - Hello-world. '
        
        // 不用方法鏈:
        myStr = myStr.toLowerCase()
        myStr = myStr.replace(/-/g, ' ')
        myStr = myStr.trim()
        
        // 用方法鏈:
        myStr = myStr.toLowerCase().replace(/-/g, ' ').trim()
        
        // 多行方法鏈的寫法:
        myStr = myStr
          .toLowerCase()
          .replace(/-/g, ' ')
          .trim()
        
        // 查看 "myStr" 的值
        console.log(myStr)
        // Output:
        // 'hello world.'

        在數組上也能用方法鏈:

        // 在數組上使用方法鏈的例子
        let myArray = [1, 7, 3, null, 8, null, 0, null, '20', 15]
        
        // 不用方法鏈:
        myArray = myArray.filter(el => typeof el === 'number' && isFinite(el))
        myArray = myArray.sort((x, y) => x - y)
        
        // 使用方法鏈:
        myArray = myArray.filter(el => typeof el === 'number' && isFinite(el)).sort((x, y) => x - y)
        
        // 多行方法鏈的寫法:
        myArray = myArray
          .filter(el => typeof el === 'number' && isFinite(el))
          .sort((x, y) => x - y)
        
        // 查看 "myArray" 的值.
        console.log(myArray)
        // Output:
        // [ 0, 1, 3, 7, 8 ]

        Promise 是一個很好的例子,因為在使用時差不多全都是方法鏈。首先創建一個 promise,然后添加適當的處理函數。

        // 創建 Promise
        const myPromise = new Promise((resolve, reject) => {
          // 創建一個假延遲
          setTimeout(function() {
            // 用一條簡單的消息解決諾言 promise
            resolve('Sorry, no data.')
          }, 1000)
        })
        
        // 使用方法鏈:
        myPromise.then((data) => console.log(data)).catch(err => console.log(error))
        
        // 多行方法鏈的寫法:
        myPromise
          .then((data) => console.log(data))
          .catch(err => console.log(error))
        // Output:
        // 'Sorry, no data.'

        方法鏈是怎樣工作的

        接下來研究它是怎樣工作的。答案很簡單,是因為 this 。

        假設有一個對象。如果在該對象內使用 this,它會引用這個對象。如果創建該對象的實例或副本,則 this 將會引用這個實例或副本。當你使用某些字符串或數組方法時,實際上是在用一個對象。

        const myObj = {
          name: 'Stuart',
          age: 65,
          sayHi() {
            // 這里的 this 是 myObj 的引用
            return `Hi my name is ${this.name}.`
          },
          logMe() {
            console.log(this)
          }
        }
        
        myObj.sayHi()
        // Output:
        // 'Hi my name is Stuart.'
        
        myObj.logMe()
        // Output:
        // {
        //   name: 'Stuart',
        //   age: 65,
        //   sayHi: ?,
        //   logMe: ?
        // }

        如果是字符串,則使用的是原始數據類型。但是你所使用的方法例如 toLowerCase(),存在于 String 對象的原型中。在對象上使用方法鏈還有一個關鍵要素: this。

        為了使鏈起作用,方法必須返回與其一起使用的對象,也就是必須返回 this。就像接力賽跑時的接力棒一樣。

        在 JavaScript 中實現方法鏈

        為了使方法鏈有效,必須滿足三個條件:首先,需要一些對象。其次,該對象需要一些以后可以調用的方法。第三,這些方法必須返回對象本身,它們必須返回 this 才能使用方法鏈。

        讓我們創建一個簡單的對象 person。 person name, agestate 屬性。state 用來表示當前處于什么狀態。要想改變這個狀態,需要用到幾個方法:walk(), sleep(), eat(), drink(), work()exercise()。

        由于我們希望所有這些方法都是可鏈的,所以它們都必須返回 this。另外代碼中還有一個用來把當前狀態記錄到控制臺的工具方法。

        // 創建 person 對象
        const person = {
          name: 'Jack Doer',
          age: 41,
          state: null,
          logState() {
            console.log(this.state)
          },
          drink() {
            // 修改 person 的 state.
            this.state = 'Drinking.'
        
            // 把狀態輸出到控制臺
            this.logState()
        
            // 返回 this 值。
            return this
          },
          eat() {
            this.state = 'Eating.'
            this.logState()
            return this
          },
          exercise() {
            this.state = 'Exercising.'
            this.logState()
            return this
          },
          sleep() {
            this.state = 'Sleeping.'
            this.logState()
            return this
          },
          walk() {
            this.state = 'Walking.'
            this.logState()
            return this
          },
          work() {
            this.state = 'Working.'
            this.logState()
            return this
          }
        }
        
        // 
        person
          .drink() // Output: 'Drinking.'
          .exercise() // Output: 'Exercising.'
          .eat() // Output: 'Eating.'
          .work() // Output: 'Working.'
          .walk() // Output: 'Walking.'
          .sleep() // Output: 'Sleeping.'
        
        // 寫在一行上
        person.drink().exercise().eat().work().walk().sleep()
        // Output:
        // 'Drinking.'
        // 'Exercising.'
        // 'Eating.'
        // 'Working.'
        // 'Walking.'
        // 'Sleeping.'

        方法、鏈、this 和箭頭函數

        必須使用 this 也意味著無法使用箭頭函數創建方法鏈。因為在箭頭函數中,this 沒有綁定到對象的實例,而是全局對象 window 的引用。如果返回 this,那么返回的不是對象本身而是 window。

        另一個問題是從箭頭函數內部訪問和修改對象屬性。由于 this 是全局對象 window,所以不能用它來引用對象及其屬性。

        如果你一定要使用箭頭函數,必須想辦法繞過這種方法。不用 this 來引用該對象,必須直接通過其名稱引用該對象,也就是用對象名替換所有出現在箭頭功能內的 this。

        // 創建 person 對象
        const person = {
          name: 'Jack Doer',
          age: 41,
          state: null,
          logState() {
            console.log(this.state)
          },
          drink: () => {
            person.state = 'Drinking.'
        
            person.logState()
        
            return person
          },
          eat: () => {
            person.state = 'Eating.'
        
            person.logState()
        
            return person
          },
          exercise: () => {
            person.state = 'Exercising.'
        
            person.logState()
        
            return person
          },
          sleep: () => {
            person.state = 'Sleeping.'
        
            person.logState()
        
            return person
          },
          walk: () => {
            person.state = 'Walking.'
        
            person.logState()
        
            return person
          },
          work: () => {
            person.state = 'Working.'
        
            person.logState()
        
            return person
          }
        }
        
        // 
        person
          .drink() // Output: 'Drinking.'
          .exercise() // Output: 'Exercising.'
          .eat() // Output: 'Eating.'
          .work() // Output: 'Working.'
          .walk() // Output: 'Walking.'
          .sleep() // Output: 'Sleeping.'

        這樣做的缺點是靈活性不好。如果如果用Object.assign()Object.create()復制對象,所有箭頭函數仍然會硬連接到原始對象。

        // 創建原始 person 對象
        const person = {
          name: 'Jack Doer',
          age: 41,
          state: null,
          logState() {
            // 打印整個對象
            console.log(this)
          },
          drink: () => {
            person.state = 'Drinking.'
        
            person.logState()
        
            return person
          },
          eat: () => {
            person.state = 'Eating.'
        
            person.logState()
        
            return person
          }
        }
        
        // 讓 person eat
        person.eat()
        // Output:
        // {
        //   name: 'Jack Doer',
        //   age: 41,
        //   state: 'Eating.',
        //   logState: ?,
        //   drink: ?,
        //   eat: ?
        // }
        
        // 基于person對象創建新對象。
        const newPerson = new Object(person)
        
        // 修改 "name" 和 "age" 屬性
        newPerson.name = 'Jackie Holmes'
        newPerson.age = 33
        
        // 讓 newPerson drink。
        // 這會打印 Jack Doer 而不是 Jackie Holmes。
        newPerson.drink()
        // Output:
        // {
        //   name: 'Jack Doer',
        //   age: 41,
        //   state: 'Drinking.',
        //   logState: ?,
        //   drink: ?,
        //   eat: ?
        // }

        但是,如果用 Object() 構造函數,就不會發生上述問題。如果用 new 關鍵字的和 Object() 構造造函數,將會創建獨立的新對象。當你對這個新對象使用某個方法時,它將僅對這個新對象有效,而對原始對象無效。

        // 創建原始 person 對象
        const person = {
          name: 'Jack Doer',
          age: 41,
          state: null,
          logState() {
            // 打印整個對象
            console.log(this)
          },
          drink: () => {
            person.state = 'Drinking.'
        
            person.logState()
        
            return person
          },
          eat: () => {
            person.state = 'Eating.'
        
            person.logState()
        
            return person
          }
        }
        
        // 讓 person eat.
        person.eat()
        // Output:
        // {
        //   name: 'Jack Doer',
        //   age: 41,
        //   state: 'Eating.',
        //   logState: ?,
        //   drink: ?,
        //   eat: ?
        // }
        
        // 基于 person 對象創建新對象 
        const newPerson = new Object(person)
        
        // 修改 "name" 和 "age" 屬性
        newPerson.name = 'Jackie Holmes'
        newPerson.age = 33
        
        // 讓 newPerson drink.
        newPerson.drink()
        // Output:
        // {
        //   name: 'Jackie Holmes',
        //   age: 33,
        //   state: 'Drinking.',
        //   logState: ?,
        //   drink: ?,
        //   eat: ?
        // }

        如果你一定要用箭頭功能,并想要復制對象的話,最好用 Object() 構造函數和 new 關鍵字創建這些副本。否則只需要用常規函數就夠了。

        方法鏈和類

        如果你喜歡使用 JavaScript 類,也可以在JavaScript中使用方法鏈接。除了語法略又不同外,整個過程和對象是一樣的。但是要注意所有可鏈的方法都必須返回 this。

        // 創建 Person 類
        class Person {
          constructor(name, age) {
            this.name = name
            this.age = age
            this.state = null
          }
        
          logState() {
            console.log(this.state)
          }
        
          drink() {
            this.state = 'Drinking.'
        
            this.logState()
        
            return this
          }
        
          eat() {
            this.state = 'Eating.'
        
            this.logState()
        
            return this
          }
        
          sleep() {
            this.state = 'Sleeping.'
        
            this.logState()
        
            return this
          }
        }
        
        // 創建 Person 類的實例
        const joe = new Person('Joe', 55)
        
        // 使用方法鏈
        joe
          .drink() // Output: 'Drinking.'
          .eat() // Output: 'Eating.'
          .sleep() // Output: 'Sleeping.'

        總結

        方法鏈是非常有用的,它可以幫你編寫更短、更易讀的代碼。

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        瘋狂的技術宅 收藏了文章 · 3月8日

        Node.js 的 QUIC 支持

        作者:James Snell

        翻譯:瘋狂的技術宅

        https://www.nearform.com/blog...

        在2019年3月,受到 NearForm 和 Protocol Labs 的支持,我開始為 Node.js 實現 QUIC 協議 支持。這個基于 UDP 的新傳輸協議旨在最終替代所有使用 TCP 的 HTTP 通信。

        image.png

        熟悉 UDP 的人可能會產生質疑。眾所周知 UDP 是不可靠的,數據包經常會有丟失、亂序、重復等情況。 UDP 不保證高級協議(例如 HTTP)嚴格要求的 TCP 所支持的可靠性和順序。那就是 QUIC 進來的地方。

        QUIC 協議在 UDP 之上定義了一層,該層為 UDP 引入了錯誤處理、可靠性、流控制和內置安全性(通過 TLS 1.3)。實際上它在 UDP 之上重新實現了大多數 TCP 的特效,但是有一個關鍵的區別:與 TCP 不同,仍然可以不按順序傳輸數據包。了解這一點對于理解 QUIC 為什么優于 TCP 至關重要。

        QUIC 消除了隊首阻塞的根源

        在 HTTP 1 中,客戶端和服務器之間所交換的所有消息都是連續的、不間斷的數據塊形式。雖然可以通過單個 TCP 連接發送多個請求或響應,但是在發送下一條完整消息之前,必須先等上一條消息完整的傳輸完畢。這意味著,如果要發送一個 10 兆字節的文件,然后發送一個 2 兆字節的文件,則前者必須完全傳輸完畢,然后才能啟動后者。這就是所謂的隊首阻塞,是造成大量延遲和不良使用網絡帶寬的根源。

        HTTP 2 嘗試通過引入多路復用來解決此問題。 HTTP 2 不是將請求和響應作為連續的流傳輸,而是將請求和響應分成了被稱為幀的離散塊,這些塊可以與其他幀交織。一個 TCP 連接理論上可以處理無限數量的并發請求和響應流。盡管從理論上講這是可行的,但是 HTTP 2 的設計沒有考慮 TCP 層出現隊首阻塞的可能性。

        TCP 本身是嚴格排序的協議。數據包被序列化并按照固定順序通過網絡發送。如果數據包未能到達其目的地,則會阻止整個數據包流,直到可以重新傳輸丟失的數據包為止。有效的順序是:發送數據包1,等待確認,發送數據包2,等待確認,發送數據包3……。使用 HTTP 1,在任何給定時間只能傳輸一個 HTTP 消息,如果單個 TCP 數據包丟失,那么重傳只會影響單個 HTTP 請求/響應流。但是使用 HTTP 2,則會在丟失單個 TCP 數據包的情況下阻止無限數量的并發 HTTP 請求/響應流的傳輸。在通過高延遲、低可靠性網絡進行 HTTP 2 通信時,與 HTTP 1 相比,整體性能和網絡吞吐量會急劇下降。

        image.png

        在 HTTP 1 中,該請求會被阻塞,因為一次只能發送一條完整的消息。

        image.png

        在 HTTP 2 中,當單個 TCP 數據包丟失或損壞時,該請求將被阻塞。

        image.png

        在QUIC中,數據包彼此獨立,能夠以任何順序發送(或重新發送)。

        幸運的是有了 QUIC 情況就不同了。當數據流被打包到離散的 UDP 數據包中傳輸時,任何單個數據包都能夠以任意順序發送(或重新發送),而不會影響到其他已發送的數據包。換句話說,線路阻塞問題在很大程度上得到解決。

        QUIC 引入了靈活性、安全性和低延遲

        QUIC 還引入了許多其他重要功能:

        • QUIC 連接的運行獨立于網絡拓撲結構。在建立了 QUIC 連接后,源 IP 地址和目標 IP 地址和端口都可以更改,而無需重新建立連接。這對于經常進行網絡切換(例如 LTE 到 WiFi)的移動設備特別有用。
        • 默認 QUIC 連接是安全的并加密的。 TLS 1.3 支持直接包含在協議中,并且所有 QUIC 通信都經過加密。
        • QUIC 為 UDP 添加了關鍵的流控制和錯誤處理,并包括重要的安全機制以防止一系列拒絕服務攻擊。
        • QUIC 添加了對零行程 HTTP 請求的支持,這與基于 TCP 的 TLS 之上的 HTTP 不同,后者要求客戶端和服務器之間進行多次數據交換來建立 TLS 會話,然后才能傳輸 HTTP 請求數據,QUIC 允許 HTTP 請求頭作為 TLS 握手的一部分發送,從而大大減少了新連接的初始延遲。

        image.png

        為 Node.js 內核實現 QUIC

        為 Node.js 內核實現 QUIC 的工作從 2019 年 3 月開始,并由 NearForm 和 Protocol Labs 共同贊助。我們利用出色的 ngtcp2 庫來提供大量的低層實現。因為 QUIC 是許多 TCP 特性的重新實現,所以對 Node.js 意義重大,并且與 Node.js 中當前的 TCP 和 HTTP 相比能夠支持更多特性。同時對用戶隱藏了大量的復雜性。

        “quic” 模塊

        在實現新的 QUIC 支持的同時,我們用了新的頂級內置 quic 模塊來公開 API。當該功能在 Node.js 核心中落地時,是否仍將使用這個頂級模塊,將在以后確定。不過當在開發中使用實驗性支持時,你可以通過 require('quic') 使用這個 API。

        const { createSocket } = require('quic')

        quic 模塊公開了一個導出:createSocket 函數。這個函數用來創建 QuicSocket 對象實例,該對象可用于 QUIC 服務器和客戶端。

        QUIC 的所有工作都在一個單獨的 GitHub 存儲庫 中進行,該庫 fork 于 Node.js master 分支并與之并行開發。如果你想使用新模塊,或者貢獻自己的代碼,可以從那里獲取源代碼,請參閱 Node.js 構建說明。不過它現在仍然是一項尚在進行中的工作,你一定會遇到 bug 的。

        創建QUIC服務器

        QUIC 服務器是一個 QuicSocket 實例,被配置為等待遠程客戶端啟動新的 QUIC 連接。這是通過綁定到本地 UDP 端口并等待從對等方接收初始 QUIC 數據包來完成的。在收到 QUIC 數據包后,QuicSocket 將會檢查是否存在能夠用于處理該數據包的服務器 QuicSession 對象,如果不存在將會創建一個新的對象。一旦服務器的 QuicSession 對象可用,則該數據包將被處理,并調用用戶提供的回調。這里有一點很重要,處理 QUIC 協議的所有細節都由 Node.js 在其內部處理。

        const { createSocket } = require('quic')
        const { readFileSync } = require('fs')
        
        const key = readFileSync('./key.pem')
        const cert = readFileSync('./cert.pem')
        const ca = readFileSync('./ca.pem')
        const requestCert = true
        const alpn = 'echo'
        
        const server = createSocket({
          // 綁定到本地 UDP 5678 端口
          endpoint: { port: 5678 },
          // 為新的 QuicServer Session 實例創建默認配置
          server: {
            key,
            cert,
            ca,
            requestCert
            alpn 
          }
        })
        
        server.listen()
        server.on('ready', () => {
          console.log(`QUIC server is listening on ${server.address.port}`)
        })
        
        server.on('session', (session) => {
          session.on('stream', (stream) => {
            // Echo server!
            stream.pipe(stream) 
          })
        
          const stream = session.openStream()
          stream.end('hello from the server')
        })

        如前所述,QUIC 協議內置并要求支持 TLS 1.3。這意味著每個 QUIC 連接必須有與其關聯的 TLS 密鑰和證書。與傳統的基于 TCP 的 TLS 連接相比,QUIC 的獨特之處在于 QUIC 中的 TLS 上下文與 QuicSession 相關聯,而不是 QuicSocket。如果你熟悉 Node.js 中 TLSSocket 的用法,那么你一定注意到這里的區別。

        QuicSocket(和 QuicSession)的另一個關鍵區別是,與 Node.js 公開的現有 net.Sockettls.TLSSocket 對象不同,QuicSocketQuicSession 都不是 ReadableWritable的流。即不能用一個對象直接向連接的對等方發送數據或從其接收數據,所以必須使用 QuicStream 對象。

        在上面的例子中創建了一個 QuicSocket 并將其綁定到本地 UDP 的 5678 端口。然后告訴這個 QuicSocket 偵聽要啟動的新 QUIC 連接。一旦 QuicSocket 開始偵聽,將會發出 ready 事件。

        當啟動新的 QUIC 連接并創建了對應服務器的 QuicSession 對象后,將會發出 session 事件。創建的 QuicSession 對象可用于偵聽新的客戶端服務器端所啟動的 QuicStream 實例。

        QUIC 協議的更重要特征之一是客戶端可以在不打開初始流的情況下啟動與服務器的新連接,并且服務器可以在不等待來自客戶端的初始流的情況下先啟動其自己的流。這個功能提供了許多非常有趣的玩法,而這在當前 Node.js 內核中的 HTTP 1 和 HTTP 2 是不可能提供的。

        創建QUIC客戶端

        QUIC 客戶端和服務器之間幾乎沒有什么區別:

        const { createSocket } = require('quic')
        
        const fs = require('fs')
        const key = readFileSync('./key.pem')
        const cert = readFileSync('./cert.pem')
        const ca = readFileSync('./ca.pem')
        const requestCert = true
        const alpn = 'echo'
        
        const servername = 'localhost'
        const socket = createSocket({
          endpoint: { port: 8765 },
          client: {
            key,
            cert,
            ca,
            requestCert
            alpn,
            servername
          }
        })
        
        const req = socket.connect({
          address: 'localhost',
          port: 5678,
        })
        
        req.on('stream', (stream) => {
          stream.on('data', (chunk) => { /.../ })
          stream.on('end', () => { /.../ })
        })
        
        req.on('secure', () => {
          const stream = req.openStream()
          const file = fs.createReadStream(__filename)
          file.pipe(stream)
          stream.on('data', (chunk) => { /.../ })
          stream.on('end', () => { /.../ })
          stream.on('close', () => {
            // Graceful shutdown
            socket.close()
          })
          stream.on('error', (err) => { /.../ })
        })

        對于服務器和客戶端,createSocket() 函數用于創建綁定到本地 UDP 端口的 QuicSocket 實例。對于 QUIC 客戶端來說,僅在使用客戶端身份驗證時才需要提供 TLS 密鑰和證書。

        QuicSocket 上調用 connect() 方法將新創建一個客戶端 QuicSession 對象,并與對應地址和端口的服務器創建新的 QUIC 連接。啟動連接后進行 TLS 1.3 握手。握手完成后,客戶端 QuicSession 對象會發出 secure 事件,表明現在可以使用了。

        與服務器端類似,一旦創建了客戶端 QuicSession 對象,就可以用 stream 事件監聽服務器啟動的新 QuicStream 實例,并可以調用 openStream() 方法來啟動新的流。

        單向流和雙向流

        所有的 QuicStream 實例都是雙工流對象,這意味著它們都實現了 ReadableWritable 流 Node.js API。但是,在 QUIC 中,每個流都可以是雙向的,也可以是單向的。

        雙向流在兩個方向上都是可讀寫的,而不管該流是由客戶端還是由服務器啟動的。單向流只能在一個方向上讀寫??蛻舳税l起的單向流只能由客戶端寫入,并且只能由服務器讀??;客戶端上不會發出任何數據事件。服務器發起的單向流只能由服務器寫入,并且只能由客戶端讀??;服務器上不會發出任何數據事件。

        // 創建雙向流
        const stream = req.openStream()
        
        // 創建單向流
        const stream = req.openStream({ halfOpen: true })

        每當遠程對等方啟動流時,無論是服務器還是客戶端的 QuicSession 對象都會發出提供 QuicStream 對象的 stream 事件??梢杂脕頇z查這個對象確定其來源(客戶端或服務器)及其方向(單向或雙向)

        session.on('stream', (stream) => {
          if (stream.clientInitiated)
            console.log('client initiated stream')
          if (stream.serverInitiated)
            console.log('server initiated stream')
          if (stream.bidirectional)
            console.log('bidirectional stream')
          if (stream.unidirectional)
            console.log(‘’unidirectional stream')
        })

        由本地發起的單向 QuicStreamReadable 端在創建 QuicStream 對象時總會立即關閉,所以永遠不會發出數據事件。同樣,遠程發起的單向 QuicStreamWritable 端將在創建后立即關閉,因此對 write() 的調用也會始終失敗。

        就是這樣

        從上面的例子可以清楚地看出,從用戶的角度來看,創建和使用 QUIC 是相對簡單的。盡管協議本身很復雜,但這種復雜性幾乎不會上升到面向用戶的 API。實現中包含一些高級功能和配置選項,這些功能和配置項在上面的例子中沒有說明,在通常情況下,它們在很大程度上是可選的。

        在示例中沒有對 HTTP 3 的支持進行說明。在基本 QUIC 協議實現的基礎上實現 HTTP 3 語義的工作正在進行中,并將在以后的文章中介紹。

        QUIC 協議的實現還遠遠沒有完成。在撰寫本文時,IETF 工作組仍在迭代 QUIC 規范,我們在 Node.js 中用于實現大多數 QUIC 的第三方依賴也在不斷發展,并且我們的實現還遠未完成,缺少測試、基準、文檔和案例。但是作為 Node.js v14 中的一項實驗性新功能,這項工作正在逐步著手進行。希望 QUIC 和 HTTP 3 支持在 Node.js v15 中能夠得到完全支持。我們希望你的幫助!如果你有興趣參與,請聯系 https://www.nearform.com/cont... !

        鳴謝

        在結束本文時,我要感謝 NearForm 和 Protocol Labs 在財政上提供的贊助,使我能夠全身心投入于對 QUIC 的實現。兩家公司都對 QUIC 和 HTTP 3 將如何發展對等和傳統 Web 應用開發特別感興趣。一旦實現接近完成,我將會再寫一文章來闡述 QUIC 協議的一些奇妙的用例,以及使用 QUIC 與 HTTP 1、HTTP 2、WebSockets 以及其他方法相比的優勢。

        James Snell( @jasnell)是 NearForm Research 的負責人,該團隊致力于研究和開發 Node.js 在性能和安全性方面的主要新功能,以及物聯網和機器學習的進步。 James 在軟件行業擁有 20 多年的經驗,并且是 Node.js 社區中的知名人物。他曾是多個 W3C 語義 web 和 IETF 互聯網標準的作者、合著者、撰稿人和編輯。他是 Node.js 項目的核心貢獻者,是 Node.js 技術指導委員會(TSC)的成員,并曾作為 TSC 代表在 Node.js Foundation 董事會任職。

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        瘋狂的技術宅 發布了文章 · 3月5日

        Node.js 的 QUIC 支持

        作者:James Snell

        翻譯:瘋狂的技術宅

        https://www.nearform.com/blog...

        在2019年3月,受到 NearForm 和 Protocol Labs 的支持,我開始為 Node.js 實現 QUIC 協議 支持。這個基于 UDP 的新傳輸協議旨在最終替代所有使用 TCP 的 HTTP 通信。

        image.png

        熟悉 UDP 的人可能會產生質疑。眾所周知 UDP 是不可靠的,數據包經常會有丟失、亂序、重復等情況。 UDP 不保證高級協議(例如 HTTP)嚴格要求的 TCP 所支持的可靠性和順序。那就是 QUIC 進來的地方。

        QUIC 協議在 UDP 之上定義了一層,該層為 UDP 引入了錯誤處理、可靠性、流控制和內置安全性(通過 TLS 1.3)。實際上它在 UDP 之上重新實現了大多數 TCP 的特效,但是有一個關鍵的區別:與 TCP 不同,仍然可以不按順序傳輸數據包。了解這一點對于理解 QUIC 為什么優于 TCP 至關重要。

        QUIC 消除了隊首阻塞的根源

        在 HTTP 1 中,客戶端和服務器之間所交換的所有消息都是連續的、不間斷的數據塊形式。雖然可以通過單個 TCP 連接發送多個請求或響應,但是在發送下一條完整消息之前,必須先等上一條消息完整的傳輸完畢。這意味著,如果要發送一個 10 兆字節的文件,然后發送一個 2 兆字節的文件,則前者必須完全傳輸完畢,然后才能啟動后者。這就是所謂的隊首阻塞,是造成大量延遲和不良使用網絡帶寬的根源。

        HTTP 2 嘗試通過引入多路復用來解決此問題。 HTTP 2 不是將請求和響應作為連續的流傳輸,而是將請求和響應分成了被稱為幀的離散塊,這些塊可以與其他幀交織。一個 TCP 連接理論上可以處理無限數量的并發請求和響應流。盡管從理論上講這是可行的,但是 HTTP 2 的設計沒有考慮 TCP 層出現隊首阻塞的可能性。

        TCP 本身是嚴格排序的協議。數據包被序列化并按照固定順序通過網絡發送。如果數據包未能到達其目的地,則會阻止整個數據包流,直到可以重新傳輸丟失的數據包為止。有效的順序是:發送數據包1,等待確認,發送數據包2,等待確認,發送數據包3……。使用 HTTP 1,在任何給定時間只能傳輸一個 HTTP 消息,如果單個 TCP 數據包丟失,那么重傳只會影響單個 HTTP 請求/響應流。但是使用 HTTP 2,則會在丟失單個 TCP 數據包的情況下阻止無限數量的并發 HTTP 請求/響應流的傳輸。在通過高延遲、低可靠性網絡進行 HTTP 2 通信時,與 HTTP 1 相比,整體性能和網絡吞吐量會急劇下降。

        image.png

        在 HTTP 1 中,該請求會被阻塞,因為一次只能發送一條完整的消息。

        image.png

        在 HTTP 2 中,當單個 TCP 數據包丟失或損壞時,該請求將被阻塞。

        image.png

        在QUIC中,數據包彼此獨立,能夠以任何順序發送(或重新發送)。

        幸運的是有了 QUIC 情況就不同了。當數據流被打包到離散的 UDP 數據包中傳輸時,任何單個數據包都能夠以任意順序發送(或重新發送),而不會影響到其他已發送的數據包。換句話說,線路阻塞問題在很大程度上得到解決。

        QUIC 引入了靈活性、安全性和低延遲

        QUIC 還引入了許多其他重要功能:

        • QUIC 連接的運行獨立于網絡拓撲結構。在建立了 QUIC 連接后,源 IP 地址和目標 IP 地址和端口都可以更改,而無需重新建立連接。這對于經常進行網絡切換(例如 LTE 到 WiFi)的移動設備特別有用。
        • 默認 QUIC 連接是安全的并加密的。 TLS 1.3 支持直接包含在協議中,并且所有 QUIC 通信都經過加密。
        • QUIC 為 UDP 添加了關鍵的流控制和錯誤處理,并包括重要的安全機制以防止一系列拒絕服務攻擊。
        • QUIC 添加了對零行程 HTTP 請求的支持,這與基于 TCP 的 TLS 之上的 HTTP 不同,后者要求客戶端和服務器之間進行多次數據交換來建立 TLS 會話,然后才能傳輸 HTTP 請求數據,QUIC 允許 HTTP 請求頭作為 TLS 握手的一部分發送,從而大大減少了新連接的初始延遲。

        image.png

        為 Node.js 內核實現 QUIC

        為 Node.js 內核實現 QUIC 的工作從 2019 年 3 月開始,并由 NearForm 和 Protocol Labs 共同贊助。我們利用出色的 ngtcp2 庫來提供大量的低層實現。因為 QUIC 是許多 TCP 特性的重新實現,所以對 Node.js 意義重大,并且與 Node.js 中當前的 TCP 和 HTTP 相比能夠支持更多特性。同時對用戶隱藏了大量的復雜性。

        “quic” 模塊

        在實現新的 QUIC 支持的同時,我們用了新的頂級內置 quic 模塊來公開 API。當該功能在 Node.js 核心中落地時,是否仍將使用這個頂級模塊,將在以后確定。不過當在開發中使用實驗性支持時,你可以通過 require('quic') 使用這個 API。

        const { createSocket } = require('quic')

        quic 模塊公開了一個導出:createSocket 函數。這個函數用來創建 QuicSocket 對象實例,該對象可用于 QUIC 服務器和客戶端。

        QUIC 的所有工作都在一個單獨的 GitHub 存儲庫 中進行,該庫 fork 于 Node.js master 分支并與之并行開發。如果你想使用新模塊,或者貢獻自己的代碼,可以從那里獲取源代碼,請參閱 Node.js 構建說明。不過它現在仍然是一項尚在進行中的工作,你一定會遇到 bug 的。

        創建QUIC服務器

        QUIC 服務器是一個 QuicSocket 實例,被配置為等待遠程客戶端啟動新的 QUIC 連接。這是通過綁定到本地 UDP 端口并等待從對等方接收初始 QUIC 數據包來完成的。在收到 QUIC 數據包后,QuicSocket 將會檢查是否存在能夠用于處理該數據包的服務器 QuicSession 對象,如果不存在將會創建一個新的對象。一旦服務器的 QuicSession 對象可用,則該數據包將被處理,并調用用戶提供的回調。這里有一點很重要,處理 QUIC 協議的所有細節都由 Node.js 在其內部處理。

        const { createSocket } = require('quic')
        const { readFileSync } = require('fs')
        
        const key = readFileSync('./key.pem')
        const cert = readFileSync('./cert.pem')
        const ca = readFileSync('./ca.pem')
        const requestCert = true
        const alpn = 'echo'
        
        const server = createSocket({
          // 綁定到本地 UDP 5678 端口
          endpoint: { port: 5678 },
          // 為新的 QuicServer Session 實例創建默認配置
          server: {
            key,
            cert,
            ca,
            requestCert
            alpn 
          }
        })
        
        server.listen()
        server.on('ready', () => {
          console.log(`QUIC server is listening on ${server.address.port}`)
        })
        
        server.on('session', (session) => {
          session.on('stream', (stream) => {
            // Echo server!
            stream.pipe(stream) 
          })
        
          const stream = session.openStream()
          stream.end('hello from the server')
        })

        如前所述,QUIC 協議內置并要求支持 TLS 1.3。這意味著每個 QUIC 連接必須有與其關聯的 TLS 密鑰和證書。與傳統的基于 TCP 的 TLS 連接相比,QUIC 的獨特之處在于 QUIC 中的 TLS 上下文與 QuicSession 相關聯,而不是 QuicSocket。如果你熟悉 Node.js 中 TLSSocket 的用法,那么你一定注意到這里的區別。

        QuicSocket(和 QuicSession)的另一個關鍵區別是,與 Node.js 公開的現有 net.Sockettls.TLSSocket 對象不同,QuicSocketQuicSession 都不是 ReadableWritable的流。即不能用一個對象直接向連接的對等方發送數據或從其接收數據,所以必須使用 QuicStream 對象。

        在上面的例子中創建了一個 QuicSocket 并將其綁定到本地 UDP 的 5678 端口。然后告訴這個 QuicSocket 偵聽要啟動的新 QUIC 連接。一旦 QuicSocket 開始偵聽,將會發出 ready 事件。

        當啟動新的 QUIC 連接并創建了對應服務器的 QuicSession 對象后,將會發出 session 事件。創建的 QuicSession 對象可用于偵聽新的客戶端服務器端所啟動的 QuicStream 實例。

        QUIC 協議的更重要特征之一是客戶端可以在不打開初始流的情況下啟動與服務器的新連接,并且服務器可以在不等待來自客戶端的初始流的情況下先啟動其自己的流。這個功能提供了許多非常有趣的玩法,而這在當前 Node.js 內核中的 HTTP 1 和 HTTP 2 是不可能提供的。

        創建QUIC客戶端

        QUIC 客戶端和服務器之間幾乎沒有什么區別:

        const { createSocket } = require('quic')
        
        const fs = require('fs')
        const key = readFileSync('./key.pem')
        const cert = readFileSync('./cert.pem')
        const ca = readFileSync('./ca.pem')
        const requestCert = true
        const alpn = 'echo'
        
        const servername = 'localhost'
        const socket = createSocket({
          endpoint: { port: 8765 },
          client: {
            key,
            cert,
            ca,
            requestCert
            alpn,
            servername
          }
        })
        
        const req = socket.connect({
          address: 'localhost',
          port: 5678,
        })
        
        req.on('stream', (stream) => {
          stream.on('data', (chunk) => { /.../ })
          stream.on('end', () => { /.../ })
        })
        
        req.on('secure', () => {
          const stream = req.openStream()
          const file = fs.createReadStream(__filename)
          file.pipe(stream)
          stream.on('data', (chunk) => { /.../ })
          stream.on('end', () => { /.../ })
          stream.on('close', () => {
            // Graceful shutdown
            socket.close()
          })
          stream.on('error', (err) => { /.../ })
        })

        對于服務器和客戶端,createSocket() 函數用于創建綁定到本地 UDP 端口的 QuicSocket 實例。對于 QUIC 客戶端來說,僅在使用客戶端身份驗證時才需要提供 TLS 密鑰和證書。

        QuicSocket 上調用 connect() 方法將新創建一個客戶端 QuicSession 對象,并與對應地址和端口的服務器創建新的 QUIC 連接。啟動連接后進行 TLS 1.3 握手。握手完成后,客戶端 QuicSession 對象會發出 secure 事件,表明現在可以使用了。

        與服務器端類似,一旦創建了客戶端 QuicSession 對象,就可以用 stream 事件監聽服務器啟動的新 QuicStream 實例,并可以調用 openStream() 方法來啟動新的流。

        單向流和雙向流

        所有的 QuicStream 實例都是雙工流對象,這意味著它們都實現了 ReadableWritable 流 Node.js API。但是,在 QUIC 中,每個流都可以是雙向的,也可以是單向的。

        雙向流在兩個方向上都是可讀寫的,而不管該流是由客戶端還是由服務器啟動的。單向流只能在一個方向上讀寫??蛻舳税l起的單向流只能由客戶端寫入,并且只能由服務器讀??;客戶端上不會發出任何數據事件。服務器發起的單向流只能由服務器寫入,并且只能由客戶端讀??;服務器上不會發出任何數據事件。

        // 創建雙向流
        const stream = req.openStream()
        
        // 創建單向流
        const stream = req.openStream({ halfOpen: true })

        每當遠程對等方啟動流時,無論是服務器還是客戶端的 QuicSession 對象都會發出提供 QuicStream 對象的 stream 事件??梢杂脕頇z查這個對象確定其來源(客戶端或服務器)及其方向(單向或雙向)

        session.on('stream', (stream) => {
          if (stream.clientInitiated)
            console.log('client initiated stream')
          if (stream.serverInitiated)
            console.log('server initiated stream')
          if (stream.bidirectional)
            console.log('bidirectional stream')
          if (stream.unidirectional)
            console.log(‘’unidirectional stream')
        })

        由本地發起的單向 QuicStreamReadable 端在創建 QuicStream 對象時總會立即關閉,所以永遠不會發出數據事件。同樣,遠程發起的單向 QuicStreamWritable 端將在創建后立即關閉,因此對 write() 的調用也會始終失敗。

        就是這樣

        從上面的例子可以清楚地看出,從用戶的角度來看,創建和使用 QUIC 是相對簡單的。盡管協議本身很復雜,但這種復雜性幾乎不會上升到面向用戶的 API。實現中包含一些高級功能和配置選項,這些功能和配置項在上面的例子中沒有說明,在通常情況下,它們在很大程度上是可選的。

        在示例中沒有對 HTTP 3 的支持進行說明。在基本 QUIC 協議實現的基礎上實現 HTTP 3 語義的工作正在進行中,并將在以后的文章中介紹。

        QUIC 協議的實現還遠遠沒有完成。在撰寫本文時,IETF 工作組仍在迭代 QUIC 規范,我們在 Node.js 中用于實現大多數 QUIC 的第三方依賴也在不斷發展,并且我們的實現還遠未完成,缺少測試、基準、文檔和案例。但是作為 Node.js v14 中的一項實驗性新功能,這項工作正在逐步著手進行。希望 QUIC 和 HTTP 3 支持在 Node.js v15 中能夠得到完全支持。我們希望你的幫助!如果你有興趣參與,請聯系 https://www.nearform.com/cont... !

        鳴謝

        在結束本文時,我要感謝 NearForm 和 Protocol Labs 在財政上提供的贊助,使我能夠全身心投入于對 QUIC 的實現。兩家公司都對 QUIC 和 HTTP 3 將如何發展對等和傳統 Web 應用開發特別感興趣。一旦實現接近完成,我將會再寫一文章來闡述 QUIC 協議的一些奇妙的用例,以及使用 QUIC 與 HTTP 1、HTTP 2、WebSockets 以及其他方法相比的優勢。

        James Snell( @jasnell)是 NearForm Research 的負責人,該團隊致力于研究和開發 Node.js 在性能和安全性方面的主要新功能,以及物聯網和機器學習的進步。 James 在軟件行業擁有 20 多年的經驗,并且是 Node.js 社區中的知名人物。他曾是多個 W3C 語義 web 和 IETF 互聯網標準的作者、合著者、撰稿人和編輯。他是 Node.js 項目的核心貢獻者,是 Node.js 技術指導委員會(TSC)的成員,并曾作為 TSC 代表在 Node.js Foundation 董事會任職。

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        贊 7 收藏 5 評論 0

        瘋狂的技術宅 收藏了文章 · 3月4日

        JS 中循環遍歷數組方式總結

        作者:Axel Rauschmayer

        翻譯:瘋狂的技術宅

        https://2ality.com/2021/01/lo...

        本文比較并總結遍歷數組的四種方式:

        • for 循環:
        for (let index=0; index < someArray.length; index++) {
          const elem = someArray[index];
          // ···
        }
        • for-in 循環:
        for (const key in someArray) {
          console.log(key);
        }
        • 數組方法 .forEach()
        someArray.forEach((elem, index) => {
          console.log(elem, index);
        });
        • for-of 循環:
        for (const elem of someArray) {
          console.log(elem);
        }

        for-of 通常是最佳選擇。我們會明白原因。


        for 循環 [ES1]

        JavaScript 中的 for 循環很古老,它在 ECMAScript 1 中就已經存在了。for 循環記錄 arr 每個元素的索引和值:

        const arr = ['a', 'b', 'c'];
        arr.prop = 'property value';
        
        for (let index=0; index < arr.length; index++) {
          const elem = arr[index];
          console.log(index, elem);
        }
        
        // Output:
        // 0, 'a'
        // 1, 'b'
        // 2, 'c'

        for 循環的優缺點是什么?

        • 它用途廣泛,但是當我們要遍歷數組時也很麻煩。
        • 如果我們不想從第一個數組元素開始循環時它仍然很有用,用其他的循環機制很難做到這一點。

        for-in循環 [ES1]

        for-in 循環與 for 循環一樣古老,同樣在 ECMAScript 1中就存在了。下面的代碼用 for-in 循環輸出 arr 的 key:

        const arr = ['a', 'b', 'c'];
        arr.prop = 'property value';
        
        for (const key in arr) {
          console.log(key);
        }
        
        // Output:
        // '0'
        // '1'
        // '2'
        // 'prop'

        for-in 不是循環遍歷數組的好方法:

        • 它訪問的是屬性鍵,而不是值。
        • 作為屬性鍵,數組元素的索引是字符串,而不是數字。
        • 它訪問的是所有可枚舉的屬性鍵(自己的和繼承的),而不僅僅是 Array 元素的那些。

        for-in 訪問繼承屬性的實際用途是:遍歷對象的所有可枚舉屬性。

        數組方法 .forEach() [ES5]

        鑒于 forfor-in 都不特別適合在數組上循環,因此在 ECMAScript 5 中引入了一個輔助方法:Array.prototype.forEach()

        const arr = ['a', 'b', 'c'];
        arr.prop = 'property value';
        
        arr.forEach((elem, index) => {
          console.log(elem, index);
        });
        
        // Output:
        // 'a', 0
        // 'b', 1
        // 'c', 2

        這種方法確實很方便:它使我們無需執行大量操作就能夠可訪問數組元素和索引。如果用箭頭函數(在ES6中引入)的話,在語法上會更加優雅。

        .forEach() 的主要缺點是:

        • 不能在它的循環體中使用 await。
        • 不能提前退出 .forEach() 循環。而在 for 循環中可以使用 break。

        中止 .forEach() 的解決方法

        如果想要中止 .forEach() 之類的循環,有一種解決方法:.some() 還會循環遍歷所有數組元素,并在其回調返回真值時停止。

        const arr = ['red', 'green', 'blue'];
        arr.some((elem, index) => {
          if (index >= 2) {
            return true; // 中止循環
          }
          console.log(elem);
          //此回調隱式返回 `undefined`,這
          //是一個偽值。 因此,循環繼續。
        });
        
        // Output:
        // 'red'
        // 'green'

        可以說這是對 .some() 的濫用,與 for-ofbreak 比起來,要理解這段代碼并不容易。

        for-of 循環 [ES6]

        for-of 循環在 ECMAScript 6 開始支持:

        const arr = ['a', 'b', 'c'];
        arr.prop = 'property value';
        
        for (const elem of arr) {
          console.log(elem);
        }
        // Output:
        // 'a'
        // 'b'
        // 'c'

        for-of 在循環遍歷數組時非常有效:

        • 用來遍歷數組元素。
        • 可以使用 await

          • 如果有需要,可以輕松地遷移到 for-await-of。
        • 甚至可以將 breakcontinue 用于外部作用域。

        for-of 和可迭代對象

        for-of 不僅可以遍歷數組,還可以遍歷可迭代對象,例如遍歷 Map:

        const myMap = new Map()
          .set(false, 'no')
          .set(true, 'yes')
        ;
        for (const [key, value] of myMap) {
          console.log(key, value);
        }
        
        // Output:
        // false, 'no'
        // true, 'yes'

        遍歷 myMap 會生成 [鍵,值] 對,可以通過對其進行解構來直接訪問每一對數據。

        for-of 和數組索引

        數組方法 .entries() 返回一個可迭代的 [index,value] 對。如果使用 for-of 并使用此方法進行解構,可以很方便地訪問數組索引:

        const arr = ['chocolate', 'vanilla', 'strawberry'];
        
        for (const [index, elem] of arr.entries()) {
          console.log(index, elem);
        }
        // Output:
        // 0, 'chocolate'
        // 1, 'vanilla'
        // 2, 'strawberry'

        總結

        for-of 循環的的可用性比 for,for-in.forEach() 更好。

        通常四種循環機制之間的性能差異應該是無關緊要。如果你要做一些運算量很大的事,還是切換到 WebAssembly 更好一些。

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        瘋狂的技術宅 收藏了文章 · 3月3日

        數組將要新增的方法:array.at(index)

        除了普通對象之外,數組是 JavaScript 中使用最廣泛的數據結構。數組上最常使用的操作是按索引訪問元素。

        本文介紹新的數組方法 array.at(index)。

        新方法最主要好處是可以用負索引從數組末尾訪問元素,而平時使用的方括號語法 array[index] 則沒有辦法做到。

        方括號語法的局限性

        通常按索引訪問數組元素的方法是使用方括號語法 array[index]

        const fruits = ['orange', 'apple', 'banana', 'grape'];
        
        const item = fruits[1];
        item; // => 'apple'

        表達式 array[index] 的執行結果是位于 index 位置的數組元素項,JavaScript 中數組的索引從 0 開始,這些你肯定知道。

        通常方括號語法是一種通過正索引(>= 0)訪問數組元素的方法。它的語法簡單易讀。

        但有時我們希望從末尾開始訪問元素。例如:

        const fruits = ['orange', 'apple', 'banana', 'grape'];
        
        const lastItem = fruits[fruits.length - 1];
        lastItem; // => 'grape'

        fruits[fruits.length-1] 是訪問數組最后一個元素的方式,其中fruits.length-1 是最后一個元素的索引。

        問題在于方括號不允許直接從數組末尾訪問元素,也不能接受負索引。

        幸運的是,一項新的提案(截至2021年1月的第3階段)將 at() 方法引入了數組(以及類型化數組和字符串),并解決了方括號的許多限制。

        array.at() 方法

        簡而言之,array.at(index) 用來訪問處于 index 位置的元素。

        如果 index 是一個正整數 >= 0,則該方法返回這個索引位置的元素:

        const fruits = ['orange', 'apple', 'banana', 'grape'];
        
        const item = fruits.at(1);
        item; // => 'apple'

        如果 index 參數大于或等于數組長度,則像方括號語法一樣返回 undefined

        const fruits = ['orange', 'apple', 'banana', 'grape'];
        
        const item = fruits.at(999);
        item; // => undefined

        當對 array.at() 方法使用負索引時,會從數組的末尾訪問元素。

        例如用索引 -1 來訪問數組的最后一個元素:

        const fruits = ['orange', 'apple', 'banana', 'grape'];
        
        const lastItem = fruits.at(-1);
        lastItem; // => 'grape'

        下面是更詳細的例子:

        const vegetables = ['potatoe', 'tomatoe', 'onion'];
        
        vegetables.at(0); // => 'potatoe'
        vegetables.at(1); // => 'tomatoe'
        vegetables.at(2); // => 'onion'
        vegetables.at(3); // => undefined
        
        vegetables.at(-1); // => 'onion'
        vegetables.at(-2); // => 'tomatoe'
        vegetables.at(-3); // => 'potatoe'
        vegetables.at(-4); // => undefined

        如果 negIndex 是一個負索引 < 0,那么 array.at(negIndex) 將會訪問位于索引 array.length + negIndex 處的元素。例如:

        const fruits = ['orange', 'apple', 'banana', 'grape'];
        
        const negIndex = -2;
        
        fruits.at(negIndex);              // => 'banana'
        fruits[fruits.length + negIndex]; // => 'banana'

        總結

        JavaScript 中的方括號語法是按索引訪問項目的常用方法。只需將索引表達式放在方括號 array[index] 中,然后既可以獲取在該索引處的數組項。

        但是有時這種方式并不方便,因為它不接受負索引。所以要訪問數組的最后一個元素,需要用這種方法:

        const lastItem = array[array.length - 1];

        新的數組方法 array.at(index) 使你可以將索引作為常規訪問器訪問數組元素。此外,array.at(index)接受負索引,在這種情況下,該方法從頭開始獲取元素:

        const lastItem = array.at(-1);

        現在只需要把 array.prototype.at polyfill 包含到你的應用程序中,就可以使用 array.at() 了。

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        瘋狂的技術宅 收藏了文章 · 3月3日

        用WebAssembly在瀏覽器中對視頻進行轉碼

        作者:Colin Eberhardt
        翻譯:瘋狂的技術宅
        原文:https://blog.scottlogic.com/2...

        我們可以憑借 FFmpeg 的 WebAssembly 版直接在瀏覽器中運行這個能強大的視頻處理工具。在本文中,我們來探索一下 FFmpeg.wasm,并寫一個簡單的代碼轉換器,把數據流傳輸到視頻元素中并播放出來。

        FFmpeg.wasm

        一般我們通過其命令行使用 FFmpeg。例如下面的命令可以吧 AVI 文件轉碼為 MP4 格式:

        $ ffmpeg -i input.avi output.mp4

        同樣的工作也可以在瀏覽器中做到。FFmpeg.wasm 是 FFmpeg 的 WebAssembly 端口,像其他 JavaScript 模塊一樣可以通過 npm 安裝,并在 Node 或瀏覽器中使用:

        $ npm install @ffmpeg/ffmpeg @ffmpeg/core

        裝好 FFmpeg.wasm 后,可以在瀏覽器中執行等效的轉碼操作:

        // fetch AVI 文件
        const sourceBuffer = await fetch("input.avi").then(r => r.arrayBuffer());
        
        // 創建 FFmpeg 實例并載入
        const ffmpeg = createFFmpeg({ log: true });
        await ffmpeg.load();
        
        // 把 AVI 寫入 FFmpeg 文件系統
        ffmpeg.FS(
          "writeFile",
          "input.avi",
          new Uint8Array(sourceBuffer, 0, sourceBuffer.byteLength)
        );
        
        // 執行 FFmpeg 命令行工具, 把 AVI 轉碼為 MP4
        await ffmpeg.run("-i", "input.avi", "output.mp4");
        
        // 把 MP4 文件從 FFmpeg 文件系統中取出
        const output = ffmpeg.FS("readFile", "output.mp4");
        
        // 對視頻文件進行后續操作
        const video = document.getElementById("video");
        video.src = URL.createObjectURL(
          new Blob([output.buffer], { type: "video/mp4" })
        );

        這里有很多有趣的東西,接下來深入研究細節。

        在 fetch API 加載 AVI 文件之后,用下面的步驟初始化 FFmpeg:

        const ffmpeg = createFFmpeg({ log: true });
        await ffmpeg.load();

        FFmpeg.wasm 由一個很薄的 JavaScript API 層和一個較大的(20M)WebAssembly 二進制文件組成。上面的代碼加載并初始化了可供使用的 WebAssembly 文件。

        WebAssembly 是在瀏覽器中運行的、經過性能優化的底層字節碼。它被專門設計為能夠用多種語言進行開發和編譯。

        FFmpeg 的歷史已經超過20年了,有一千多人貢獻過代碼。在 WebAssembly 出現之前,要給它創建 JavaScript 能夠調用的接口,所涉及的工作可能會非常繁瑣。

        未來 WebAssembly 的使用會更加廣泛,現在它作為把大量成熟的 C/C++ 代碼庫引入 Web 的一種機制,已經非常成功了, Google Earth,AutoCADTensorFlow 等都是非常典型的案例。

        在初始化之后,下一步是把 AVI 文件寫入文件系統:

        ffmpeg.FS(
          "writeFile",
          "input.avi",
          new Uint8Array(sourceBuffer, 0, sourceBuffer.byteLength)
        );

        這段代碼有些奇怪,想要知道這是什么情況,需要更深入地研究 FFmpeg.wasm 的編譯方式。

        Emscripten 是遵循 WebAssembly 規范開發的把 C/C++ 代碼編譯為 WebAssembly 的工具鏈,正是它把 FFmpeg.wasm 編譯為 WebAssembly 的。但是 Emscripten 不只是一個 C++ 編譯器,為了簡化現有代碼庫的遷移,它通過基于 Web 的等效項提供對許多 C/C++ API 的支持。例如通過把函數調用映射到 WebGL 來支持 OpenGL。它還支持 SDL、POSIX 和 pthread。

        Emscripten 通過提供 file-system API 來映射到內存中的存儲。使用 FFmpeg.wasm 可以直接通過 ffmpeg.FS 函數公開底層的 Emscripten 文件系統API,你可以用這個借口瀏覽目錄、創建文件和其他各種針對文件系統的操作。

        下一步是真正有意思的地方:

        await ffmpeg.run("-i", "input.avi", "output.mp4");

        如果你在 Chrome 的開發工具中進行觀察,會注意到它創建了許多 Web Worker,每個 Web Worker 都加載了 ffmpeg.wasm:

        image.png

        在這里用到了 Emscripten 的 Pthread 支持(https://emscripten.org/docs/p...)。啟用日志記錄后,你可以在控制臺中查看進度;

        Output #0, mp4, to 'output.mp4':
           Metadata:
             encoder         : Lavf58.45.100
             Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 256x240, q=-1--1, 35 fps, 17920 tbn, 35 tbc
             Metadata:
               encoder         : Lavc58.91.100 libx264
             Side data:
               cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
        frame=   47 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x
        frame=   76 fps= 68 q=30.0 size=       0kB time=00:00:00.65 bitrate=   0.6kbits/s speed=0.589x
        frame=  102 fps= 62 q=30.0 size=       0kB time=00:00:01.40 bitrate=   0.3kbits/s speed=0.846x

        最后一步是讀取輸出文件并將其提供給 video 元素:

        const output = ffmpeg.FS("readFile", "output.mp4");
        const video = document.getElementById("video");
        video.src = URL.createObjectURL(
          new Blob([output.buffer], { type: "video/mp4" })
        );

        有趣的是,帶有虛擬文件系統的命令行工具 FFmpeg.wasm 有點像 docker!

        創建流式轉碼器

        對大視頻文件進行編碼轉換可能耗時較長。我們可以先把文件轉碼為切片,并將其逐步添加到視頻緩沖區中。

        你可以用 Media Source Extension APIs 來構建流媒體播放,其中包括 MediaSourceSourceBuffer 對象。創建和加載緩沖區的操作可能非常棘手,因為這兩個對象都提供了生命周期事件,必須通過處理這些事件才能在正確的時間添加新的緩沖區。為了管理這些事件的協調,我用到了 RxJS。

        下面的函數基于 FFmpeg.wasm 轉碼后的輸出創建一個 RxJS Observable:

        const bufferStream = filename =>
          new Observable(async subscriber => {
            const ffmpeg = FFmpeg.createFFmpeg({
              corePath: "thirdparty/ffmpeg-core.js",
              log: false
            });
        
            const fileExists = file => ffmpeg.FS("readdir", "/").includes(file);
            const readFile = file => ffmpeg.FS("readFile", file);
        
            await ffmpeg.load();
            const sourceBuffer = await fetch(filename).then(r => r.arrayBuffer());
            ffmpeg.FS(
              "writeFile", "input.mp4",
              new Uint8Array(sourceBuffer, 0, sourceBuffer.byteLength)
            );
        
            let index = 0;
        
            ffmpeg
              .run(
                "-i", "input.mp4",
                // 給流進行編碼
                "-segment_format_options", "movflags=frag_keyframe+empty_moov+default_base_moof",
                // 編碼為 5 秒鐘的片段
                "-segment_time", "5",
                // 通過索引寫入文件系統
                "-f", "segment", "%d.mp4"
              )
              .then(() => {
                // 發送剩余的文件內容
                while (fileExists(`${index}.mp4`)) {
                  subscriber.next(readFile(`${index}.mp4`));
                  index++;
                }
                subscriber.complete();
              });
        
            setInterval(() => {
              // 定期檢查是否已寫入文件
              if (fileExists(`${index + 1}.mp4`)) {
                subscriber.next(readFile(`${index}.mp4`));
                index++;
              }
            }, 200);
          });

        上面的代碼用了和以前相同的 FFmpeg.wasm 設置,將要轉碼的文件寫入內存文件系統。為了創建分段輸出,ffmpeg.run 的配置與上一個例子有所不同,需要設置合適的轉碼器。在運行時 FFmpeg 把帶有增量索引(0.mp4, 1.mp4, …)的文件寫入內存文件系統。

        為了實現流式傳輸輸出,需要通過輪詢文件系統以獲取轉碼后的輸出,并通過 subscriber.next 把數據作為事件發出。最后當 ffmpeg.run 完成時,余下的文件內容被送出并關閉流。

        需要創建一個MediaSource對象來把數據流傳輸到視頻元素中,并等待 sourceopen 事件觸發。下面的代碼用到了 RxJS 的 combineLatest 來確保在觸發這個事件之前不處理 FFmpeg 輸出:

        const mediaSource = new MediaSource();
        videoPlayer.src = URL.createObjectURL(mediaSource);
        videoPlayer.play();
        
        const mediaSourceOpen = fromEvent(mediaSource, "sourceopen");
        
        const bufferStreamReady = combineLatest(
          mediaSourceOpen,
          bufferStream("4club-JTV-i63.avi")
        ).pipe(map(([, a]) => a));

        當接收到第一個視頻切片或緩沖時,需要在正確的時間向 SourceBuffer 添加 MediaSource,并將原始緩沖區附加到 SourceBuffer。在此之后,還有一個需要注意的地方,新緩沖不能馬上添加到 SourceBuffer 中,需要等到它發出 updateend 事件表明先前的緩沖區已被處理后才行。

        下面的代碼用 take 處理第一個緩沖區,并用 mux.js 庫讀取 mime 類型。然后從 updateend 事件返回一個新的可觀察流:

        const sourceBufferUpdateEnd = bufferStreamReady.pipe(
          take(1),
          map(buffer => {
            // 基于當前的 mime type 創建一個buffer
            const mime = `video/mp4; codecs="${muxjs.mp4.probe
              .tracks(buffer)
              .map(t => t.codec)
              .join(",")}"`;
            const sourceBuf = mediaSource.addSourceBuffer(mime);
        
            // 追加道緩沖區
            mediaSource.duration = 5;
            sourceBuf.timestampOffset = 0;
            sourceBuf.appendBuffer(buffer);
        
            // 創建一個新的事件流 
            return fromEvent(sourceBuf, "updateend").pipe(map(() => sourceBuf));
          }),
          flatMap(value => value)
        );

        剩下的就是在緩沖區到達及 SourceBuffer 準備就緒時追加緩沖區??梢酝ㄟ^ RxJS 的 zip 函數實現:

        zip(sourceBufferUpdateEnd, bufferStreamReady.pipe(skip(1)))
          .pipe(
            map(([sourceBuf, buffer], index) => {
              mediaSource.duration = 10 + index * 5;
              sourceBuf.timestampOffset = 5 + index * 5;
              sourceBuf.appendBuffer(buffer.buffer);
            })
          )
          .subscribe();

        就這樣對事件進行了一些協調,最終只需很少的代碼就能對視頻進行轉碼了,并將結果逐漸添加到視頻元素中。

        最后一個例子的代碼在GitHub上。

        173382ede7319973.gif


        本文首發微信公眾號:前端先鋒

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

        歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章


        歡迎繼續閱讀本專欄其它高贊文章:


        查看原文

        認證與成就

        • 獲得 10371 次點贊
        • 獲得 32 枚徽章 獲得 1 枚金徽章, 獲得 4 枚銀徽章, 獲得 27 枚銅徽章

        擅長技能
        編輯

        開源項目 & 著作
        編輯

        (??? )
        暫時沒有

        注冊于 2014-04-11
        個人主頁被 55k 人瀏覽

        一本到在线是免费观看_亚洲2020天天堂在线观看_国产欧美亚洲精品第一页_最好看的2018中文字幕