零、初识React
在一个HTML文件中引入react
和react-dom
后,可以访问到这两个对象
可以看到,React对象中有一些方法,比如我们接下来要学习的
creatElement
等
一、createElement
API
React对象中有一个createElement
方法,在这个方法中可以定义一个虚拟DOM
第一个参数表示这个虚拟DOM的名称(如div,p,h1等等,也可以是一个组件的名称)
第二个参数表示这个虚拟DOM上一些必要的属性(如id,classname,titile),如果没有则写入null
第三个参数开始为这个虚拟DOM中所有子元素(为虚拟DOM,所以也需要createElement。如果是文本节点可以直接书写而不需要createElement),有多个则直接跟在后面,作为第三第四第五个参数。
let h1 = React.createElement(
'h1',
{
title:'一个标题',
},
'the first react dom'
);
console.log(h1);
可以看到,输出的变量为一个对象,它就是我们创建出来的虚拟DOM。其中有一些属性以
_
开头,这是一些内部属性。type
为这个元素的类型,props
则是这个元素的一些子元素等等。
- 1、将虚拟DOM渲染到页面中
react-dom
中的render
函数用于将虚拟DOM渲染到页面中。
接收两个参数:第一参数表示虚拟DOM,第二个参数表示真实DOM容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<div id="app"></div>
</head>
<body>
</body>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script>
console.log(React);
console.log(ReactDOM);
let h1 = React.createElement(
'h1',
{
title:'一个标题',
},
'the first react dom'
);
console.log(h1);
ReactDOM.render(h1,document.getElementById('app'));
</script>
</html>
二、组件化
- 1.使用
createClass
创建组件(可以直接跳过)
-
注意:react 16将createClass移除了,使用es6语法,如果必须使用es5创建类的话,请使用create-react-class包
如果一个虚拟DOM需要被多次复用,则需要将它封装在一个组件内。使用createClass
方法。
createClass
方法接收的参数是一个对象,对象中的属性和方法是对组件的说明。其中render
方法是将组件中的虚拟dom输出,所以我们将虚拟dom定义在render
方法中。
render
方法的返回值是 vetrual dom tree,就是一个组件,组件名称通常第一个字母大写。
let H1 = React.createClass({
render: function () {
return React.createElement(
'h1',
{
title:'一个标题',
},
'the first react dom'
)
}
});
console.log(H1); //输出为一个function
此时发现此时得到一个函数,我们需要的是一个虚拟dom,即一个js对象。所以我们调用React.createElements()
方法,第一参数为得到的组件函数,没有其他属性和子元素,所以为null,可以直接不写。
let h1 = React.createElement(H1);
ReactDOM.render(h1,document.getElementById('app1'));// 渲染到页面中
在这里我们可以创建多个虚拟DOM分别挂接在不同的页面元素上,这样不同的元素就是不同的组件,互不影响。如果没有使用组件化,需要多次使用的话就是
ReactDOM.render(h1,document.getElementById('app1'));
ReactDOM.render(h1,document.getElementById('app2'));
此时是同一个虚拟DOM的多次挂载
而组件化过后是
let h1 = React.createElement(H1);
ReactDOM.render(h1,document.getElementById('app1'));
let h2 = React.createElement(H1);
ReactDOM.render(h2,document.getElementById('app2'));
此时h1 h2是不同的虚拟DOM,互不影响。
- 2.使用函数构造组件
function H2() {
return <h1 style={{color: 'red', fontSize: '14px', WebkitBoxShadow: '10px 10px yellow'}}>一个胖子</h1>
}
ReactDOM.render(<H2/>, document.getElementById('app'));
- 3.ES6创建组件
class H2 extends React.component{
render() {
return (
<h1 title='一个标题'>the first react dom</h1>
)
}
}
ReactDOM.render(<H2 />,document.getElementById('app'));
- 3.组件和元素的区别
在我们使用createElement
构造React元素时,我们得到的是一个React元素,而使用类时,我们得到的是一个函数,通过对组件调用ReactDOM.render
方法,组件将生成的React元素返回,ReactDOM实现DOM的更新。
所以我们也可以这么写
function H2() {
return <h1 style={{color: 'red', fontSize: '14px', WebkitBoxShadow: '10px 10px yellow'}}>一个胖子</h1>
}
let element = <H2 />; //生成ReactDOM
ReactDOM.render(element, document.getElementById('app'));
class H2 extends React.component{
render() {
return (
<h1 title='一个标题'>the first react dom</h1>
)
}
}
let element = <H2 />; //生成ReactDOM
ReactDOM.render(element,document.getElementById('app'));
注意组件的返回值只能是单个元素,所以如果是多个,我们需要在外层嵌套一个div
三、JSX语法
解决创建一个虚拟DOM成本过高的问题,使用JSX语法可以让我们用书写HTML的方法来书写虚拟DOM元素。需要编译,使用babel。
let H2 = React.createClass({
render() {
return (
<h1 title='一个标题' className='title'>the first react dom</h1>
{/*注释要写在插值符号即花括号中,并且使用多行注释的符号,否则会被当成文本元素渲染出来*/}
{/*在这里类名叫做className 因为JSX其实是JS的外延而非HTML,所以属性都是按照JS的语法来书写的,比如驼峰*/}
)
}
});
let h2 = (<H2 />);
ReactDOM.render(h2,document.getElementById('app'));
四、插值符号{}
在插值符号{}中可以写入变量、语句等等。在return的jsx中使用。
五、style样式的设置
为组件内元素添加样式时,必须传递一个对象
class H3 extends React.Component {
render() {
return (
<h1 style={{color:'red',fontSize:'14px',WebkitBoxShadow:'10px 10px yellow'}}>一个胖子</h1>
)
}
}
ReactDOM.render(<H3 />,document.getElementById('app'));
传递了一个对象,并且必须放在{}
中。
多单词的属性采用驼峰,有CSS3前缀的第一个字母要大写。
六、props属性
props属性用于向组件传递数据,在复用组件时,传递不同的值实现组件的多样化。
在ReactDOM.render中渲染组件时,向组件传递数据
function H2(props) {
return <h1>{props.name}</h1>
}
ReactDOM.render(<H2 />,document.getElementById('app'));
class H2 extends React.Component {
render() {
return (
<h1 title='一个标题'>{this.props.titleData}</h1>
)
}
}
ReactDOM.render(<H2 titleData="the data of user"/>, document.getElementById('app'));
七、状态State
- 1.状态与属性十分相似,但是状态是私有的,完全受控于当前组件。需要添加状态的类,需要按照class语法进行声明,然后在
constructor
中对state进行定义。构造函数是唯一能够初始化 this.state 的地方。
class H2 extends React.Component {
constructor(props){
super(props);
this.state = {
state1: '1223'
}
}
render() {
return (
<h1 title='一个标题'>{this.state.state1}</h1>
)
}
}
ReactDOM.render(<H2 titleData="the data of user"/>, document.getElementById('app'));
state也不一定要写在contructor里面,可以像下面这样
class H2 extends React.Component {
state = {
state1: '1223'
}
render() {
return (
<h1 title='一个标题'>{this.state.state1}</h1>
)
}
}
ReactDOM.render(<H2 titleData="the data of user"/>, document.getElementById('app'));
- 2.状态的更新应该通过
this.setState({state1:'newValue'})
方法,而不能直接修改this.state.state1
。
this.setState
方法的参数也可以是一个函数,像下面这样
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment //该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数:
}));
组件可以选择将其状态作为属性传递给子组件
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
这个子组件也可以是自定义的组件
<FormattedDate date={this.state.date} />
然后子组件在props中得到传递过来的属性,但是子组件并不知道也不关心这个属性是来自父组件的属性、状态’还是用户手动输入。
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
这通常被称为自顶向下或单向数据流。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。
八、事件处理
React元素也和DOM元素一样可以绑定时间处理函数,也是采用on
来绑定,但是要采用驼峰写法
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
<button onClick={activateLasers}>
Activate Lasers
</button>
当使用 ES6 class
语法来定义一个组件的时候,事件处理器会成为类的一个方法。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
必须注意JSX 回调函数中的 this
,类的方法默认是不会绑定到 this
的。如果你忘记绑定 this.handleClick
并把它传入 onClick
, 调用这个函数的时候 this
的值会是 undefined
。(也就是说如果你没有使用bind绑定,那么onClick的处理函数里面的this就是undefinde)。
除了上面代码的方法解决,还可以这样
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
}
handleClick = () => {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
九、列表生成
let nums = [1,2,3];
let list = nums.map((item) => {
return <li>{item}</li>
});
ReactDOM.render(<ul>{list}</ul>,document.getElementById('app'));
函数组件形式
function NumList(props) {
const nums = props.nums;
const list = nums.map((item) => {
return <li>{item}</li>
})
return(
<ul>
{list}
</ul>
)
}
const nums = [1,2,3];
ReactDOM.render(<NumList nums={nums} />,document.getElementById('app'));
此时有一个问题就是li
没有key
属性(按道理这里应该会有一个警告,我也不知道为什么我没有...),我们需要为每一个li
元素添加一个key
。一个元素的key
最好是这个元素在列表中拥有的一个独一无二的字符串
。
function NumList(props) {
const nums = props.nums;
const list = nums.map((item) => {
return <li key={item.toString()}>
{item}
</li>
})
return(
<ul>
{list}
</ul>
)
}
const nums = [1,2,3];
ReactDOM.render(<NumList nums={nums} />,document.getElementById('app'));
key会作为给React的提示,但不会传递给组件。如果组件中需要使用和key相同的值,应将其作为属性传递
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
这里Post组件可以读出props.id,但是不能读出props.key
在前面我们声明了一个单独的list变量并将其放在jsx中,其实我们也可直接将map放到jsx中
function NumList(props) {
const nums = props.nums;
return(
<ul>
{nums.map((item) => {
return <li key="{item.toString()}">{item}</li>
})}
</ul>
)
}
const nums = [1,2,3];
ReactDOM.render(<NumList nums={nums} />,document.getElementById('app'));