您的位置:首页 > Web前端 > React

React.js初探-如何将一个普通button改造成类React组件

2017-05-22 15:42 555 查看

React.js初探-如何将一个普通button改造成类React组件

React.js目的在于提高前端组件的复用性,而组件状态和表现通常是随着数据而变化,在React中体现为通过父组件传递props或自身定义state来获取数据,通过props与state中数据的变化经过计算来重新渲染组件。下面以一个常见的例子来解释一下组件能为我们带来的便利以及React的超简化版本。

我是一个前端程序员,有一天老板叫我实现一个功能,功能描述如下:一个按钮有两个状态,分别是点击改变颜色和点击还原颜色,原始颜色是红色,点击后颜色为绿色。

通常情况下,我很快接会写出了如下代码:

HTML结构:

<button style="color:red;">换肤</button>


JavaScript代码:

const oButton = document.querySelector('button')
var isChanged = false
oButton.addEventListener('click',function(){
isChanged = !isChanged
if (isChanged) {
this.innerHTML = '还原'
this.style.color = 'green'
}else{
this.innerHTML = '换肤'
this.style.color = 'red'
}
},false)


是的,我很快就完成了老板交代的任务。

但是过了几天,老板又找到了我,说:“我们现在需要个按钮,功能是这样的:按钮初始状态为绿色,文字为下载。点击之后变为红色,文字为取消“。我想来想去,都觉得和前几天做的那个按钮很像,于是打算将这个Button做成一个组件算了,免得他到时候又提出什么类似的要求。

接下来,我开始按钮改造之旅:

HTML结构:

<div id="root"></div>


JavaScript代码,将按钮抽取为一个Button类:

const createDOMFromString = (str) => {
var dom = document.createElement('div')
dom.innerHTML = str
return dom
}

class Button {
handleClick() {
consol
e2aa
e.log('clicked')
}

render() {
const el = createDOMFromString('<button style="color:red;">换肤</button>')
el.addEventListener('click',this.handleClick.bind(this),false)
this.el = el
return el
}
}

const root = document.querySelector('#root')
root.appendChild(new Button().render())


接下来点击按钮控制台会输出
clicked
,但是我的目的还没有达到,需求是按钮点击后改变颜色和文字的。那接下来的任务就是通过点击改变按钮的状态,再状态改变之后使按钮的变现也从随之产生变化:

//点击事件只能添加到DOM,此方法用于将字符串包装为真实DOM
const createDOMFromString = (str) => {
var dom = document.createElement('div')
dom.innerHTML = str
return dom
}

class Button {
constructor() {
this.state = {
isChanged: false
}
}

handleClick() {
this.state.isChanged = !this.state.isChanged
const oldEl = this.el
this.el = this.render()
this.onClick && this.onClick(this.el,oldEl)
}

render() {
const el = createDOMFromString(`<button style="color:${this.state.isChanged?'green':'red'};">${this.state.isChanged?'还原':'换肤'}</button>`)
el.addEventListener('click',this.handleClick.bind(this),false)
this.el = el
return el
}
}

const oButton = new Button()
const root = document.querySelector('#root2')
root.appendChild(oButton.render())
oButton.onClick = function(el,oldEl) {
root.insertBefore(el,oldEl)
root.removeChild(oldEl)
}


最后为了以后不重复写某些通用代码,我抽取出来一个Component类作为Button组件的组件,并为Button添加上了props,通过props来配置Button的颜色和文字。最终我的Button改造如下,很快就实现了下载和换肤两个按钮:

const createDOMFromString = (str) => {
var dom = document.createElement('div')
dom.innerHTML = str
return dom
}

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

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

setState(state) {
this.state = state
const oldEl = this.el
this.el = this.renderDOM()
this.onClick && this.onClick(this.el,oldEl)
}

renderDOM() {
const el = createDOMFromString(this.render())
el.addEventListener('click',this.handleClick.bind(this),false)
this.el = el
return el
}
}

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

handleClick() {
this.setState({isChanged: !this.state.isChanged})
}

