本文做者:胡子大哈
本文原文:huziketang.com/books/react…javascript
转载请注明出处,保留原文连接以及做者信息前端
在线阅读:huziketang.com/books/react…java
看看上一节前端组件化(一):从一个简单的例子讲起咱们的代码,仔细留意一下 changeLikeText
函数,这个函数包含了 DOM 操做,如今看起来比较简单,那是由于如今只有 isLiked
一个状态。因为数据状态改变会致使须要咱们去更新页面的内容,因此假想一下,若是你的组件依赖了不少状态,那么你的组件基本所有都是 DOM 操做。react
一个组件的显示形态由多个状态决定的状况很是常见。代码中混杂着对 DOM 的操做实际上是一种很差的实践,手动管理数据和 DOM 之间的关系会致使代码可维护性变差、容易出错。因此咱们的例子这里还有优化的空间:如何尽可能减小这种手动 DOM 操做?浏览器
这里要提出的一种解决方案:一旦状态发生改变,就从新调用 render
方法,构建一个新的 DOM 元素。这样作的好处是什么呢?好处就是你能够在 render
方法里面使用最新的 this.state
来构造不一样 HTML 结构的字符串,而且经过这个字符串构造不一样的 DOM 元素。页面就更新了!听起来有点绕,看看代码怎么写,修改原来的代码为:app
class LikeButton {
constructor () {
this.state = { isLiked: false }
}
setState (state) {
this.state = state
this.el = this.render()
}
changeLikeText () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
this.el = createDOMFromString(` <button class='like-btn'> <span class='like-text'>${this.state.isLiked ? '取消' : '点赞'}</span> <span>👍</span> </button> `)
this.el.addEventListener('click', this.changeLikeText.bind(this), false)
return this.el
}
}复制代码
其实只是改了几个小地方:less
render
函数里面的 HTML 字符串会根据 this.state
不一样而不一样(这里是用了 ES6 的模版字符串,作这种事情很方便)。setState
函数,这个函数接受一个对象做为参数;它会设置实例的 state
,而后从新调用一下 render
方法。changeLikeText
会构建新的 state
对象,这个新的 state
,传入 setState
函数当中。这样的结果就是,用户每次点击,changeLikeText
都会调用改变组件状态而后调用 setState
;setState
会调用 render
,render
方法会根据 state
的不一样从新构建不一样的 DOM 元素。函数
也就是说,你只要调用 setState
,组件就会从新渲染。咱们顺利地消除了手动的 DOM 操做。组件化
上面的改进不会有什么效果,由于你仔细看一下就会发现,其实从新渲染的 DOM 元素并无插入到页面当中。因此在这个组件外面,你须要知道这个组件发生了改变,而且把新的 DOM 元素更新到页面当中。性能
从新修改一下 setState
方法:
...
setState (state) {
const oldEl = this.el
this.state = state
this.el = this.render()
if (this.onStateChange) this.onStateChange(oldEl, this.el)
}
...复制代码
使用这个组件的时候:
const likeButton = new LikeButton()
wrapper.appendChild(likeButton.render()) // 第一次插入 DOM 元素
component.onStateChange = (oldEl, newEl) => {
wrapper.insertBefore(newEl, oldEl) // 插入新的元素
wrapper.removeChild(oldEl) // 删除旧的元素
}复制代码
这里每次 setState
都会调用 onStateChange
方法,而这个方法是实例化之后时候被设置的,因此你能够自定义 onStateChange
的行为。这里作的事是,每当 setState
中构造完新的 DOM 元素之后,就会经过 onStateChange
告知外部插入新的 DOM 元素,而后删除旧的元素,页面就更新了。这里已经作到了进一步的优化了:如今不须要再手动更新页面了。
非通常的暴力,由于每次 setState
都从新构造、新增、删除 DOM 元素,会致使浏览器进行大量的重排,严重影响性能。不过没有关系,这种暴力行为能够被一种叫 Virtual-DOM 的策略规避掉,但这不是本文所讨论的范围。
这个版本的点赞功能很不错,我能够继续往上面加功能,并且还不须要手动操做DOM。可是有一个很差的地方,若是我要从新另外作一个新组件,譬如说评论组件,那么里面的这些 setState
方法要从新写一遍,其实这些东西均可以抽出来,变成一个通用的模式。
下一节《React.js 小书 Lesson4 - 前端组件化(三):抽象出公共组件类》咱们把这个通用模式抽离到一个类当中。