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
    • 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 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 的生命週期 ###### tags: `javascript`、`react` ## 前情提要 對於 React 基本觀念還不熟悉的話,請參考[這篇文章](https://hackmd.io/PRs8nUPPQYG344wnG-ICtg) ## constructor 的作用 下面藉由兩種寫法認識: 1. constructor 的作用與注意事項 2. 介紹在 React 當中,通常如何撰寫 JSX 讓 Component 依據相對應的條件被移除或是新增。 第一種寫法: ```jsx= import React, { Component } from 'react' // Children Component class Title extends Component { constructor() { super() console.log('Title created') } render() { return ( <h1>title</h1> ) } } // Parent Component class App extends Component { constructor() { super() this.state = { isShow: true } console.log('App created') } render() { const { isShow } = this.state // 使用解構的寫法方便指定 state 的 isShow return ( <div> {isShow && <Title />} // 新增與移除 Component 的技巧 // 每次點按鈕就會把 state 的 isShow 設定到當前布林值的相反值 <button onClick={() => { this.setState({ isShow: !this.state.isShow }) }}>Toggle</button> </div> ) } } export default App ``` 第二種寫法: ```jsx= import React, { Component } from 'react' class Title extends Component { constructor() { super() console.log('Title created') } render() { // 大家在用 React 時很常使用的寫法,方 bang const { isShow } = this.props return ( // isShow 是 True 的話就顯示 <h1>;false 的話就隱藏 <h1> <h1 style={{ display: isShow ? 'block' : 'none' }}>title</h1> ) } } class App extends Component { constructor() { super() this.state = { isShow: true } console.log('App created') } render() { const { isShow } = this.state return ( <div> // 設定 isShow function 讓 chidren component 來呼叫 <Title isShow={isShow} /> <button onClick={() => { this.setState({ isShow: !this.state.isShow }) }}>Toggle</button> </div> ) } } export default App ``` ### 先看第一種寫法,來看看它的初始 render UI: ![render](https://i.imgur.com/TIIo965.png) 可以注意到 DevTools 的 Console 首先先印出 `App created` 再來才是 `Title created`,很合理,因為它是 Parent Component,會先被執行,所以理所當然 `constructor` 裡面的 `console.log('App created')`(第 22 行)結果會先被印出來,緊接著才執行 `render()`,碰到 `<Title />` 標籤才執行 `Title` 的 Component,有執行 `Title` 的時候,`Title` 的 `constructor` 才會被執行,進而執行 `console.log('Title created')`。 在程式碼當中我們設定 `Toggle` 字樣的 button 來當作顯示與隱藏 Component 的開關,這個寫法在 React 很常使用,因為在 JSX 語法不能直接寫 `if` 判別式,所以使用短路的方式來寫,也就是第 29 行的 `{isShow && <Title />}` 寫法,只要 `isShow` 是 `false`,那麼後面的 `<Title />` 就不會被顯示出來 and vice versa,因此這樣的做法就可以當作控制 Component 的新增與移除了。 回到這個例子,只要 `title` 字樣存在(也就是 `<h1>title</h1>` 在 DOM 上,表示 `<Title />` 存在於 `App` Component),表示目前的 `isShow` 是 `true`,那麼按了 `Toggle` 之後,`isShow` 就變成 `false`,因此 `<Title />` 這個標籤就不存在於 `App` Component,因此 `Title` Component 的元素就不會呈現在畫面當中,如下圖: ![](https://i.imgur.com/PhVAk4N.png) ### 兩種寫法的差別 實際上兩種寫法呈現的 UI 都是一樣的,所以我在上面僅有詳細介紹第一種寫法的 UI 呈現,但是!沒錯,就是有這個但是,兩種寫法對於 DOM 節點的呈現有所不同。 首先第一種寫法的 `<Title />` 標籤,存在於 `App` Component 與否必須根據 `isShow` 的值決定,當今天 `isShow` 是 `true`,它的 DOM 是長這樣的: ![](https://i.imgur.com/lHhJKyL.png) `isShow` 是 `false` 的 DOM: ![](https://i.imgur.com/85uNL5I.png) 很明顯的,當 `isShow` 為 `false` 的時候,`<h1>title</h1>` 這個 DOM 節點完全地消失於 DOM 上面,表示說 `<Title />` 這個標籤已經不在 `App` Component 裡面了。 而第二種寫法是 `<Title />` 這個標籤不管 UI 有沒有呈現相對應的 `Title` Component 的元素, `<Title />` 都會一直存在於 `App` Component 裡面,這代表什麼意思?就是整個 DOM 會一直存在 `<Title />` 的元素節點,它只不過是改變了 CSS 屬性所以導致沒顯示在畫面上而已,下面的兩張圖片我們可以分別查看 UI 沒有 `title` 字串和有 `title` 字串時,DOM 的差別: 畫面有 `title` 字串: ![](https://i.imgur.com/5lAZrpl.png) 畫面沒有 `title` 字串: ![](https://i.imgur.com/y3IeKtF.png) 顯而易見的是,`<h1>` 這個 DOM 節點一直存在著,只不過是 `display` 的值改變而已。 ### 與 constructor 的關係 今天如果使用第一種寫法,我們先去看 DevTools 的 Console: 這是一開始 render log 出來的字串並且先隱藏了 `title`: ![](https://i.imgur.com/qEj8Osg.png) 當再按一次 `Toggle`: ![](https://i.imgur.com/CpcBr8k.png) 會發現到多了一次 `Title created` 的 log,這代表什麼?表示說每當 `<Title />` 標籤從 `App` Component 當中連根拔除又把 `<Title />` 塞回去後,`Title` Component 的 `constructor` 就會被重新執行一次,在這個例子當中就是 `console.log('Title created')` 又被執行一次了,也就是說每次 Component 在 UI 被(重新)建立時,Component 的 `constructor` 這個 method 都會被執行,而且是先被執行,接著才是後續的其它 method(諸如 `render`)被執行。另外`App` Component 的 `constructor` 在這個例子當中,只執行了第一次,因為就只有 `export` `App` 的時候,`App` Component 被建立一次而已。 ### 更正確的 constructor 寫法 拿上面的 `Title` Component 來舉例: ```jsx= class Title extends Component { constructor() { super() console.log('Title created') } render() { return ( <h1>title</h1> ) } } ``` 原本我們的 `constructor` 都是這麼寫的,不過使用了 `constructor` 通常就是會需要使用到 `props`,因此最保險的做法其實應該在有使用 `constructor` 的地方都帶入 `props` 參數,也就是這樣: ```jsx= class Title extends Component { constructor(props) { super(props) console.log('Title created') } render() { return ( <h1>title</h1> ) } } ``` 這樣的寫法會是比較不會出什麼紕漏的寫法,否則會跑出莫名其妙的 bug。 如果 `class` 當中不需要使用 `construcotr` 的話(比如不需要 `props`),不需要建立 `construcotr` 也是可以的。 ## shouldComponentUpdate 這個單元我們以下面的程式碼作為範例: ```jsx= class App extends Component { constructor(props) { super(props) this.state = { number: 1 } } render() { const { number } = this.state return ( <div> <h1>{number}</h1> <div onClick={() => { this.setState({ number: this.state.number + 1 }) }}>Click Me</div> </div> ) } } ``` 介面長這樣: ![](https://i.imgur.com/MS2IWpu.png) 只要按了 `Click Me`,`state.numer` 就會被加 1,然後重新 render UI,而這整個過程就是 React 的基本用法,`state` 被改變了之後,會自動 call `render` 把 UI 重新渲染一遍呈現更新過後的畫面。 而 `state` 改變之後自動 call `render` 的運作方式實際上和 `shouldComponentUpdate` 這個 method 有關係,它的預設值是 `true`: ```javascript= shouldComponentUpdate(nextProps, nextState) { return true } ``` 如果把它改為 `return false` 的話,那麼 `state` 改變就不會 call `render` 了,也就是不會更新 UI 的畫面,不過 `state` 的值依然有確實地被改變的。 也可以在 `shouldComponentUpdate` method 當中寫入判別式: ```javascript= shouldComponentUpdate(nextProps, nextState) { if (nextState.number === 3) { return false } return true } ``` 這樣的意思就是當 `state.number` 的值變為 3 的時候就不會 call `render`,其它 `state.number` 數值則照常 call `render`。 ### 實際的例子 ```jsx= class Title extends Component { render() { console.log('Title render') return ( <h1>{this.props.title}</h1> ) } } class App extends Component { constructor(props) { super(props) this.state = { number: 1 } } render() { const { number } = this.state return ( <div> <h1>{number}</h1> <Title title={'hello world'} /> <div onClick={() => { this.setState({ number: this.state.number + 1 }) }}>Click Me</div> </div> ) } } ``` 上面的例子畫面長這樣: ![](https://i.imgur.com/T35s3SL.png) 當每次按 `Click Me` 的時候, `1` 的數字會跟著 `state.number` 改變而加 1,而 `Title` 這個 Component 也會跟著重新 `render()` ,儘管 `Title` 的內容完全沒變化(一直都是 `hello world`): ![](https://i.imgur.com/OroC8CB.png) 可以看到在第 3 行寫的 `console.log('Title render')` 一直被 log,也就是每按一下 `Click Me` 就被 log 一次,也就是說 `Title` Component 一直觸發 `render`。 但是我們不想要這樣,我們希望內容沒改變的 Component 就不要再 `render` 一遍,這時就可以派出我們的得力助手:`shouldComponentUpdate`。 我們可以在 `Title` Component 加上這段 `shouldComponentUpadte`: ```jsx= class Title extends Component { shouldComponentUpdate(nextProps) { if (nextProps.title !== this.props.title) { return true } return false } render() { console.log('Title render') return ( <h1>{this.props.title}</h1> ) } } ``` 意思就是當今天下一個收到的 `props` 的 `title` 的值和目前的 `props` 的 `title` 值不一樣的時候,才需要做 `render`(`true`),否則平常時候就不做 `render`(`false`)。 而使用 `shouldComponentUpdate` 的目的當然是為了減少不必要的 `render` 進而拖慢效能,但這樣子做是否就都可以達到效能優化了?答案是未必,得端看 `shouldComponentUpdate` 的整體執行速度,主要是程式碼第 3 行 `if (nextProps.title !== this.props.title)` 這其中的判別式處理的速度和 `render` 一次的速度究竟哪個比較快,所以不是說使用越多 `shouldComponentUpdate` 來減少 `render` ,效能就比較好,除非今天要 `render` 的內容實在多到一個不行那就顯而易見了。 另外 `shouldComponentUpdate` 的設定也會影響到下面有講到的 `componentDidUpdate`,如果是 `shouldComponentUpdate` 是設定 `false`,那麼 `componentDidUpdate` 也不會有作用。 ## componentDidMount 與 componentWillUnmount 以這個為例子: ```jsx= class Title extends Component { render() { return ( <h1>hello </h1> ) } } class App extends Component { constructor(props) { super(props) this.state = { showTitle: true } } render() { const { showTitle } = this.state return ( <div> <div onClick={() => { this.setState({ showTitle: !this.state.showTitle }) }}>toggle</div> {showTitle && <Title />} </div> ) } } ``` UI 的畫面: ![](https://i.imgur.com/imic3xG.png) 當點擊 `toggle` 的時候,`hello` 有關的 DOM 就會消失或者是出現。 接著正式進入這一個主題。在 Component call `render` 之後的某一段時間必須經過一個動作叫「Mount」,所有 `render` 的內容才會被真正放到 DOM 上面,而與 `componentDidMount` 這個 method 有關,經過這個 method 當中的所有元素與動作其實都是已經被放到 DOM 上面了,換句話說,在 `componentDidMount` 裡面,你必定可以選到任何 `render` 後的內容。我們把上面的例子的三處地方新增內容: ```jsx= class Title extends Component { render() { return ( <h1>hello </h1> ) } } class App extends Component { constructor(props) { super(props) this.state = { showTitle: true } // update here console.log('created', document.querySelector('.test')) } // update here componentDidMount() { console.log('Mount', document.querySelector('.test')) } render() { const { showTitle } = this.state return ( < div > // update here <div className='test' onClick={() => { this.setState({ showTitle: !this.state.showTitle }) }}>toggle</div> {showTitle && <Title />} </div > ) } } ``` 我們在 `toggle` 的 DOM 上新增 `class='test'`,然後分別在 Component 被建立時,和在 `componentDidMount` 裡面,選取 `class='test'` 這個 DOM 節點,結果發現到只有在 `componentDidMount` 才選取得到這個節點: ![](https://i.imgur.com/KhTJw1z.png) 也就是說,在 Component 建立的過程,DOM 節點其實還未被放到畫面上;另外一方面,我們在 `componentDidMount` 那邊可以確定百分之百拿得到 `render` 後的 DOM 節點。 **而 `componentDidMount` 比較大的作用是可以做一些初始化的設定**,比方說: ```jsx= class Title extends Component { constructor(props) { super(props) this.state = { title: 'hello' } } componentDidMount() { setTimeout(() => { this.setState({ title: 'yoyoyo!' }) }, 2000) } render() { return ( <h1>{this.state.title} </h1> ) } } class App extends Component { constructor(props) { super(props) this.state = { showTitle: true } } render() { const { showTitle } = this.state return ( < div > <div onClick={() => { this.setState({ showTitle: !this.state.showTitle }) }}>toggle</div> {showTitle && <Title />} </div > ) } } ``` 我們在 `Title` Component 裡面設定一個 `componentDidMount`,這個 `componentDidMount` 設定了一個 `setTimeout()`,過了 2 秒之後,`state` 的 `title` 值就會由 `hello` 被改變成 `yoyoyo!`。 不過,雖然上面使用 `componentDidMount` 達到初始化的設定了,但卻衍伸出另外一個問題,當我們按了 `toggle` 後,`Title` Component 就會從 DOM 節點上消失,兩秒後就會啟動 `setState` 的功能,但在 DOM 上面卻找不到 `Title` Component 的節點因此這個 `state` 的更新就會顯示這樣的錯誤: ![](https://i.imgur.com/qpZs3U4.png) 而這時我們需要使用另外一個 method 避免這個錯誤發生,也就是 `componentWillUnmount`, `componentWillUnmount` 如名稱所述,它是一個在 component 要被「unmount」以前,會早一步觸發它裡面所寫的功能的 method,因此,我們要解決上面的錯誤的話,可以這麼寫: ```jsx= // 這邊只貼上有修改與新增的段落 componentDidMount() { this.timer = setTimeout(() => { this.setState({ title: 'yoyoyo!' }) }, 2000) } componentWillUnmount() { clearTimeout(this.timer) } ``` 為 `componentDidMount` 加上 `this.timer` 去指定 `setTimeout`,接著新增 `componentWillUnmount`,然後在裡面新增 `clearTimeout(this.timer)`,也就是清除 `this.timer` 這個計時器,因此現在只要 `Title` Component 要被 unmount 以前,就會去觸發`componentWillUnmount` 讓它清除計時器。 也因此 `componentDidMount` 和 `componentWillUnmount` 通常是成雙成對的,兩個通常會同時設定,不然可能會發生不必要的錯誤。 ## componentDidUpdate `componentDidUpdate` 是用在當 Component 的 `state` 更新的時候會被 call 的 method。 以下面這個例子為例: ```jsx= class App extends Component { constructor(props) { super(props) this.state = { apple: 1, orange: 1, banana: 1, } this.addApple = this.addApple.bind(this) this.addOrange = this.addOrange.bind(this) this.addBanana = this.addBanana.bind(this) } addApple() { this.setState({ apple: this.state.apple + 1 }) } addOrange() { this.setState({ orange: this.state.orange + 1 }) } addBanana() { this.setState({ banana: this.state.banana + 1 }) } render() { const { apple, orange, banana } = this.state return ( <div> <div> apple: {apple} <button onClick={this.addApple}>+1</button> </div> <div> orange: {orange} <button onClick={this.addOrange}>+1</button> </div> <div> banana: {banana} <button onClick={this.addBanana}>+1</button> </div> </div> ) } } ``` 這個例子的 UI 介面長這樣: ![](https://i.imgur.com/X7QvHnh.png) 這個範例是,當按了各別水果的 `+1` button 後,各別水果的數量就會增加 1 個。 如果今天我們想要 log 出每個水果在更新數量之後,顯示當前的水果數量的話,可以這麼寫: ```jsx= addApple() { this.setState({ apple: this.state.apple + 1 }) console.log(this.state.apple, this.state.orange, this.state.banana) } addOrange() { this.setState({ orange: this.state.orange + 1 }) console.log(this.state.apple, this.state.orange, this.state.banana) } addBanana() { this.setState({ banana: this.state.banana + 1 }) console.log(this.state.apple, this.state.orange, this.state.banana) } ``` 替三種水果變更 `state` 的 method 當中放入 `console.log()`,可是這樣很沒效率,我們可以改這麼寫: ```jsx= log() { console.log(this.state.apple, this.state.orange, this.state.banana) } addApple() { this.setState({ apple: this.state.apple + 1 }) this.log() } addOrange() { this.setState({ orange: this.state.orange + 1 }) this.log() } addBanana() { this.setState({ banana: this.state.banana + 1 }) this.log() } ``` 新增一個 `log` 的 method,裡面放 `console.log()`,這樣的話,在各個變更 `state` 的 method 只要都呼叫 `this.log` 就比較簡潔並且好維護了,可是會發現到一個一開始就存在的問題: ![](https://i.imgur.com/wdk0qdr.png) 當按了按鈕新增蘋果的數量之後,`console.log()` 出來的數量卻沒有新增,這就意味著,當 `setState` 執行完之後,並不會馬上執行 `console.log()`,也就是說,這兩行指令是非同步的。如果要解決這個問題,其實在 `setState` 函式中還有第二個參數,就是 callback function,也就是說當執行完 `setState` 才會 call 第二個參數的 function 執行,因此我們只要把 `this.log` 放進去 `setState` 就可以了,像這樣: ```jsx= log() { console.log(this.state.apple, this.state.orange, this.state.banana) } addApple() { this.setState({ apple: this.state.apple + 1 }, () => { this.log() }) } addOrange() { this.setState({ orange: this.state.orange + 1 }, () => { this.log() }) } addBanana() { this.setState({ banana: this.state.banana + 1 }, () => { this.log() }) } ``` 或者直接放 `this.log` function 在第二個參數即可。 上面的作法是當對 `state` 做操作的時候我們就去 log 結果出來,不過我們可以換另一個角度來看,如果今天我們的判定方式改為當 Component 的 `state` 有被改變(Update)時就去 log 的話,寫起來會比較好維護,而這就要用到 `componentDidUpdate` 來實作了,所以我們可以這麼改: ```jsx= componentDidUpdate(prevProps, prevState) { if (prevState.apple !== this.state.apple || prevState.orange !== this.state.orange || prevState.banana !== this.state.banana ) { this.log() } } log() { console.log(this.state.apple, this.state.orange, this.state.banana) } addApple() { this.setState({ apple: this.state.apple + 1 }) } addOrange() { this.setState({ orange: this.state.orange + 1 }) } addBanana() { this.setState({ banana: this.state.banana + 1 }) } ``` 這樣的好處是,所有關於 `log` 的語法都會被集中在 `componentDidUpate`,也因此不會說發生忘了加上其中一個 `log` 在某一個水果上的問題,同時維護性變得更好,我們只需要單獨更動 `componentDidUpate` 裡面的內容即可。 ## 其它 lifecycle 在這篇文章當中,所有提到的新名詞都是 React 的 lifecycle 相關語法,不過除了這些 lifecycle,還有兩個比較少在用的 lifecycle 沒被提到,可以到 React 官網的這邊來看:http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ 而且上面網址的 diagram 集結了所有 lifecycle 會在 UI 呈現的流程的哪邊運作,還滿清楚的。

    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