RZ-Huang
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # React.js 基本概念與引入 CSS 的方式 ###### tags: `javascript`、`react` [reference]: https:// "React.js 簡介與環境建置" ## 前情提要 如果還沒建置 React 的環境,請參考[這篇文章](https://hackmd.io/c9_NvnJkROyUkJoKSCgj5A) > 注意!下面的程式碼幾乎都省略了開頭的 `import React, { Component } from 'react'` 和 結尾的 `export default App` ## Component 與 render function 的交織 在 React 當中,每個東西都是 Component,最後呈現的前端畫面都是 Component 合併在一起的結晶。你也可以在一個 Component 裡面放另外一個 Component,如下所示: ```jsx= import React, { Component } from 'react' class Text extends Component { render() { return ( <p>Hi,I'm a text</p> ) } } class Title extends Component { render() { return ( <h1>The Title man is me</h1> ) } } class App extends Component { render() { return ( <div> <Title /> <Text /> </div> ) } } export default App ``` 在上面的例子可以看到在 `App` 這個 Component 中,又加了 `<Title />` 與 `<Text />` 兩個標籤,這兩個分別代表了 `Title` 和 `Text` 的 Component,而整個執行順序是當遇到了 `App` 這個 Component(假設在 `index.js`檔案(這個是 webpack 打包的 entry 檔案)只有設定引入 `App` Component)就開始 render 它的畫面,然後接著遇到了 `<Title/>` 就 render `Title` 這個 Component 的畫面;以此類推。 ## JSX 語法 在上面 Component 的那段程式碼當中,我們可以看到在 `App` 的 Component 裡,`return` 回去要 render 出來的 HTML 語法不是正規的 HTML 語法: ```jsx= <div> <Title /> <Text /> </div> ``` 雖然 HTML 實際上也可以使用這樣的方式來建構,但是各方面功能的支援度並不高。 而上面這樣的寫法被稱作「JSX」,可以把它看作 HTML 與 JavaScript 的合體語法,但它既不是 HTML 也不是一般的字串,有興趣研究的可以到 React 官網閱讀 JSX 的詳細介紹:[Introducing JSX](https://reactjs.org/docs/introducing-jsx.html) #### 新增 class 屬性 在 JSX 語法,如果要對標籤新增 `class` 屬性的話,需要輸入 `className` 而不是 `class`: ```jsx= <div className='Title'> <Title /> <Text /> </div> ``` #### 放入 JavaScript 的變數到 JSX 我們可以在 JSX 的語法當中放入所建立好的 JavaScript 變數,像下面這樣: ```jsx= class App extends Component { render() { const titleClass = 'title'; return ( <div className={titleClass}> <Title /> <Text /> </div> ) } } ``` 在 `const titleClass = 'title';` 這邊我們定義了 `titleClass` 這個變數的值是 `title`。 而在 JSX 的語法當中,我們可以使用 `{}` 大括弧的用法,在大括弧裡面放入 JavaScript 的變數,這樣就可以把變數的值給帶進去 JSX 語法了,如上面例子的 ` <div className={titleClass}>`,而這個用法就像是在 JavaScript 的字串當中要放入變數的概念一樣:`<div class=${titleClass}` ,只是語法上有所差異。 另外在標籤裡面新增 CSS 屬性在 HTML 的預設當中必須使用 `style` 屬性,在 JSX 裡也是一樣的,不過帶入的值要參照上面所說的 `{}` 大括弧格式,像是這樣: ```jsx= class App extends Component { render() { const titleClass = 'title'; const styleAttr = { color: 'red', } return ( <div className={titleClass} style={styleAttr}> <Title /> <Text /> </div> ) } } ``` `styleAttr` 的值就是要帶入 JSX 語法中的 CSS 屬性,當然你也可以直接這樣寫就不必另外新增一個變數: ```jsx= class App extends Component { render() { const titleClass = 'title'; return ( <div className={titleClass} style={{ color: 'Green', }}> <Title /> <Text /> </div> ) } } ``` 要記得在大括弧裡面再加一次大括弧(因為 CSS 屬性新增時所需)。 #### CSS 屬性駝峰式命名 另外,在 CSS 的屬性中,有些屬性名稱中間是有 `-` 的符號,像是 `font-size`,但是如果加在 JSX 的語法裡,這樣的用法是錯誤的,要以駝峰式的命名才行,如下: ```jsx= class App extends Component { render() { const titleClass = 'title'; return ( <div className={titleClass} style={{ color: 'Green', fontSize: '50px', // 駝峰式命名 }}> <Title /> <Text /> </div > ) } } ``` #### return 的寫法 拿上面的例子來說: ```jsx= class App extends Component { render() { const titleClass = 'title'; return ( <div className={titleClass} style={{ color: 'Green', fontSize: '50px', // 駝峰式命名 }}> <Title /> <Text /> </div > ) } } ``` 在 `return` 的地方會看到頭跟尾有加 `()` 的符號,主要原因在於想要回傳的內容並沒有與 `return` 同一行,因此必須加上 `()` 的符號,否則會 `return` 失敗,但如果欲回傳的內容的第一行和 `return` 在同一行就不需要加了,像下面這樣: ```jsx= class App extends Component { render() { const titleClass = 'title'; return <div className={titleClass} style={{ color: 'Green', fontSize: '50px', // 駝峰式命名 }}> <Title /> <Text /> </div > } } ``` ## React 的事件機制 以往我們寫 HTML 和 JavaScript 時,語法是分開的,像是事件監聽,我們必須先建立好 HTML 的結構後,再去寫 `document.querySelector('.title').addEventListener()` 這麼一大串的語法才可以把所有功能與架構安置好。 但現在,我們使用 React,就可以藉由 JSX 的語法讓 HTML 和 JavaScript 的寫法結合起來,像是上面所說的事件監聽,在 JSX 當中我們只要像下面這麼寫,功能會是一模一樣的: ```jsx= class Title extends Component { render() { function titleClick() { alert('The Title'); } return ( <h1 onClick={titleClick}>The Title man is me</h1> ) } } ``` 而你也可以這麼寫(箭頭函式): ```jsx= class Title extends Component { render() { return ( <h1 onClick={() => { alert('The Title'); }}> The Title man is me</h1 > ) } } ``` 值得注意的是,點擊的事件監聽的屬性是駝峰式的命名:`onClick()`;傳送表單的事件監聽則是 `onSubmit()`。 ## state 在上述的實作當中,我們僅提到 React 的 Component 概念,接著要再加上 React 的另一個很重要的觀念,也就是 **state**(狀態),有了 state,我們就可以根據 state 的改變做出相對應的 UI 畫面呈現(Component 的不同結合方式)。 ### state 的做法 最簡單在 JSX 當中使用 `state` 的做法如下: ```jsx= class App extends Component { state = { counter: 1 } render() { return ( <div> <h1>hello</h1> <div>{this.state.counter}</div> </div> ) } ``` 但是結果是失敗的,打開 DevTools,呈現的錯誤訊息是: ![new state syntax error](https://i.imgur.com/eoRUsy3.png) 上面的意思是說,所使用的 `state` 語法其實還在實驗階段,目前不能直接使用,如果要直接使用的話,必須安裝 `@babel/plugin-proposal-class-properties` 的 plugin 才能做使用。 還在實驗階段的話,我們使用最原始的做法較保險: ```jsx= class App extends Component { // JavaScript 的 OOP 繼承用法 constructor() { super() this.state = { counter: 1 } } render() { return ( <div> <h1>hello</h1> <div>{this.state.counter}</div> </div> ) } } ``` 畫面呈現就會是這樣: ![](https://i.imgur.com/slSUSEb.png) 進一步把 `hello` 字串放到 `state` 裡面: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } } render() { return ( <div> <h1>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 把 `hello` 字串加到 `this.state` 當中,再藉由 `this.state.title` 把值(`hello`)給放入 JSX 語法裡。 ### setState() 如果我們要改變 `state` 當中的值,必須使用 `setState()` 的語法,如下: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } } render() { return ( <div> <h1 onClick={() => { this.setState({ counter: this.state.counter + 1 }) }}>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 結合 `onClick()` 語法,當點擊 `<h1>` 標籤時,`state` 裡的 `counter` 就會加 1。而這個 `setState()` 的用法比較像是 HTTP Method 的 `PATCH` 而非 `PUT`,簡單說就是它只會「更新」你要更新的 `state` 值(在這個例子就是只會改到 `state` 的 `counter` 值,`title` 的值不會被改到),而不是所有的 `state` 值被你「更新的內容」所取代。 另外要注意的是,在 `onClick()` 裡面的 function 必須使用**箭頭函式**,因為如果使用 `function () {}` 匿名函式的語法的話,裡頭的 `this.setState()` 所指的 `this` 不會是 `App` 這個 Component 本身,而是 `onClick()` 這個函式,但是這個函式的 `this.setState()` 呈現的結果會是 `undefined`,很合理,因為它本身就沒有 `setState()` 這個 method 嘛。而箭頭函式比較特別的是,它會根據呼叫它的地方的上層 `this` 為它的 `this` 值,也就是箭頭函式被定義時就決定好 `this` 的值了,這點與正常使用 `this` 時的性質不太一樣,簡單講就是下方 `console.log(this)` 的值: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } } render() { console.log(this) // 這個 return ( <div> <h1 onClick={() => { this.setState({ counter: this.state.counter + 1 }) }}>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 另外一個 setState() 的用法: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 但是這樣寫百分之百是錯的,為什麼?理由跟第一個用法不能使用非箭頭函式一樣,這樣寫的話,`this.handleClick` 所指的目標(也就是在 `App` 的 `handleClick` 這個函式) 本身裡面的 `this` (也就是 `this.setState` 的 `this`)會是 `handleClick` 這個函式本身,而非 `App` 這個 Component 本身。因此我們必須再加上 `bind()` 來給定 `handleClick` 的 `this` 值固定為何: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } // 這樣 this.handleClick 回傳的值就都會是 this 了(這邊的 this 指的是 App Component) this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 而你也可以把 `bind()`寫在 `onClick()` 裡面: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick.bind(this)}>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 不過最好的做法還是上面那一個,比較方便後續其它的操作。 然而,實際上還有第三種 `setState()` 的做法: ```jsx= class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } } // 改成很像箭頭函式的物件(實際上叫做 Class Property)並且把函式儲存在變數當中 handleClick = () => { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <div>{this.state.counter}</div> </div> ) } } ``` 可是這樣的做法會跑出剛才上面我們要引進 `state` 的其中一個方法有出現的錯誤: ![](https://i.imgur.com/xOp13rI.png) 一樣是叫你要安裝 `@babel/plugin-proposal-class-properties` 的 plugin,所以這個`setState()` 的方式也是還在實驗中的用法,不過是比較省事的用法。 ## props 與 state 的合體技 上面提到可以在 Component 之中加上 `state`,但如果今天 Component 裡面又有另外一個 Component,那麼其它的 Component 要如何拿到使用 `state` 的 Component 的資料?像是這樣的範例: ```jsx= class Counter extends Component { render() { return ( <div>test</div> ) } } class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <Counter /> // update here </div> ) } } ``` `Counter` 加到 `App` 這個 Component 裡面了,那像上面所說的,`Counter` 如果也要拿到在 `App` 當中產生的 `state`,該怎麼辦? 來,可以這麼做: ```jsx= class Counter extends Component { render() { return ( <div>{this.props.number}</div> // update here ) } } class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <Counter number={this.state.counter} /> // update here </div> ) } } ``` 在 `<Counter />` 的標籤裡面,再加上 `number={this.state.counter}`,有點像是加上了一個 attribute,然後在 `Counter` Component 裡面要獲取 `number` 的值的話,就加上 `this.props.number`。 而這整個過程,就是 React 的 `props` 機制,利用 `props` 當作媒介,去獲取在別人家所放的東西,就像是邦交國一樣,我國請一位外交官駐點(如`number`)在別人家的領事館(如 `<Counter />`),然後外交官藉由打電話或是其它方式回傳(這邊指的是 `props` 機制)別人家的訊息(如 `this.props.number`)回來我國(如`Counter` Component)。 因此使用 `props` 很輕易地就可以拿到 `App` 的 `state`(或是 function) 資料到其它 Component 上應用,這個機制可以讓相關的畫面(相關 Component)因 `state` 改變而同時更新資料,接著重新渲染 UI。 還有這種寫法: ```jsx= class Counter extends Component { render() { return ( <div>{this.props.children}</div> // update here ) } } class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <Counter> {this.state.counter} // update here </Counter> </div> ) } } ``` 把 `Counter` 當作一般的標籤一樣,在標籤之間的所有內容,會回傳到 `this.props.children` 當中,而 `children` 就是表示在標籤之間的所有內容。 也可以回傳非 `state` 的內容,直接在 `App` Component 建立 `Counter` Component 的內容: ```jsx= class Counter extends Component { render() { return ( <div>{this.props.children}</div> ) } } class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <h1 onClick={this.handleClick}>{this.state.title}</h1> <Counter> // update here <a href="http://www.google.com"> {'counter hi'} </a> </Counter> </div> ) } } ``` 畫面就是這樣: ![](https://i.imgur.com/ztj9x3f.png) ## functional Component 除了 `class Component` 之外,其實還有 `functional Component`,如果 `class Component` 長這樣: ```jsx= class Counter extends Component { render() { return ( <div>{this.props.number}</div> ) } } ``` 那麼 `functional Component ` 就是長這樣: ```jsx= function Counter(props) { return ( <div>{props.number}</div> ) } ``` 可以藉由參數放入 `props` 當作橋梁來獲取在其它 Component 上面在 `Counter` 標籤的值。 而你也可以使用 ES6 的解構語法: ```jsx= function Counter(props) { const { number } = props return ( <div>{number}</div> ) } ``` 還有更偷懶的方式: ```jsx= function Counter({ number }) { return ( <div>{number}</div> ) } ``` 也可以寫成箭頭函式: ```jsx= const Counter = ({ number }) => { return ( <div>{number}</div> ) } ``` 那,`class Component` 和 `functional Component` 究竟有什麼不同呢? 簡單來說,`functional Component` 做得到的事,`class Component` 都做得到,但是反過來說,`functional Component` 不能使用 `state`,因為沒有繼承 Component 的 class,自然不會有 `this`、`constructor` 那些用法,甚至類似的其它功能 `functional Component` 都沒辦法使用。 那麼使用 `functional Component` 的時機為何?**只要你只是單純想要在 UI 的某一塊加上幾行元素(標籤)並且不使用其它功能,那就可以用 `functional Component`** 來寫會比較簡潔與便利,畢竟每次寫 `class` 都要一大串,要寫 `extends` 還要 `render()` 有的沒的。 ## Component 之間的溝通流程 以這個例子為例: ```jsx= const Counter = ({ number }) => { return ( <div>{number}</div> ) } const Title = (props) => { return ( <h1>{props.text}</h1> ) } class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> <Title text={this.state.title} /> <Counter number={this.state.counter} /> </div> ) } } ``` 如果我們要在畫面按下 `Title` 標籤的內容的時候可以讓 `App` 之中的 `state` 的 `counter` 加 1 該如何做?這牽涉了 Component 之間傳遞的問題。而在這個例子當中,我們稱呼 `App` 為 Parent Component;`Title` 和 `Counter` 為 Children Component,現在我們要解決的目標是,在 `Title` 這個 Children Component 觸發事件的時候,要能傳遞並反應到 Parent Component 的 `state` 當中。 可以這麼做: ```jsx= const Counter = ({ number }) => { return ( <div>{number}</div> ) } // update here const Title = (props) => { return ( <h1 onClick={props.handleClick}>{props.text}</h1> ) } class App extends Component { constructor() { super() this.state = { title: 'hello', counter: 1 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ counter: this.state.counter + 1 }) } render() { return ( <div> // update here <Title handleClick={this.handleClick} text={this.state.title} /> <Counter number={this.state.counter} /> </div> ) } } ``` 在 Parent Component(`App`)當中的 Children Component(`<Title/>`)標籤加上 `handleClick` 的 function,然後在 Children Component(`Title`)加上事件監聽 `onClick()` 並且藉由 `props` 指向 `handleClick` 這個 function。 整個傳遞的流程是這樣: 1. 先 render `Title` 的 `text` 值(hello),並且加上 `handleClick` 這個 function 和 render `counter` 的 `number` 值(1)。 2. 當觸發 `Title` 在 `<h1>` 的 `onClick()` 事件監聽時,等同觸發(或者是呼叫)了 `props.handleClick` ,也就是在 `App` component 的 `hanldeClick` funciton。 3. `handleClick` 被呼叫後,執行 `setState()` 讓 state 的 `counter` 的值加 1,來到了 2。 4. re-render `Title` 的 `text` 值(hello),並且加上 `handleClick` 這個 function(不過 React 很聰明的是沒有改變過的資料或 DOM 節點就不會重新 render)。 5. re-render `counter` 的 `number` 值(2)。 以上就是 Component 之間的傳遞過程,其中最關鍵就在第 2 的步驟。 你也可以在 `props.handleClick` 加上參數,像這樣:`props.handleClick(123)`,那麼那個 `123` 的引數就會傳到被呼叫的 function 的參數當中(但這個例子的 function 沒有參數,只是舉例)。 ## React 加上 CSS 的各種用法(門派) ### 第一種:按照慣例 最傳統的用法,也就是直接在 HTML 檔案引入 CSS 檔案: ``` <link rel='stylesheet' type='text/css' href='./style.css' /> ``` ### 第二種:css-loader、style-loader 藉由安裝兩個與 webpack 有關的 modules 引入 webpack 流程,就可以在 React 當中負責呈現畫面的 JavaScript 檔案直接 `import` CSS 檔案,這個概念有點像是第一種方法(在 HTML 引入 CSS 檔案),不過比較方便的點是這個方法在 webpack 流程並借助 React 的 library ,因此可以即時呈現更新的畫面。 首先安裝 `css-loader` 和 `style-loader`:`npm install css-loader style-loader` 接著,在 `webpackage.config.js` 檔案加上 `css-loader` 和 `style-loader`: ```javascript= module: { rules: [ { test: /\.css$/, loader: ['style-loader', 'css-loader'] } ] } ``` 這邊的 `loader` 還有另外一個較正規的寫法: ```javascript= module: { rules: [ { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' } ] } ] } ``` 值得注意的是,`loader` 的**執行順序為由右到左,或是由下到上**,在這個例子當中,`css-loader` 需要先被執行才是正確的執行順序。 接著在 `app.js` 檔案(也就是藉由 React 語法負責呈現 UI 的 JavaScript 檔案)中新增:`import "./style.css"`,這個 `style.css` 就是要引入的 CSS 檔案,接著 `npm run start` 查看是否有成功。 ### 第三種:sass-loader 也就是在 webpack 的流程再加上 CSS preprocessor 的處理,只要安裝 `sass-loader` 後,並且做一些和第二種方式類似的設定調整,就可以使用了,詳情請看 [sass-loader](https://www.npmjs.com/package/sass-loader) ### 第四種:styled-components https://www.styled-components.com/ 這是個很特別的方式,特別在於,你可以在 `app.js` 當中自定義標籤名稱,並且設定 CSS 屬性,同時也支援根據不同 `props` 產生不同的 CSS 屬性。 首先先安裝 `styled-components`:`npm install styled-components`。 安裝完畢後就可以直接在任何 JavaScript 檔案引入這個 module,我們引入到 `app.js` 檔案: ```jsx= import styled from 'styled-components' const Header = styled.h1` color: red; font-size: 20px; ` const Title = (props) => { return ( <Header onClick={props.handleClick}>{props.text}</Header> ) } ``` 第 1 行引入 `styled-components` 的 `styled` 用法,就可以開始自定義標籤,第 3 行的 `Header` 就是自定義所有的 `<h1>` 標籤在 `app.js` 的檔案取代為 `Header` 名稱並且擁有 `color: red; font-size: 20px;` 的 CSS 屬性。 因此你可以在第 10 行看到標籤由原本的 `<h1>` 改為 `<Header>` 了,但本質上 render 出的 HTML 結構它還是 `<h1>` 標籤,只不過是在 JavaScript 檔案定義的名稱不同。 ### 第五種:每個與 JS 檔案搭配的 CSS 檔案,屬性命名不衝突,並自動編譯 SCSS 檔成 CSS 檔 按照參考資料的文章安裝 `npm install node-sass` `npm install style-loader css-loader sass-loader --save-dev` `npm install mini-css-extract-plugin --save-dev` **注意**: 在開發時,更改 SCSS 的語法不會同步更新,解決方法: [webpack live hot reload for sass ](https://stackoverflow.com/questions/52043727/webpack-live-hot-reload-for-sass/52045575) #### 參考資料 [SCSS 加上 Webpack 混搭款,讓你寫 CSS 上天堂](https://ithelp.ithome.com.tw/articles/10215681) </br> 對於每個 CSS 的使用方式各有千秋,各有門派,可以自行決定哪一套比較適合你,比較好上手。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully