感謝 iT 邦幫忙與博碩文化,本系列文章已出版成書「從 Hooks 開始,讓你的網頁 React 起來」,首刷版稅將全額贊助 iT 邦幫忙鐵人賽,歡迎前往購書,鼓勵筆者撰寫更多優質文章。
看了好多天的計數器,今天將讓我們換個主題吧!在開始之前,先來討論一下什麼是網速吧!
Mbps
是用來計算網路頻寬最常見的單位,自從大家升級到 4G 的行動網路後,如果你不是使用吃到飽的使用者,勢必會看到「降速」或「限速」這兩個詞,可是你有想過最常見的降速 20Mbps
、或者限速 5Mbps
是什麼意思嗎?
但因為最常接觸到的單位常常是 MB
,因為這常用在傳輸檔案或儲存空間上。因此有些不太清楚的店員可能會跟你說 20Mbps
就是每秒鐘有 20MB
的網路傳輸速度,但真的是這樣嗎?錯!20Mbps 完全不等於每秒鐘有 20MB 的網路傳輸量。
又或者你現在想要追一下最近很紅的「俗女養成記」,怕網速太慢影響看劇的興致,於是打開 Netflix 提供的測速網站 Fast.com 想要測一下網速:
測完發現有 300Mbps
,可是你有想過,這個 300Mbps
是什麼意思嗎?這絕對不是表示每秒鐘有 300MB 的網路頻寬。
Mbps
和 MB/s
雖然不同,但這兩個單位之間是可以轉換的。那麼多少 Mbps
才會等於 1 MB/s
呢?
在後面幾天,就讓我們來做一個網速單位換算器吧!
要做網速單位換算器之前,我們要先來了解 Mbps
到底是什麼意思,它又要怎麼轉換成 MB/s
。
實際上 Mbps
中的第一個 M
是英文的 million,也就是「百萬」; 小寫 b
是 bit
的意思,中文稱作「位元」;後面的 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
以上:
今天就先讓我們來完成 UI 的部分吧,這主要是參考 Rizky 在 dribbble 的設計。
因為 HTML 和 CSS 並不是我們主要著墨的內容,所以 CSS 的部分版哥都已經幫你完成好了!請你打開 CodePen 的 Day 9 - Network Speed Converter with only CSS,然後按下 Fork。
在這個 CodePen 中,除了之前就有使用過的 React 套件之外,這裡還載入了 FontAwesome 這個套件,FontAwesome 提供了非常多的圖示可以使用,它有分免費版和付費版,在非常多網站上都可以看到它的蹤影:
HTML 的部分,最外層的結構長這樣子:
<div class="container">
<div class="card-header"><!-- ... --></div>
<div class="card-body"><!-- ... --></div>
<div class="card-footer"><!-- ... --></div>
</div>
card-header
和 card-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-control
和 converter
:
<!-- ... -->
<div class="card-body">
<div class="unit-control">
<!-- ... -->
</div>
<div class="converter">
<!-- ... -->
</div>
</div>
<!-- ... -->
unit-control
和 converter
的部分,裡面則都分別切分成左、中、右三個區塊。
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 檢視:
在上面版哥的程式範例中,仍然是使用 HTML 和 CSS,為了熟悉之後的實作,今天要做的事情很簡單,就要請你把上面的 UI 改成透過已經學習過的 JSX 來完成就好了,如此你將更熟悉這個樣板的 HTML 結構,和 JSX 的使用。
以下幾點是之前我們學過,而在切換成 JSX 過程中需要留意的:
class
屬性改成 className
完成後的程式碼可以參考 Day 9 - Network Speed Converter - Started Template @ CodePen。明天我們在用這個樣版繼續做延伸說明。
在先前使用 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 編輯器的右下方看到一個「驚嘆號」,表示程式本身有錯誤:
點下去這個驚嘆號,它會顯示詳細的錯誤內容:
如果需要的話,外層可以多包一個 HTML 標籤,例如 <div>
,這樣這個 JSX 元素的最外層仍然只有一個根節點:
// ? 外層多包一個 `<div>`
const Counter = () => (
<div>
<div class="container">
<!-- ... -->
</div>
<div class="other-container">
<!-- ... -->
</div>
</div>
);
這時候畫面就可以正確顯示。
但有些時候,你不希望自己的這些元素外層還要額外包一個 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>
):
由於開發者大多很簡潔懶惰,能用簡短而清楚的方式來表達意思自然是最好不過的,因此,<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>
</>
);