iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 9
4
Modern Web

從 Hooks 開始,讓你的網頁 React 起來系列 第 9

[Day 09 - 網速換算器] 網速傻傻分不清楚 Mbps? Mb/s? 來寫個單位換算器吧

看了好多天的計數器,今天將讓我們換個主題吧!在開始之前,先來討論一下什麼是網速吧!

imgur

Mbps 是用來計算網路頻寬最常見的單位,自從大家升級到 4G 的行動網路後,如果你不是使用吃到飽的使用者,勢必會看到「降速」或「限速」這兩個詞,可是你有想過最常見的降速 20Mbps、或者限速 5Mbps 是什麼意思嗎?

但因為最常接觸到的單位常常是 MB,因為這常用在傳輸檔案或儲存空間上。因此有些不太清楚的店員可能會跟你說 20Mbps 就是每秒鐘有 20MB 的網路傳輸速度,但真的是這樣嗎?錯!20Mbps 完全不等於每秒鐘有 20MB 的網路傳輸量

Imgur

又或者你現在想要追一下最近很紅的「俗女養成記」,怕網速太慢影響看劇的興致,於是打開 Netflix 提供的測速網站 Fast.com 想要測一下網速:

Imgur

測完發現有 300Mbps,可是你有想過,這個 300Mbps 是什麼意思嗎?這絕對不是表示每秒鐘有 300MB 的網路頻寬。

MbpsMB/s 雖然不同,但這兩個單位之間是可以轉換的。那麼多少 Mbps 才會等於 1 MB/s 呢?

在後面幾天,就讓我們來做一個網速單位換算器吧!

Imgur

Mbps ? 或 MB/s ?

要做網速單位換算器之前,我們要先來了解 Mbps 到底是什麼意思,它又要怎麼轉換成 MB/s

實際上 Mbps 中的第一個 M 是英文的 million,也就是「百萬」; 小寫 bbit 的意思,中文稱作「位元」;後面的 ps 則是 per second 的意思,也就是「每秒」。綜合起來,Mbps 指的是「每秒鐘可以傳輸多少百萬位元(Million bits per second)」。

MB/s 呢?這裡第一個 M 一樣式 million 百萬的意思;但 B 則是大寫的 B大寫的 B 和小寫的 b 在意思上是完全不同的大寫 B 是指 Byte ,中文稱作「位元組」。一個位元組(Byte)需要由 8 個位元(bit)所組成的

所以實際上 Mbps 的值需要「除以 8 」後才會是指每秒鐘可以有多少 MB 的傳輸量

小寫 b大寫 B 是不同的,小寫 b 指的是「位元(bit)」,大寫 B 指的是「位元組(Byte)」,一個位元組是由 8 個位元組成,所以 Mbps 的值需要「除以 8」之後才會是指每秒鐘有多少 MB 的傳輸量

也就是說至少要到 8Mbps 以上,才表示你的網路速度每秒鐘可以傳輸 1MB 以上:

Imgur

來實作單位轉換器 UI 吧

今天就先讓我們來完成 UI 的部分吧,這主要是參考 Rizkydribbble 的設計。

CSS 部分

因為 HTML 和 CSS 並不是我們主要著墨的內容,所以 CSS 的部分版哥都已經幫你完成好了!請你打開 CodePen 的 Day 9 - Network Speed Converter with only CSS,然後按下 Fork。

在這個 CodePen 中,除了之前就有使用過的 React 套件之外,這裡還載入了 FontAwesome 這個套件,FontAwesome 提供了非常多的圖示可以使用,它有分免費版和付費版,在非常多網站上都可以看到它的蹤影:

Imgur

HTML 部分

HTML 的部分,最外層的結構長這樣子:

<div class="container">
  <div class="card-header"><!-- ... --></div>
  <div class="card-body"><!-- ... --></div>
  <div class="card-footer"><!-- ... --></div>
</div>

card-headercard-footer 的部分最單純,放個標題就可以,分別對應到卡片的上面和下面:

<div class="container">
  <div class="card-header">Network Speed Converter</div>
  <div class="card-body">
    <!-- ... -->
  </div>
  <div class="card-footer">FAST</div>
</div>

比較複雜的是 card-body 裡面的部分,這裡一樣把它切成上下兩個部分,分別是 unit-controlconverter

<!-- ... -->
<div class="card-body">
  <div class="unit-control">
    <!-- ... -->
  </div>
  <div class="converter">
    <!-- ... -->
  </div>
</div>
<!-- ... -->

unit-controlconverter 的部分,裡面則都分別切分成左、中、右三個區塊。

unit-control 的部份:

