React.js 小書 Lesson2 - 前端組件化(一):從一個簡單的例子講起

不少課程一上來就給你們如何配置環境、怎麼寫 React.js 組件。可是本課程仍是但願你們對問題的根源有一個更加深刻的瞭解,其實不少的庫、框架都是解決相似的問題。只有咱們對這些庫、框架解決的問題有深刻的瞭解和思考之後,咱們才能駕輕就熟地使用它們,而且有新的框架出來也不會太過迷茫;由於其實它們解決都是同一個問題。javascript

這兩節課咱們來探討一下是什麼樣的問題致使了咱們須要前端頁面進行組件化,前端頁面的組件化須要解決什麼樣的問題。後續課程咱們再來看看 React.js 是怎麼解決這些問題的。html

因此這幾節所講的內容將和 React.js 的內容沒有太大的關係,可是若是你能順利瞭解這幾節的內容,那麼後面哪些對新手來講很複雜的概念對你來講就是很是天然的事。前端

一個簡單的點贊功能

咱們會從一個簡單的點贊功能講起。 假設如今咱們須要實現一個點贊、取消點讚的功能。java

React.js 組件化圖片

若是你對前端稍微有一點了解,你就順手拈來:react

HTML:git

<body>
    <div class='wrapper'> <button class='like-btn'> <span class='like-text'>點贊</span> <span>👍</span> </button> </div> </body> 

爲了模擬現實當中的實際狀況,因此這裏特地把這個 button 裏面的 HTML 結構搞得稍微複雜一些。有了這個 HTML 結構,如今就給它加入一些 JavaScript 的行爲:github

JavaScript:app

const button = document.querySelector('.like-btn') const buttonText = button.querySelector('.like-text') let isLiked = false button.addEventListener('click', () => { isLiked = !isLiked if (isLiked) { buttonText.innerHTML = '取消' } else { buttonText.innerHTML = '點贊' } }, false) 

功能和實現都很簡單,按鈕已經能夠提供點贊和取消點讚的功能。這時候你的同事跑過來了,說他很喜歡你的按鈕,他也想用你寫的這個點贊功能。這時候問題就來了,你就會發現這種實現方式很致命:你的同事要把整個 button 和裏面的結構複製過去,還有整段 JavaScript 代碼也要複製過去。這樣的實現方式沒有任何可複用性。框架

結構複用

如今咱們來從新編寫這個點贊功能,讓它具有必定的可複用。此次咱們先寫一個類,這個類有 render 方法,這個方法裏面直接返回一個表示 HTML 結構的字符串:less

class LikeButton { render () { return ` <button id='like-btn'> <span class='like-text'>贊</span> <span>👍</span> </button> ` } } 

而後能夠用這個類來構建不一樣的點贊功能的實例,而後把它們插到頁面中。

const wrapper = document.querySelector('.wrapper') const likeButton1 = new LikeButton() wrapper.innerHTML = likeButton1.render() const likeButton2 = new LikeButton() wrapper.innerHTML += likeButton2.render() 

React.js 組件化圖片

這裏很是暴力地使用了 innerHTML ,把兩個按鈕粗魯地插入了 wrapper 當中。雖然你可能會對這種實現方式很是不滿意,但咱們仍是勉強了實現告終構的複用。咱們後面再來優化它。

實現簡單的組件化

你必定會發現,如今的按鈕是死的,你點擊它它根本不會有什麼反應。由於根本沒有往上面添加事件。可是問題來了,LikeButton 類裏面是雖說有一個 button,可是這玩意根本就是在字符串裏面的。你怎麼能往一個字符串裏面添加事件呢?DOM 事件的 API 只有 DOM 結構才能用。

咱們須要 DOM 結構,準確地來講:咱們須要這個點贊功能的 HTML 字符串表示的 DOM 結構。假設咱們如今有一個函數 createDOMFromString ,你往這個函數傳入 HTML 字符串,可是它會把相應的 DOM 元素返回給你。這個問題就能夠解決了。

// ::String => ::Document const createDOMFromString = (domString) => { const div = document.createElement('div') div.innerHTML = domString return div } 

先不用管這個函數應該怎麼實現,先知道它是幹嗎的。拿來用就好,這時候用它來改寫一下 LikeButton 類:

class LikeButton { render () { this.el = createDOMFromString(` <button class='like-button'> <span class='like-text'>點贊</span> <span>👍</span> </button> `) this.el.addEventListener('click', () => console.log('click'), false) return this.el } } 

如今 render() 返回的不是一個 html 字符串了,而是一個由這個 html 字符串所生成的 DOM。在返回 DOM 元素以前會先給這個 DOM 元素上添加事件再返回。

由於如今 render 返回的是 DOM 元素,因此不能用 innerHTML 暴力地插入 wrapper。而是要用 DOM API 插進去。

const wrapper = document.querySelector('.wrapper') const likeButton1 = new LikeButton() wrapper.appendChild(likeButton1.render()) const likeButton2 = new LikeButton() wrapper.appendChild(likeButton2.render()) 

如今你點擊這兩個按鈕,每一個按鈕都會在控制檯打印 click,說明事件綁定成功了。可是按鈕上的文本仍是沒有發生改變,只要稍微改動一下 LikeButton 的代碼就能夠完成完整的功能:

class LikeButton { constructor () { this.state = { isLiked: false } } changeLikeText () { const likeText = this.el.querySelector('.like-text') this.state.isLiked = !this.state.isLiked likeText.innerHTML = this.state.isLiked ? '取消' : '點贊' } render () { this.el = createDOMFromString(` <button class='like-button'> <span class='like-text'>點贊</span> <span>👍</span> </button> `) this.el.addEventListener('click', this.changeLikeText.bind(this), false) return this.el } } 

這裏的代碼稍微長了一些,可是仍是很好理解。只不過是在給 LikeButton 類添加了構造函數,這個構造函數會給每個 LikeButton 的實例添加一個對象 statestate裏面保存了每一個按鈕本身是否點讚的狀態。還改寫了原來的事件綁定函數:原來只打印 click,如今點擊的按鈕的時候會調用 changeLikeText 方法,這個方法會根據 this.state 的狀態改變點贊按鈕的文本。

如今這個組件的可複用性已經很不錯了,你的同事們只要實例化一下而後插入到 DOM 裏面去就行了。

下一節咱們繼續優化這個例子,讓它更加通用。


由於第三方評論工具備問題,對本章節有任何疑問的朋友能夠移步到 React.js 小書的論壇 發帖,我會回答你們的疑問。

相關文章
相關標籤/搜索