React.js 小書 Lesson4 – 前端元件化(三):抽象出公共元件類

鬍子大哈發表於2017-11-10

React.js 小書 Lesson4 – 前端元件化(三):抽象出公共元件類

本文作者:鬍子大哈
本文原文:http://huziketang.com/books/react/lesson4

轉載請註明出處,保留原文連結以及作者資訊

線上閱讀:http://huziketang.com/books/react


為了讓程式碼更靈活,可以寫更多的元件,我們把這種模式抽象出來,放到一個 Component 類當中:

  class Component {
    setState (state) {
      const oldEl = this.el
      this.state = state
      this.el = this._renderDOM()
      if (this.onStateChange) this.onStateChange(oldEl, this.el)
    }

    _renderDOM () {
      this.el = createDOMFromString(this.render())
      if (this.onClick) {
        this.el.addEventListener(`click`, this.onClick.bind(this), false)
      }
      return this.el
    }
  }

這個是一個元件父類 Component,所有的元件都可以繼承這個父類來構建。它定義的兩個方法,一個是我們已經很熟悉的 setState;一個是私有方法 _renderDOM_renderDOM 方法會呼叫 this.render 來構建 DOM 元素並且監聽 onClick 事件。所以,元件子類繼承的時候只需要實現一個返回 HTML 字串的 render 方法就可以了。

還有一個額外的 mount 的方法,其實就是把元件的 DOM 元素插入頁面,並且在 setState 的時候更新頁面:

  const mount = (component, wrapper) => {
    wrapper.appendChild(component.renderDOM())
    component.onStateChange = (oldEl, newEl) => {
      wrapper.insertBefore(newEl, oldEl)
      wrapper.removeChild(oldEl)
    }
  }

這樣的話我們重新寫點贊元件就會變成:

  class LikeButton extends Component {
    constructor () {
      this.state = { isLiked: false }
    }

    onClick () {
      this.setState({
        isLiked: !this.state.isLiked
      })
    }

    render () {
      return `
        <button class=`like-btn`>
          <span class=`like-text`>${this.state.isLiked ? `取消` : `點贊`}</span>
          <span>?</span>
        </button>
      `
    }
  }

  mount(new LikeButton(), wrapper)

這樣還不夠好。在實際開發當中,你可能需要給元件傳入一些自定義的配置資料。例如說想配置一下點贊按鈕的背景顏色,如果我給它傳入一個引數,告訴它怎麼設定自己的顏色。那麼這個按鈕的定製性就更強了。所以我們可以給元件類和它的子類都傳入一個引數 props,作為元件的配置引數。修改 Component 的建構函式為:

...
    constructor (props = {}) {
      this.props = props
    }
...

繼承的時候通過 super(props)props 傳給父類,這樣就可以通過 this.props 獲取到配置引數:

  class LikeButton extends Component {
    constructor (props) {
      super(props)
      this.state = { isLiked: false }
    }

    onClick () {
      this.setState({
        isLiked: !this.state.isLiked
      })
    }

    render () {
      return `
        <button class=`like-btn` style="background-color: ${this.props.bgColor}">
          <span class=`like-text`>
            ${this.state.isLiked ? `取消` : `點贊`}
          </span>
          <span>?</span>
        </button>
      `
    }
  }

  mount(new LikeButton({ bgColor: `red` }), wrapper)

這裡我們稍微修改了一下原有的 LikeButtonrender 方法,讓它可以根據傳入的引數 this.props.bgColor 來生成不同的 style 屬性。這樣就可以自由配置元件的顏色了。

只要有了上面那個 Component 類和 mount 方法加起來不足40行程式碼就可以做到元件化。如果我們需要寫另外一個元件,只需要像上面那樣,簡單地繼承一下 Component 類就好了:

  class RedBlueButton extends Component {
    constructor (props) {
      super(props)
      this.state = {
        color: `red`
      }
    }

    onClick () {
      this.setState({
        color: `blue`
      })
    }

    render () {
      return `
        <div style=`color: ${this.state.color};`>${this.state.color}</div>
      `
    }
  }

簡單好用,現在可以靈活地元件化頁面了。

噢,忘了,還有一個神祕的 createDOMFromString,其實它更簡單:

  const createDOMFromString = (domString) => {
    const div = document.createElement(`div`)
    div.innerHTML = domString
    return div
  }

Component 完整的程式碼可以在這裡找到 reactjs-in-40

總結

我們用了很長的篇幅來講一個簡單的點讚的例子,並且在這個過程裡面一直在優化編寫的方式。最後抽離出來了一個類,可以幫助我們更好的做元件化。在這個過程裡面我們學到了什麼?

元件化可以幫助我們解決前端結構的複用性問題,整個頁面可以由這樣的不同的元件組合、巢狀構成。

一個元件有自己的顯示形態(上面的 HTML 結構和內容)行為,元件的顯示形態和行為可以由資料狀態(state)和配置引數(props)共同決定。資料狀態和配置引數的改變都會影響到這個元件的顯示形態。

當資料變化的時候,元件的顯示需要更新。所以如果元件化的模式能提供一種高效的方式自動化地幫助我們更新頁面,那也就可以大大地降低我們程式碼的複雜度,帶來更好的可維護性。

好了,課程結束了。你已經學會了怎麼使用 React.js 了,因為我們已經寫了一個——當然我是在開玩笑,但是上面這個 Component 類其實和 React 的 Component 使用方式很類似。掌握了這幾節的課程,你基本就掌握了基礎的 React.js 的概念。

接下來我們開始正式進入主題,開始正式介紹 React.js。你會發現,有了前面的鋪墊,下面講的內容理解起來會簡單很多了。

下一節連結直達:《React.js 小書 – Lesson5 React.js 基本環境安裝》

相關文章