<!-- ... -->
<div class="unit-control">
  <div class="unit">Mbps</div>

  <span class="exchange-icon fa-fw fa-stack">
    <i class="far fa-circle fa-stack-2x"></i>
    <i class="fas fa-exchange-alt fa-stack-1x"></i>
  </span>

  <div class="unit">Mb/s</div>
</div>
<!-- ... -->

converter 的部分:

<!-- ... -->
<div class="converter">

  <div class="flex-1">
    <div class="converter-title">Set</div>
    <input type="number" class="input-number" min="0" />
  </div>

  <span class="angle-icon fa-2x" style="margin-top: 30px">
    <i class="fas fa-angle-right"></i>
  </span>

  <div class="text-right flex-1">
    <div class="converter-title">Show</div>
    <input type="text" class="input-number text-right" value="125" disabled />
  </div>
</div>
<!-- ... -->

完成後完整的程式碼可以到 Day 9 - Network Speed Converter with HTML and CSS 檢視:

Imgur

將畫面改成 JSX

在上面版哥的程式範例中,仍然是使用 HTML 和 CSS,為了熟悉之後的實作,今天要做的事情很簡單,就要請你把上面的 UI 改成透過已經學習過的 JSX 來完成就好了,如此你將更熟悉這個樣板的 HTML 結構,和 JSX 的使用。

以下幾點是之前我們學過,而在切換成 JSX 過程中需要留意的:

  • 把 HTML 中的 class 屬性改成 className
  • 當標籤內沒有內容時,可以讓該元素自行關閉(self-closing)
  • 一個 JSX 最多只能有一個外層元素

完成後的程式碼可以參考 Day 9 - Network Speed Converter - Started Template @ CodePen。明天我們在用這個樣版繼續做延伸說明。

重要補充:一個 JSX 元素只能有一個最外層元素

在先前使用 JSX 的過程中,我們經常提到「一個 JSX 元素只能有一個最外層元素」。這是什麼意思呢?以下面的例子來說,在 Counter 這個組件的 JSX 中,只有一個根節點,就是最外層的 <div class="container">...</div>

const Counter = () => (
  <div class="container">
    <!-- ... -->
  </div>
);

錯誤作法

但若我們在這個 JSX 元素中,放入另一個節點 <div class="other-container">...</div> 的話,是不被允許的:

// ❌ 這是不被允許的
const Counter = () => (
  <div class="container">
    <!-- ... -->
  </div>
  <div class="other-container">
    <!-- ... -->
  </div>
);

你會在 CodePen 編輯器的右下方看到一個「驚嘆號」,表示程式本身有錯誤:

Imgur

點下去這個驚嘆號,它會顯示詳細的錯誤內容:

Imgur

正確做法一:外層的包一個 HTML Tag

如果需要的話,外層可以多包一個 HTML 標籤,例如 <div> ,這樣這個 JSX 元素的最外層仍然只有一個根節點:

// ? 外層多包一個 `<div>`
const Counter = () => (
  <div>
    <div class="container">
      <!-- ... -->
    </div>
    <div class="other-container">
      <!-- ... -->
    </div>
  </div>
);

這時候畫面就可以正確顯示。

正確做法二:使用 React Fragment

但有些時候,你不希望自己的這些元素外層還要額外包一個 HTML 標籤時,React 提供了一個 <React.Fragment> 的標籤讓你使用,寫起來會像這樣:

const Counter = () => (
  <React.Fragment>
    <div class="container">
      <div class="chevron chevron-up" />
      <div class="number">256</div>
      <div class="chevron chevron-down" />
    </div>
    <div class="other-container">
    </div>
  </React.Fragment>
);

如此,就可以解決 JSX 外層元素只能有一個根節點的情況,同時當我們透過瀏覽器的 console 視窗來檢視時,原本的 HTML 元素外層不會再被多包一個 <div> 標籤(畫面左側 #root 裡面多了一個 <div>):

Imgur

由於開發者大多很簡潔懶惰,能用簡短而清楚的方式來表達意思自然是最好不過的,因此,<React.Fragment> 還可以縮寫成 <>,蛤?你問我縮寫成什麼?就是 <>,沒錯,你不需要再寫落落長的 <React.Fragment></React.Fragment>,只需要寫 <></>,像這樣:

// <></> 是 <React.Fragment></React.Fragment> 的縮寫
const Counter = () => (
  <>
    <div class="container">
      <div class="chevron chevron-up" />
      <div class="number">256</div>
      <div class="chevron chevron-down" />
    </div>
    <div class="other-container">
    </div>
  </>
);

程式範例

參考資源


上一篇
[Day 08 - 計數器] 一個不夠,給我一次來十個 - JSX 中迴圈的使用
下一篇
[Day 10 - 網速換算器] 換算起來吧 - 資料綁定與組件拆分
系列文
從 Hooks 開始,讓你的網頁 React 起來30

尚未有邦友留言

立即登入留言