iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 4
5
Modern Web

一步一腳印的React旅程系列 第 4

[筆記][React]關於Components的那件小事

經過前兩天的折騰,總算把開發環境給處理好了,從孟母身上我們就可以知道環境有多麼重要,不只對人,連程式也不例外,對吧?所以讓我們從今天開始學習React


起手式

因為這一篇才算正式介紹起React,所以在這裡先簡單列一下之後例子的起手式,讓大家比較不會覺得搞不懂是怎麼開始的,首先HTML我會先建立一個id=rootdiv當作目標節點,且我們嵌入的是用webpack打包過後的JavaScript檔案bundle.js
HTML

<html>
<head>
</head>
<body>
    <div id="root"></div>
    <script src="bundle.js"></script>
</body>
</html>

然後我們修改的檔案都是app.jsx,如果有例外的話都會在另外說明,一開始會先匯入用npm下載的reactreact-dom
app.jsx

import React from 'react';
import ReactDOM from 'react-dom';

webpack也只處理app.jsx這個檔案,本機開啟的測試port號就不一定,如果大家發現是網址是9000就代表我是用桌電打,8080是筆電,不過這個一點關係也沒有XD:
webpack.config.js

const path = require('path');
module.exports = {
    entry: ['./app.jsx'],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './'),
    },
    module: {
        rules: [
            { test: /.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'] } } }
        ]
    },
    devServer: {
        port: 9000
    }
};

掰惹位,JSX比較常用的語法可以在第一天的文章中找到,下方就不會再說明,除非有比較特別的部分,另外之後也都不會提到打包這個動作了,所以如果還不太熟可以先看前兩天關於webpack的文章。

Components(組件)是什麼?

先來提提Element,對有寫過HTML的讀者來說,Element應該不是個陌生的東西,在網頁中從一整個頁面到小則一個小按鈕都可能是一個Element,當我們要在JSX中建立Element的話,最簡單的方式應該會選擇這麼做:

let title = <h1>Hello, world!</h1>

ReactDOM.render(title,document.getElementById('root'))

上方的寫法就和前幾天說的,用JSX建立的標題是一個ElementReactDOM.render中指定的document.getElementById('root')也是一個Element,並用ReactDOM.render將第一個Element放進第二個Element中,而在一般的網頁中,把各種不同或相似的Element給組合、拼湊在一起就會成為Components,所以在開發React的思維就是去思考在這個頁面中,有哪些Element是重複出現或是相似度高的,將這些ElementJSX建立成一個Components,讓每個Components擁有重複性及可擴充性,畢竟在程式界有個名言「Don't repeat yourself」,讓我們使用React實現它!

動態的Components

通常使用React建立的Components放到畫面上後是無法再被改變的,不過利用動畫的原理,每次都產生一個新的Components並重新插入網頁中,就能讓畫面產生變化,值得一提的是,React會在覆蓋前一次的Element前自動與目前的樣子進行比對,且只會更新不同的地方,這樣就能避免掉當Element很巨大的時候會產生的一些效能問題,如下例子:

//宣告一個匿名function
const displayTime = () =>{
  //建立一個顯示目前時間的組件
  let nowTime = (
    <div>
      /*在組件裡要註解得前後註解標記,下方則是利用大括號加入JS語法*/
      <span>現在時間:{new Date().toLocaleTimeString()}</span>
    </div>
  )
  //將上方的組件放進id為root的element中
  ReactDOM.render(nowTime,document.getElementById('root'))
}

//每隔一秒重新取得時間放到畫面上
setInterval(displayTime,1000)

執行後畫面上可以看到目前的時間,且不斷的更新,但是當我們更進一步打開瀏覽器上的開發者工具,選擇Element頁籤,就會發現和程式不同的是,現在時間:這個固定的字串只會在第一次被放進來,之後會重複刷新的只有每次都不同的{new Date().toLocaleTimeString()}這個部分而已:
https://ithelp.ithome.com.tw/upload/images/20180916/201069350m52U73wYT.jpg

但是從上面的例子我們也可以知道說,一個Components被宣告後就是不可動的,那問題來了,既然他宣告後就不可動了,那如果每個組件都差了那麼一點點,不都要重新宣告?

在解決上方的問題之前,我們先為之後的寫法來學習如何用class製作Component,當然classJavaScript的語法糖,其實比較單純的Component也還是可以用function來做到一樣的事情,如果不了解JavaScript建構器的概念可以看這邊[筆記][JavaScript]使用建構器創造實體物件只是這樣寫比較方便也比較潮

但是為什麼上面特別註明比較單純的Component呢?因為只有用class建構的組件才可以有他的生命週期在,不過這個留到下一篇文章,今天先來學基本的吧!

用Class做一個組件建構器

好的,先前我們使用直接將JSX的物件宣告給一個變數,但現在開始要試著用class的寫法來理解接下來的事情,以下是最簡單的例子:

