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>
相关文章推荐
- 如何创建一个Android原生的react-native组件(一)
- js 中如何把一个普通的字符串转化为数组对象呢
- 如何创建一个android的react-native组件(二)
- 如何创建一个依赖Android AAR文件的React Native组件
- 如何在react组件记载外链js文件
- 【JAVASCRIPT】React学习-如何构建一个组件
- 【ReactJS】通过一个例子学习React组件的生命周期
- 关于在reactjs项目中如何用webpack配置组件按需加载
- Tkinter Button按钮组件如何调用一个可以传入参数的函数
- 初学ReactJS,写了一个RadioButtonList组件
- 利用React写一个评论区组件(React初探)
- 如何创建一个依赖Android AAR文件的React Native组件
- 如何在没有action触发的情况下实例化一个组件
- 在使用JS调用Window.close()方法关闭一个窗口是,如何禁止弹出对话窗口?
- 如何使一个普通的按钮有效或无效
- 如何在JS里取得两个数相除的整数和余数,那么如何判断一个数是整数呢?
- 有关打印、收藏等的JS代码(打印等主要使用了一个IE组件来实现)
- 如何使用一个不错的图表组件WebChart(免费) 用c#编写
- JS如何获取自定义组件的ID
- JS如何遍历一个文件夹下的所有文件与目录