render() {
return `<button style="color:${this.state.isChanged?this.props.color.actived:this.props.color.default};">${this.state.isChanged?this.props.text.actived:this.props.text.default}</button>`
}
}

const oButton = new Button({color:{default:'red',actived: 'green'},text: {default: '换肤',actived:'还原'}})
mount(oButton,document.querySelector('#root3'))
const oButtonDownload = new Button({color:{default:'green',actived: 'red'},text: {default: '下载',actived:'取消'}})
mount(oButtonDownload,document.querySelector('#root4'))


通过上面一步步的分析重构,最终构造出了一个可复用的Button,接触过React的同学应该会感觉到有点熟悉。这里我们的例子虽说原理与React相似,但是与React得差距是特别大的如没有React的虚拟DOM等等,所以革命尚未成功,同志任需努力呀~。

好吧,接下来整个例子的完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button style="color: red;">换肤</button>
<div id="root"></div>
<div id="root2"></div>
<div id="root3"></div>
<div id="root4"></div>
<script>
// level 1 初始
(function(){
const oButton = document.querySelector('button') var isChanged = false oButton.addEventListener('click',function(){ isChanged = !isChanged if (isChanged) { this.innerHTML = '还原' this.style.color = 'green' }else{ this.innerHTML = '换肤' this.style.color = 'red' } },false)
})();

// level 2 组件
(function(){
const createDOMFromString = (str) => {
var dom = document.createElement('div')
dom.innerHTML = str
return dom
}

class Button {

handleClick() {
console.log('clicked')
}

render() {
const el = createDOMFromString('<button style="color:red;">换肤</button>')
el.addEventListener('click',this.handleClick.bind(this),false)
this.el = el
return el
}
}

const root = document.querySelector('#root')
root.appendChild(new Button().render())
})();

// level 3 按钮表现随状态发生改变
(function(){
const createDOMFromString = (str) => {
var dom = document.createElement('div')
dom.innerHTML = str
return dom
}

class Button {
constructor() {
this.state = {
isChanged: false
}
}

handleClick() {
this.state.isChanged = !this.state.isChanged
const oldEl = this.el
this.el = this.render()
this.onClick && this.onClick(this.el,oldEl)
}

render() {
const el = createDOMFromString(`<button style="color:${this.state.isChanged?'green':'red'};">${this.state.isChanged?'还原':'换肤'}</button>`)
el.addEventListener('click',this.handleClick.bind(this),false)
this.el = el
return el
}
}

const oButton = new Button()
const root = document.querySelector('#root2')
root.appendChild(oButton.render())
oButton.onClick = function(el,oldEl) {
root.insertBefore(el,oldEl)
root.removeChild(oldEl)
}
})();

// level 4 抽取公共方法
(function(){
const createDOMFromString = (str) => { var dom = document.createElement('div') dom.innerHTML = str return dom } const mount = (component,wrapper) => { wrapper.appendChild(component.renderDOM()) component.onClick = (el,oldEl) => { wrapper.insertBefore(el,oldEl) wrapper.removeChild(oldEl) } } class Component { constructor(props) { this.props = props } setState(state) { this.state = state const oldEl = this.el this.el = this.renderDOM() this.onClick && this.onClick(this.el,oldEl) } renderDOM() { const el = createDOMFromString(this.render()) el.addEventListener('click',this.handleClick.bind(this),false) this.el = el return el } } class Button extends Component{ constructor(props) { super(props) this.state = { isChanged: false } } handleClick() { this.setState({isChanged: !this.state.isChanged}) } render() { return `<button style="color:${this.state.isChanged?this.props.color.actived:this.props.color.default};">${this.state.isChanged?this.props.text.actived:this.props.text.default}</button>` } } const oButton = new Button({color:{default:'red',actived: 'green'},text: {default: '换肤',actived:'还原'}}) mount(oButton,document.querySelector('#root3')) const oButtonDownload = new Button({color:{default:'green',actived: 'red'},text: {default: '下载',actived:'取消'}}) mount(oButtonDownload,document.querySelector('#root4'))
})();
</script>
</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: