# 前端組件化(三):抽象出公共組件類
> 作者:[胡子大哈](http://huziketang.com/books/react) </br>
> 原文鏈接: [http://huziketang.com/books/react/lesson4](http://huziketang.com/books/react/lesson4)
**轉載請注明出處,保留原文鏈接以及作者信息**
為了讓代碼更靈活,可以寫更多的組件,我們把這種模式抽象出來,放到一個?`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)
~~~
這里我們稍微修改了一下原有的?`LikeButton`?的?`render`?方法,讓它可以根據傳入的參數?`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](https://github.com/huzidaha/reactjs-in-40)。
## 總結
我們用了很長的篇幅來講一個簡單的點贊的例子,并且在這個過程里面一直在優化編寫的方式。最后抽離出來了一個類,可以幫助我們更好的做組件化。在這個過程里面我們學到了什么?
組件化可以幫助我們解決前端結構的復用性問題,整個頁面可以由這樣的不同的組件組合、嵌套構成。
一個組件有自己的顯示形態(上面的 HTML 結構和內容)行為,組件的顯示形態和行為可以由數據狀態(state)和配置參數(props)共同決定。數據狀態和配置參數的改變都會影響到這個組件的顯示形態。
當數據變化的時候,組件的顯示需要更新。所以如果組件化的模式能提供一種高效的方式自動化地幫助我們更新頁面,那也就可以大大地降低我們代碼的復雜度,帶來更好的可維護性。
好了,課程結束了。你已經學會了怎么使用 React.js 了,因為我們已經寫了一個——當然我是在開玩笑,但是上面這個?`Component`?類其實和 React 的?`Component`?使用方式很類似。掌握了這幾節的課程,你基本就掌握了基礎的 React.js 的概念。
接下來我們開始正式進入主題,開始正式介紹 React.js。你會發現,有了前面的鋪墊,下面講的內容理解起來會簡單很多了。
- 前言
- 第一階段
- Lesson 1 - React.js 簡介
- Lesson 2 - 前端組件化(一):從一個簡單的例子講起
- Lesson 3 - 前端組件化(二):優化 DOM 操作
- Lesson 4 - 前端組件化(三):抽象出公共組件類
- Lesson 5 - React.js 基本環境安裝
- Lesson 6 - 使用 JSX 描述 UI 信息
- Lesson 7 - 組件的 render 方法
- Lesson 8 - 組件的組合、嵌套和組件樹
- Lesson 9 - 事件監聽
- Lesson 10 - 組件的 state 和 setState
- Lesson 11 - 配置組件的 props
- Lesson 12 - state vs props
- Lesson 13 - 渲染列表數據
- Lesson 14 - 實戰分析:評論功能(一)
- Lesson 15 - 實戰分析:評論功能(二)
- Lesson 16 - 實戰分析:評論功能(三)
- 第二階段
- Lesson 17 - 前端應用狀態管理 —— 狀態提升
- Lesson 18 - 掛載階段的組件生命周期(一)
- Lesson 19 - 掛載階段的組件生命周期(二)
- Lesson 20 - 更新階段的組件生命周期
- Lesson 21 - ref 和 React.js 中的 DOM 操作
- Lesson 22 - props.children 和容器類組件
- Lesson 23 - dangerouslySetHTML 和 style 屬性
- Lesson 24 - PropTypes 和組件參數驗證
- Lesson 25 - 實戰分析:評論功能(四)
- Lesson 26 - 實戰分析:評論功能(五)
- Lesson 27 - 實戰分析:評論功能(六)