//當我們宣告一個組件時,名稱第一字母必須大寫,這很重要ㄛ
class HelloTitle extends React.Component {
    //render是唯一必要的屬性,會回傳一個根Element
    render() {
        return <h1>Hello, world!</h1>
    }
}

以後不會再有的順帶一提,將上方寫法換成ES5是這樣子的:

function HelloTitle(){
    return <h1>Hello, world!</h1>
}

組件建立完成後,便可交給react-dom來處理它:

//將剛剛建立的HelloTitle放進id為root的Element中
ReactDOM.render(<HelloTitle />,document.getElementById('root'))

https://ithelp.ithome.com.tw/upload/images/20180918/20106935MPrp5yhPeC.png

是不是沒想想中那麼難?只是變成用class去宣告而已對吧!什麼?你問說既然一樣只是宣告要怎麼做些微的調整,嘖嘖!這就要看第一個出現的小幫手Props吧!

Props屬性

如果有使用過vue.js的大大們應該對這個屬性很熟悉,Props主要用來提供值給組件使用,不論是設定屬性或是資料,都可以透過Props來完成,因此就算是同一個組件,也會根據提供的Props而變得有所不同,例如:

//宣告一個Component組件
class HelloTitle extends React.Component {
    render() {
        {/*在花括號中使用JavaScript取得該組件的props屬性,再從中props中取name的值*/}
        return <h1>Hello, {this.props.name}!</h1>
    }
}

//宣告一個根Element,在裡面使用剛剛宣告的HelloTitle組件
let titleDiv = (<div>
                   {/*在JSX中註解需使用花括號及前後註解標記
                   在使用時指定name的值為GQSM,這會傳進class的props中*/}
                   <HelloTitle name="GQSM" />
                   {/*另一個組件則是指定name的值為Horse,這會傳進class的props中*/}
                   <HelloTitle name="Horse" />
               </div>)

//將剛剛宣告的titileDiv放進root中
ReactDOM.render(titleDiv,document.getElementById('root'))

https://ithelp.ithome.com.tw/upload/images/20180919/201069352Is3ei24r6.png

結果可以很明顯地看到,雖然都是使用HelloTtile,但是會因為設定的props而讓組件變得不同,而且為了保持資料流是單向的,每次傳進去總是會回傳同一個結果,props是不可被變動的。

另外Props是可以多層使用的:

class HelloTitle extends React.Component{
    render(){
        return <h1>Hello ,{this.props.name}!</h1>
    }
}

//在HelloDiv組件中使用HelloTitle組件,並透過props將name值傳給HelloTitle
class HelloDiv extends React.Component{
    render(){
        return <div>
                   <HelloTitle name={this.props.name} />
               </div>
    }
}

//使用HelloDiv組件並將GQSM傳入name裡面
ReactDOM.render(<HelloDiv name="GQSM" />,document.getElementById('root'))

結果:
https://ithelp.ithome.com.tw/upload/images/20180919/20106935GJyt61CHOW.png

以上範例雖然只有利用props來簡單變動組件內的內容,但其實不管是內容或是組件內的屬性也好,都可以利用這種方式去做設定,讓每個組件都可以重複利用,不會造成寫了一個A組件,卻要因為B組件的字體大小不同而又多寫一個組件的狀況發生,上述的例子可以以下方做法達成:

class HelloTitle extends React.Component {
    render(){
        return <p style={this.props.style}>{this.props.content}</p>
    }
}

class TitleDiv extends React.Component {
    render(){
        return (<div>
                    <HelloTitle content="比較大的字" style={ {'font-size':18} } />
                    <HelloTitle content="比較小的字" style={ {'font-size':12} } />
               </div>)
    }
}

ReactDOM.render(<TitleDiv />,document.getElementById('root'))

像上方一樣在style中傳入一個寫著樣式的物件,物件內用一個CSS屬性對一個值,並把他設給定給<p>style屬性,可以完成以下的效果:
https://ithelp.ithome.com.tw/upload/images/20180920/201069356xug0I0jNa.png


以上是關於在react中建立Component組件的一些例子,之後我們的使用都會圍繞在Component上,所以最基本的使用方式一定要熟悉,下一篇文章會來提組件的狀態和生命週期,還請大家多多指教!

如果文章中有任何問題或是解釋不清楚的地方,還麻煩各位大大留言告訴我,我會盡快回答及修正文章內容,感謝大家的觀看/images/emoticon/emoticon41.gif

參考文章:

  1. https://reactjs.org/docs/components-and-props.html

上一篇
[筆記][React]從零到一的webpack開發環境(2)-React開發篇
下一篇
[筆記][React]Component的狀態State及生命週期Lifecycle
系列文
一步一腳印的React旅程30

尚未有邦友留言

立即登入留言