1.ReactDOM.render()是react的最基本方法,用于将模板转化为HTML语言,并插入指定的DOM节点。
render(
hello world!
,
document.getElementById('root')
)
2.JSX语法
上面的代码中,HTML标签直接写在javascript语言中,不加任何引号,这就是JSX的语法,它允许HTML和javascript混写。
import React,{ Component } from "react";
import { render } from "react-dom";
var flowers = ["玫瑰花","郁金香","百合花"];
render(
{
flowers.map((item,index)=>{
return(
我喜欢{item}
)
})
}
div>,
document.getElementById("root")
);
上面的代码还有一种写法:
var flowers = ["玫瑰花","郁金香","百合花"];
render(
{
flowers.map((item,index)=>
我喜欢{item}
)
}
div>,
document.getElementById("root")
);
两者运行结果都如下:
但是不建议用后者方法,不容易理解。
上面代码体现了JSX语法格式,遇到<,用HTML规则解析,遇到{,用javascript规则解析。
JSX允许在模板插入javascript变量,如果这个变量是一个数组,会展开所有成员。
import React,{ Component } from "react";
import { render } from "react-dom";
var flowers = ["玫瑰花","郁金香","百合花"];
render(
我喜欢{flowers}
,
document.getElementById("root")
);
运行结果如下
我喜欢玫瑰花郁金香百合花
那怎么才能让运行结果变成这样呢?
我喜欢玫瑰花,郁金香,百合花
这个问题留给读者
3.组件
react允许将代码封装成一个组件,然后像插入普通HTML标签一样,在网页中插入这个组件,react.createClass()方法用于生成一个组件类。
import React,{ Component } from "react";
import { render } from "react-dom";
var Main = React.createClass({
render: function(){
return hello {this.props.name}
}
})
render(
"xiao" />,
document.getElementById("root")
);
另一种写法:
import React,{ Component } from "react";
import { render } from "react-dom";
class Main extends Component{
render(){
return hello {this.props.name}
}
}
render(
"xiao" />,
document.getElementById("root")
);
运行结果都是一样的,如下
hello xiao
上面的代码中变量Main就是一个组件类,模板插入时,会自动生成一个实例,所有组件类都必须有自己的render方法,用于输出组件。
注意:
组件的第一个字母必须大写,否则会报错,比如Main,不能写成main。
另外组件类只能包含一个顶级标签,否则会出现问题。
class Main extends Component{
render(){
return(
hello {this.props.name}
,
喜欢
)
}
}
上面代码中
<h1>hello {this.props.name}h1>,
<p>喜欢p>
用逗号隔开不会报错,但不能正常渲染,渲染的结果如下
喜欢
如果去掉逗号的话,就会报错了
<h1>hello {this.props.name}h1>
<p>喜欢p>
报错如下:
Module build failed: SyntaxError: F:/react/redux/react_excise/src/js/index.js: Adjacent JSX elements must be wrapped in an enclosing tag (11:3)
意思就是说相邻的JSX元素必须被包裹在一个封闭的标签
所以正确的写法就是用一个标签包裹起来,正如上面所说组件类只能包含一个顶级标签。
实现代码如下:
class Main extends Component{
render(){
return(
<div>
hello {this.props.name}
喜欢
div>
)
}
}
render(
"xiao" />,
document.getElementById("root")
);
运行结果如下:
组件类添加属性时要注意,class属性改成className,for属性改成forName。这是因为class和for是javascript标签的保留字。
4、 this.props.children
this.props对象的属性与组件的属性一一对应,但是有一个例外,就是this.props.children属性,它表示组件的所有子节点。
import React,{ Component } from "react";
import { render } from "react-dom";
class Main extends Component{
render(){
return(
{
React.Children.map(this.props.children,function(child){
return (
- {child}
)
})
}
)
}
}
render(
hello
world
,
document.getElementById("root")
);
上面代码组件Main有两个span子节点,它们可以通过this.props.childrend读取,运行结果如下
这里需要注意, this.props.children 的值有三种可能:
如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object;
如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。
React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。
5.PropTypes
组件的属性可以接受任意值,字符串、对象、函数等等都可以,有时我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。
组件类的propTypes属性就是验证组件实例的属性是否符合要求。
我们看一下之前是怎么写的
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return {this.props.title}
;
}
});
var data = 123;
ReactDOM.render(
,
document.body
);
这种写法,是本地引入react.js、react-dom.js和browser.js的写法
这种写法创建组件类通过React.createClass这种方式,但目前这种方法在ES6中已经过时。
我们看下面这种写法:
import React,{ Component } from "react";
import { render } from "react-dom";
import { PropTypes } from "prop-types";
class MyComponent extends Component{
propTypes: {
title: React.PropTypes.string.isRequired,
}
render(){
return (
{this.props.title}
)
}
}
let data = 123;
render(
,
document.getElementById('root')
);
import React,{ Component } from "react";
import { render } from "react-dom";
首先我介绍一哈上面这两行代码中的作用,这两行代码是引入react和react-dom,这是ES6的写法,对ES6不熟悉的,可以看阮一峰的ES6,这里就不详细介绍了。
propTypes: {
title: React.PropTypes.string.isRequired,
}
其次,上面这段代码是无效的,为什么呢?
因为ES6语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外,所以proptypes要写在class组件外
我们看代码是如何实现的:
import React,{ Component } from "react";
import { render } from "react-dom";
class MyComponent extends Component{
render(){
return (
{this.props.title}
)
}
}
MyComponent.proptypes = {
title: React.PropTypes.string.isRequired,
}
let data = 123;
render(
,
document.getElementById('root')
);
但这时报错了,两个警告:
bundle.js:1004 Warning: Failed prop type: Invalid prop `title` of type `number` supplied to `MyComponent`, expected `string`.
这个错误就是我们用PropTypes的目的,因为不是字符串,所以才会报错,如果写成字符串的话,就会消失。
bundle.js:9912 Warning: Accessing PropTypes via the main React package is deprecated. Use the prop-types package from npm instead.
这个错误的意思就是要安装prop-types,我安装了,但没有用,你可以自己试一下看为什么?在这里不影响我们验证的内容。
真是路漫漫其修远兮啊 这个坑花费了好长时间才踩掉,但我很开心。
经过和室友的研究,最终解决这个上面这个遗留的bug,只需要把React去掉,引进Proptypes。
在引进前需要在命令行中安装
npm install prop-types --save-dev
这是webpack的用法,对webpack不了解的可以看我之前写的webpack教程。
有没有安装成功可以在package.json文件查看
import { PropTypes } from "prop-types";
代码如下:
import React,{ Component } from "react";
import { render } from "react-dom";
import { PropTypes } from "prop-types";
class MyComponent extends Component{
render(){
return (
{this.props.title}
)
}
}
MyComponent.propTypes = {
title: PropTypes.string.isRequired,
}
let data = "123";
render(
,
document.getElementById('root')
);
此外,getDefaultProps方法可以用来设置组件属性的默认值。
代码如下:
import React,{ Component } from "react";
import { render } from "react-dom";
import { PropTypes } from "prop-types";
class MyComponent extends Component{
render(){
return (
{this.props.title}
)
}
}
MyComponent.defaultProps = {
title: "this is flower"
}
render(
,
document.getElementById('root')
);
上面代码中会输出:
this is flower
6.获取真实的DOM节点
组件并不是真实的DOM,而是存在内存中的一种数据结构,是一种虚拟的DOM,当它真正插入文档中,才会变成真实的DOM。
根据react的设计,所有的DOM变化都先发生在虚拟DOM上,再将实际变动的部分反应在实际的DOM上变化,这种算法叫做DOM diff,它可以提高网页的表现性能。
但是,有时从组件中获取真实DOM节点,这时就用到ref属性。
import React,{ Component } from "react";
import { render } from "react-dom";
import { PropTypes } from "prop-types";
class MyComponent extends Component{
handleClick(){
this.refs.myTextInput.focus();
console.log(this.refs.myTextInput.focus());
}
render(){
return (
type="text" ref="myTextInput" />
type="button" value="Focus the text input" onClick={this.handleClick}/>
)
}
}
render(
,
document.getElementById('root')
);
上面这种写法会报错,错误如下:
Uncaught TypeError: Cannot read property 'refs' of null
为什么会报错呢?
因为涉及到this的问题,这里有两种解决方案
一种是:绑定this
"button" value="Focus the text input" onClick={this.handleClick.bind(this)} />
另一种是用箭头函数,会自动绑定this
"button" value="Focus the text input" onClick={()=>{this.handleClick()}} />
在handleClick函数中就可以打印你在输入框中输入的内容。
例如:
xiao
注意:
这里的this指的是组件myComponent,用法可以看30分钟掌握ES6教程
上面的代码中,组件MyComponent的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的DOM,怎么获取呢?
虚拟DOM是拿不到数据的,为了做到这一点,文本输入框必须有一个ref属性,然后this.refs.[refName]就会返回这个真实的DOM节点。
7.this.state
组件免不了和用户互动,React的一大创新,就是将组件看成状态机,一开始有一个初始状态,然后用户互动,导致状态发生变化,从而重新渲染UI。
import React,{ Component } from "react";
import { render } from "react-dom";
import { PropTypes } from "prop-types";
class MyComponent extends Component{
constructor(props){
super(props);
this.state = {
liked: false
}
}
handleClick(event){
//console.log("aa");
this.setState({liked: !this.state.liked})
}
render(){
var text = this.state.liked ? 'like': 'haven\'t liked';
return (
this.handleClick.bind(this)}>
You { text } this click toggle
)
}
}
render(
,
document.getElementById('root')
);
注意:
this的绑定,有三种 方式:
1. var _this = this;
2. bind(this);
3. 箭头函数,它是自动绑定this。
具体需要改动的代码如下:
第一种方式:ES6箭头函数的写法
()=>{this.handleClick()}}>
You { text } this click toggle
第二种方式:绑定方式(bind(this))
this.handleClick.bind(this)}>
You { text } this click toggle
使用ES6 class语法创建组件,初始化state的工作要在constructor中完成,不需要再调用getInitialState方法。这种语法更加符合JavaScript的习惯。
this.state属性读取,当用户点击组件,导致状态改变,this.setState就修改状态,每次修改以后,自动调用render()方法。再次渲染页面。
在上面整个代码中constructor相当于初始化,执行了一次。
import React,{ Component } from "react";
import { render } from "react-dom";
import { PropTypes } from "prop-types";
class MyComponent extends Component{
constructor(props){
super(props);
this.state = {
liked: false
}
console.log("1",this.state.liked)
}
handleClick(){
this.setState({liked: !this.state.liked})
}
render(){
console.log("2",this.state.liked)
var text = this.state.liked ? 'like' :'no liked';
return(
{this.handleClick()}}>
You { text }
)
}
}
render(
,
document.getElementById('root')
);
上面代码我们可以分割出来研究
constructor(props){
super(props);
this.state = {
liked: false
}
console.log("1",this.state.liked)
}
这段代码主要是初始化,所有里面的console执行了一次
render(){
console.log("2",this.state.liked)
var text = this.state.liked ? 'like' :'no liked';
return(
()=>{this.handleClick()}}>
You { text }
)
}
当点击p标签的内容时,就会执行下面这个函数
handleClick(){
this.setState({liked: !this.state.liked})
}
每次状态都会取相反的值,第一次默认是false,那么点击一次就会变成true,就会调用render,重新渲染页面。