iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 7
4
Modern Web

在 React 生態圈內打滾的一年 feat. TypeScript系列 第 7

Day06 | 一次說完 JSX 基本用法

前言

本來打算直接從 Hooks 開始說起 React,但後來發現還是得先理解基本的操作方法,後續的文章會看得比較輕鬆,但我不會對語法著墨的太深,畢竟接下來的 Hooks 會改變一些語法,於是說說 Hooks 前,先來熟悉一下 React 的 JSX 基本用法吧!


前置準備

  1. 文中的專案會以前一天的專案架構繼續講解,如果未跟到前一天的進度,可以從 GitHub 上 Clone 下來。
  2. 本篇會稍長,要擁有一個比平常更有學習熱忱的心。

JSX

JSX 並不是一種新的語言,只是用了 HTML 和 JavaScript 的語法特性組成,可以把成理解成在

在 HTML 中穿插 JavaScript,也能說是在 JavaScript 中使用 HTML,

或是更正確的說法,JSX 是 React 所提供的語法糖,它讓我們省去操作 React 頂層複雜的 API,詳細可以查看前幾章節的 Day03 | JSX 瀏覽器看不懂?要翻譯就靠 Babel

但它用在哪裡呢?

Component

Component 是 React 中的核心元素,將頁面的各個部分切成一塊塊 Component,並在頁面上組合拼湊,因此

每個 Component 都是獨立個體,又能夠一同運作,

不需再重複 Coding 一樣的程式碼和邏輯,就是我們寫下 Component 的目標,

而 JSX 就是 Component 的輸出值。

輸出的過程也沒有想像中的困難,這裡得看好囉!首先,下方是一段 HTML 的 Hello World:

<div>Hello world!</div>

然後下方是一段 JavaScript 的 Function,它也回傳了字串 Hello World :

const GetHelloWorld = () => 'Hello World';

相信大家對上方的兩行程式碼都在熟悉不過,甚至覺得有點被污辱到智商了,但是相信我,JSX 就只是回傳了 HTML 的 JavaScript 而已:

const HelloWorld = () => <div>Hello World!</div>;

現在最好的驗證方式是,大家可以打開從筆者 GitHub 上 Clone 下來的程式碼,打開 src/index.jsx,它應該會長這樣子:

而學會了 JSX 的我們,可以將第五行改成上方演示的 HelloWorld Function,這裡要注意下方的 ReactDom.render 也要改成抓取 HelloWorld 而不是 Main

修改後使用 npm run start 運行專案:

是個完美的結果對吧!

CSS

再來是 CSS 部分,其實這些地方前面幾篇都有稍微提過了,但是為了文章的連貫性,一直沒有解釋為什麼這麼做,而是請大家複製貼上,真的很不好意思。

話說回來,有時候在寫 HTML 的時候,會將 Style 另外寫,然後用 class 指定 DOM 擁有該樣式,這麼做就不會把 HTML 和 CSS 混得亂七八糟,但是在 JSX 中的 class 不是 class,而是 className,style 則是從另外一個檔案中作為物件 import 進來。

下方先開啟 src/index.scss,可以看到目前的樣式:

.main {
    color: #faf;

    .point {
        color: #faf;
    }
}

現在另外加上一個 mainBlockground

.main {
    color: #faf;

    .point {
        color: #faf;
    }
}

.mainBackground {
    background-color: #e7e7e7;
}

回到 src/index.jsx,可以看見上方已經有將該 index.scss 給 import 進來,名稱叫做 styles,他是一個物件在 index.scss 中設定的 class 樣式都可以從它裡面取得。

import 後,替上方的 HelloWord Component 增加 className 屬性,然後從 styles 物件中取出樣式的值:

上方替 div 加上了 className,然後需要注意的是我把 JavaScript 放到 {} 中,對的,如果要替 JSX 裡的 HTML 添加 JavaScript 語法,就得加在大括號 {} 中,結果如下,HelloWorld! 已經擁有背景:

另外,因為 classNams 也可以設定多個內容,只是就得用字串連接的方式,串起多個 class:

重點在第 6 行組合了兩個 Class,結果如下:

inline style

如果有必要,就要將 CSS 寫在 HTML 裡,JSX 裡的 style 屬性需要包裹著一個 Object,如同從 index.scss import 的 styles

由一個 key 對一個 value,分別就是要設定的 CSS 屬性和它的值,這裡不要忘了,因為我們要給 Object,而 Object 又是 JavaScript 的格式,所以別忘了替它加上大括號:

這裡要注意的是 font-size 在 JSX 中被寫為 fontSize,那是因為 JavaScript 中的 Object key 不能有 - 號,因此 inline 的 style,都要用駝峰式取得 CSS 樣式名稱,例如: background-color 就要寫成 backgroundColor

當然,如果你堅持使用 CSS 的短橫線,也可以這麼做:

將所有的 key 都變成字串,就不會出現短橫線的錯誤了,上方兩種版本出來的結果是相同的:

Props

每個 Component 都會自帶一個參數 Props,用來接收使用時賦予的資料。

這個 Props 是讓 Component 能夠重複使用的關鍵之一,假設現在除了 Hello World! 外,還要再 Hello Sun,Hello Air,Hello Water,就可以用 Props,

觀察相似 Component 中的差異部分,將它抽出來用 Props 控制

Props 的用法就像 HTML 屬性一樣,在使用 Component 的時候,傳入自己設置的屬性作為 Props:

然後回到 Component 中,便可使用 Props 接上 name 的值:

// 自帶傳入 props 參數
const HelloWorld = (props) => {

  // 接收 Props
  const name = props.name;
 
  /* 其餘省略 */
}

