我相信大家踏入一门编程语言的第一步就是“哈喽,我的”。恩,学会这一句,世界就会是我们的了。为了降低学习成本,首先避开了“繁琐”的服务器环境搭建,直接依赖浏览器环境,在客户端先熟悉一下React语法和一些基础知识。
虽然不用配置服务器环境,但是要引入相关JS:react.js, react-dom.js, babel.min.js (不能太懒呀!)
我这边打算直接通过例子来总结知识点,下面是一个最简单的 "hello world" 实现程序。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-hello-world</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type='text/babel'>
ReactDOM.render(<h1>hello world</h1>, document.getElementById('app'));
</script>
</body>
</html>
总结:
1.除了引入react相关的js文件外,还额外引入了babel这个插件,主要是为了对我们的js代码进行编译,把es6语法转换为es5(当然上面这个例子中我们还没用到ES6语法,下面例子会展示),我们需要将script中的type改成text/babel,这样babel.js才能正确转化。我们还发现了js和html混合使用的方法,这里的方法和我们以前不一样,没加任何引号,这种方法叫做jsx,是react发明的一种写法,也是需要babel去转换的。
what!什么是jsx写法?http://www.cnblogs.com/zourong/p/6043914.html
ps: jsx语法中注释代码的话这样: { /* 注释代码 */}
es6就不用我多说了吧,前面也有相关学习笔记:http://blog.csdn.net/liuzijiang1123/article/details/56835823
(主要掌握:import,export,class,const,let,arrow function)
目前react的代码书写规范是:jsx+es6;
ES5和ES6对于React的开发书写是有一定差异的,大家可以去网上多找一下类似的文章:http://www.cnblogs.com/yongjz/p/5356914.html
2.ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。
在这里我们自己创建了一个html节点<h1>hello world</h1>这个节点,不过是个虚拟的dom节点,并没有真正在我们页面上出现,我们出现将其与页面中真正存在的dom节点相关联,就是"挂载"在真正的dom节点旗下。页面加载成功后,你可以打开F12看,这个<h1>节点“挂载”在我们的"app"节点下面。
下面再举一个hello world的例子,变个花样~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-hello-world</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type='text/babel'>
class HelloMessage extends React.Component { //notice use of React.Component
constructor() {
super();
}
render() {
return (
<div>
<h1>{this.props.says}</h1>
</div>
);
}
};
ReactDOM.render(<HelloMessage/>, document.getElementById('app'));
</script>
</body>
</html>
上面这个hello world例子就比第一个的“复杂”一些。
总结:
1.React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。这里用到了es6中的新语法class,我们创建了一个HelloMessage的类,继承React.Component类;定义了一个render方法,用来返回我们的html模块。
注意:
A.添加组件属性,有一个地方需要注意,就是 class
属性需要写成 className
,for
属性需要写成 htmlFor
,这是因为 class
和 for
是 JavaScript 的保留字。
B.组件类的第一个字母必须大写,否则会报错,比如HelloMessage
不能写成helloMessage
。另外,组件类只能包含一个顶层标签,否则也会报错,这个就是为什么我们在render中返回的html标签中总会有一个大的标签把要返回的内容包裹起来(我这里是用div),因为ReactDOM.render第一个参数只能接受一个虚拟的DOM节点。
下面是参考阮老师的博客进行的一些拓展:
拓展1:
组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如<HelloMessage says="Hello world">
,就是 HelloMessage
组件加入一个 says属性,值为 Hello world.组件的属性可以在组件类的this.props
对象上获取,比如 says
属性就可以通过 this.props.says
读取,这样我们又多了一种实现hello world的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-hello-world</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type='text/babel'>
class HelloMessage extends React.Component { //notice use of React.Component
constructor() {
super();
}
render() {
return (
<div>
<h1>{this.props.says}</h1>
</div>
);
}
};
ReactDOM.render(<HelloMessage says="Hello world"/>, document.getElementById('app'));
</script>
</body>
</html>
由于class有一个本身也会有一个属性,我们还可以把这个hello world定义到它的属性里面:
constructor() {
super();
this.says="hello world";
}
<h1>{this.says}</h1>
这样修改也能通过属性来实现hello world。
拓展2:
this.props
对象的属性与组件的属性一一对应,但是有一个例外,就是this.props.children
属性。它表示组件的所有子节点。
下面这个例子也可以输出"hello world",通过寻找组件下面的所有子节点来显示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-hello-world</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type='text/babel'>
class HelloMessage1 extends React.Component{
constructor() {
super();
}
render() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>
);
}
};
ReactDOM.render(
<HelloMessage1>
<span>hello</span>
<span>world</span>
</HelloMessage1>,
document.getElementById('app')
);
</script>
</body>
</html>
拓展3:获取真实的DOM节点
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做DOM diff ,它可以极大提高网页的性能表现。但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref
属性。
下面这个代码中的功能是,当我去点击按钮时,我们的text就会获取到焦点;这个时候就需要获取到我们真实的DOM节点( text文本框 );为了做到这一点,文本输入框必须有一个ref
属性,然后this.refs.[refName]
就会返回这个真实的 DOM 节点。由于this.refs.[refName]
属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。通过为组件指定Click
事件的回调函数,确保了只有等到真实 DOM 发生Click
事件之后,才会读取this.refs.[refName]
属性。
当然这里还需要注意的是:
1.我们这里给按钮添加onClick触发事件,这里必须用驼峰式!!不能用onclick,因为html里面是不区分大小写的,但是js里面区分,我们这里需要写成onClick。
2.我们这里用的是es6的写法,onClick里面的this没有绑定,我们需要重新绑定一下我们的this,否则会报错。可以参考:https://segmentfault.com/q/1010000008155467
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-hello-world</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type='text/babel'>
class HelloMessage extends React.Component { //notice use of React.Component
constructor() {
super();
}
handleClick(){
this.refs.mytext.focus();
}
render() {
return (
<div>
<input type="text" ref="mytext"/>
<input type="button" value="fous" onClick={()=>this.handleClick()} />
</div>
);
}
};
ReactDOM.render(<HelloMessage/>, document.getElementById('app'));
</script>
</body>
</html>
拓展4:
组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI,这种状态由this.state来表示,也可以代表一种组件的属性;由于this.props 和 this.state 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而this.state 是会随着用户互动而产生变化的特性。(this.props 钟情,this.state花心)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-hello-world</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type='text/babel'>
class HelloMessage extends React.Component { //notice use of React.Component
constructor() {
super();
this.state={
liked:false
}
}
handleClick(){
this.setState({liked: !this.state.liked});
}
render() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={()=>this.handleClick()}>
You {text} this Click to toggle.
</p>
);
}
};
ReactDOM.render(<HelloMessage/>, document.getElementById('app'));
</script>
</body>
</html>
上面代码是一个LinkButton组件,在ES6中我们直接在构造函数的this.state中定义初始状态,也就是一个对象,可以通过其获取属性。当用户点击组件,导致状态变化,this.setState方法就修改状态值,每次修改以后,自动调用this.render 方法,再次渲染组件。
拓展5:
组件的生命周期成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数(除了移除DOM),will函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
- componentWillMount()
- componentDidMount()
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
此外,React 还提供两种特殊状态的处理函数。
- componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
- shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
当一个组件被调用的时候先执行getDefaultProps,getInitialState获取他的默认属性和状态(es6中换了其他的写法,写在了constructor这个函数中),然后执行componentWillMount(即将渲染),接下来渲染到dom树上(触发render函数),渲染完成触发componentDidMount函数;
这时候,该组件就进入了一个running状态,并监视他的props和state以及被移除事件:
当props发生变化时执行componentWillReceiveProps然后去判断是否需要重新渲染(shouldComponentUpdate),如果不需要则继续保持running状态;如果需要则如初始时一样,执行componentWillMount(即将渲染),接下来渲染到dom树上,渲染完成触发componentDidMount函数,保持running状态继续监视;
当state发生变化时,则直接判断是否需要重新渲染(shouldComponentUpdate),然后根据是否需要决定执行渲染过程还是继续保持running状态;
当该组件被移除(unmount)时,将直接执行componentWillUnmount,该组件从dom树上消失;