您的位置:首页 > Web前端

前端为什么要使用组件化的思想,通过一个实例来分析

2018-02-11 11:02 302 查看
在平时项目中,为什么我们都会采用组件化的思想去编写代码?

其实的原因很简单!!! 我们在写代码的时候,写完以后发现这个代码会出现在其他地方,想要复用,或者同事感兴趣,想使用这个代码。这个时候我们就需要通过组件化来实现代码的复用了,否则工作量真的是………..

接下来通过一个例子大概分析一下:



这个是一个点赞的功能~ 如果给我写,那 简单

HTML

<body>
<div class='wrapper'>
<button class='like-btn'>
<span class='like-text'>点赞</span>
<span>(假装有一张手的小图标)</span>
</button>
</div>
</body>


JavaScript

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 代码也要复制过去。这样的实现方式没有任何可复用性。

实现简单的组件化

这里有一个将字符串,变成DOM的函数

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


按组件处理以后

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
}
}


在别的地方使用组件

现在这个组件的可复用性已经很不错了,你的同事们只要实例化一下然后插入到 DOM 里面去就好了。

const wrapper = document.querySelector('.wrapper')

const likeButton1 = new LikeButton()
wrapper.appendChild(likeButton1.render())

const likeButton2 = new LikeButton()
wrapper.appendChild(likeButton2.render())


一个组件的显示多个状态

一个组件的显示形态由多个状态决定的情况非常常见。代码中混杂着对 DOM 的操作其实是一种不好的实践,手动管理数据和 DOM 之间的关系会导致代码可维护性变差、容易出错。

这里要提出的一种解决方案:

一旦状态发生改变,就重新调用 render 方法,构建一个新的 DOM 元素。这样做的好处是什么呢?好处就是你可以在 render 方法里面使用最新的 this.state 来构造不同 HTML 结构的字符串,并且通过这个字符串构造不同的 DOM 元素。页面就更新了!听起来有点绕,看看代码怎么写,修改原来的代码为:

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

setState (state) {
const oldEl = this.el
this.state = state
this.el = this.render()
if (this.onStateChange) this.onStateChange(oldEl, this.el)
}

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
}
}


其实只是改了几个小地方:

render 函数里面的 HTML 字符串会根据 this.state 不同而不同(这里是用了 ES6

的模版字符串,做这种事情很方便)。

新增一个 setState 函数,这个函数接受一个对象作为参数;它会设置实例的

当用户点击按钮的时候, changeLikeText 会构建新的 state

对象,这个新的 state ,传入 setState 函数当中。

这样的结果就是,用户每次点击,changeLikeText 都会调用改变组件状态然后调用 setState ;setState 会调用 render ,render 方法会根据 state 的不同重新构建不同的 DOM 元素。

也就是说,你只要调用 setState,组件就会重新渲染。我们顺利地消除了手动的 DOM 操作。

在别的地方使用组件

const likeButton = new LikeButton()
wrapper.appendChild(likeButton.render()) // 第一次插入 DOM 元素
likeButton.onStateChange = (oldEl, newEl) => {
wrapper.insertBefore(newEl, oldEl) // 插入新的元素
wrapper.removeChild(oldEl) // 删除旧的元素
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