再來將原本 Component 的 World 替換成 props.name,這時候因為 HelloWorld 不只在 Hello World! 了,就把它改成 SayHello,修改一輪後會變這樣子:

那現在,就能試著以不同的 name 傳入 SayHello

並從網頁上確認 Render 後的結果:

然後也許大家會發現抓取 Props 的部分,被 ESLint 檢查錯誤:

這裡三天後會再來解釋,各位就先放置沒關係 :D

然後 Component 能夠接收的 Props 不只有 String 或 Number

只要你想得到,Function、Object,或是另一個 Component,都可以藉由 Props 給予 Component。

最後 Props 它會自帶一個預設的屬性 children,只要我們這麼使用:

那 React 就會幫我們將 OtherComponent 這個 Component 放入 SayHelloprops.children

Loop 迴圈

上方的例子就很適合做成迴圈,因為我不過就只是在 Render 需要説 Hello 的對象而已,對吧!

第一步,在使用時將要說 Hello 的對象改成 Array 陣列 ,並將他傳入 names 的 Props 裡面,注意在 HTML 的部分只要有 JavaScript 就得用 {} 大括號:

第二步要到 SayHello 的 Component 內,接收新的 Props ,並對他使用 map,Return 每次的結果:

這裡大家可以多思考一下,程式運行的流程:

  1. map 讀取 names 裡的每一個值。
  2. 將讀到的值以 name 送入要 Return 的 DOM 裡。
  3. 最後再將產生的 DOM 陣列整包回傳。

結果會和剛才一樣,但程式的語意上就更清楚,也減少 coding 或以後需求變動時的重複動作。

需要注意的是使用迴圈的時候沒有用 {} 大括號,是因為迴圈是在 HTML 外做的,如果是在 HTML 之間仍然要使用。

另一點是 key 的部分,眼尖的各位應該都有注意到,div 明明就沒有 Props,為何要傳入 key

其實這是 React 用來幫助 Render 效能的小彩蛋,雖然不輸入也能夠正常運作,但是輸入了在 Render 的效能會更好,也因為 React 是以 Key 去處理效能,所以理想狀況是給它一個該筆資料固定的值,例如 id,如果以 index 設成 key 就有可能每次都會不同,因此對效能也沒幫助。

然後是 Key 的位置,要記得放在迴圈內 Render 的第一層,且 key 算是 React 的保留字,因此假如你是 Loop Render 自製的 Component ,那在該 Component 內是無法從 Props 內獲得 Key 值的。

總和以上兩點,假設我將程式改成下方, 14 行的 Loop 在 HTML 裡,就得使用 {}key 也在專屬它的第一層:

這裡再提醒一下,map 的警告和 names 很有關係,再麻煩大家忍到三天後吧 :)

if 判斷式

本篇最後要提及的技巧就是 if,一樣接續上方的例子,如果我們無法保證傳入的 names,在每個索引內都有值,那為了不讓畫面空白,就要去判斷如果這次要打招呼的對象是空的,就預設對 World 打招呼。

這個部分非常簡單,不論是一般的 if 語句或是 ? 都可以直接使用,畢竟本來就是 JavaScript,只是要注意得用 {} 大括號包著:

也可以另外寫一個 Function 來做判斷,讓語意上更清楚:

不只有內容,整個 JSX 都在你的掌握中,看到那 className 嗎?讓我們替預設的文字取消掉 styles.main 的樣式:

接下來在 Render SayHello 的地方,給 names 亂入一個空的索引值:

最後結果如下:

本文的範例程式碼會記錄在筆者的 GitHub 上,歡迎各位參考使用!


結尾

是不是既有趣又方便,雖然一開始可能會不習慣 HTML 及 JavaScript 的混合用法,但是只要多 Coding,這些都不是問題,如果有問題或漏講的觀念、好奇什麼,再麻煩各位留言告訴我!謝謝大家!


上一篇
Day05 | F5 按鍵守護者,讓網頁自動刷新的 Webpack-dev-server
下一篇
Day07 | 從 Hooks 開始的 Component 新生活
系列文
在 React 生態圈內打滾的一年 feat. TypeScript31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
huli
iT邦新手 3 級 ‧ 2019-09-18 20:02:15

「那 React 就會幫我們將 OtherComponent 這個 Component 放入 SayHello 的 props.chuldren」,不小心打錯字XD

看更多先前的回應...收起先前的回應...
神Q超人 iT邦研究生 5 級 ‧ 2019-09-18 20:06:16 檢舉

真的耶!好感謝你那麼認真看 /images/emoticon/emoticon02.gif

後設鐵人中的建議我也都有注意,我會在鐵人賽結束後將建議的內容補上的

huli iT邦新手 3 級 ‧ 2019-09-18 20:09:52 檢舉

當局者迷旁觀者清(?),我也只是剛好掃過去看到XDD

話說我覺得這篇寫得滿不錯的,很清楚然後循序漸進,不知道之後會不會有揭開 JSX 神秘面紗的部分,讓大家看看 JSX 被 babel 轉譯之後的長相

神Q超人 iT邦研究生 5 級 ‧ 2019-09-18 20:35:00 檢舉

有時候真的要隔一天重新 Review 文章才會發現一些愚蠢的錯誤 XDD

關於 JSX 的神秘面紗,我應該會採納你提供的建議,把它放在 Day03 | JSX 瀏覽器看不懂?要翻譯就靠 Babel 這篇,讓初學者更清楚為什麼需要 Babel :)

神Q超人 iT邦研究生 5 級 ‧ 2019-10-17 08:51:16 檢舉

YAYA!已在 Babel 中補上 JSX 的神秘面紗 /images/emoticon/emoticon69.gif

我要留言

立即登入留言