React.js概览
React是什么
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
React 中拥有多种不同类型的组件,我们先从 React.Component
的子类开始介绍:
class ShoppingList extends React.Component {
render() {
return (
//以下是一个react元素
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
// 用法示例: <ShoppingList name="Mark" />
在上面的代码中,使用了组件的形式来告诉React我们希望在屏幕上呈现的内容。正是因为函数式声明,使得在数据发生变化时React能够高效地重新渲染组件。
我们首先定义了一个叫ShoppingList
的组件类,每一个组件可以接受一些参数,这些参数被称为props,上面的name="Mark"
就是将参数传入组件的过程。然后再通过render
渲染到页面。
由React扩展的Js语法,JSX 可以让你更轻松地书写这些函数式声明结构。
上面的这些代码等同于:
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* ... h1 children ... */),
React.createElement('ul', /* ... ul children ... */)
);
在 JSX 中你可以任意使用 JavaScript 表达式,只需要用一个大括号把表达式括起来。每一个 React 元素事实上都是一个 JavaScript 对象,你可以在你的程序中把它当保存在变量中或者作为参数传递。
前文中的 ShoppingList 组件只会渲染一些内置的 DOM 组件,如<div />
、<li />
等。但是你也可以组合和渲染自定义的 React 组件。例如,你可以通过 <ShoppingList />
来表示整个购物清单组件。每个组件都是封装好的,并且可以单独运行,这样你就可以通过组合简单的组件来构建复杂的 UI 界面。
阅读初始代码
在前面我们初始化的源代码中,打开 src/index.js
,这些初始代码是我们要开发的小游戏的基础代码。在上面已经提供了 CSS 样式,这样你只需要关注使用 React 来开发这个井字棋了。
通过阅读代码可以看到三个组件:
Square-棋盘格
class Square extends React.Component {
render() {
return (
<button className="square">
{/* TODO */}
</button>
);
}
}
我们可以看到,Square组件返回了一个类名叫 square
的 button
按钮。React 将这个类名为 square
组件封装成了一个组件,方便后面调用。
在 css 代码中,它有这样的样式:
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
Board-棋盘
在这个命名为 Board
的函数中利用上面的 Square
的组件创建了一个九宫格,这个九宫格由三行组成,每行由三个棋盘格组成。
class Board extends React.Component {
renderSquare(i) {
return <Square />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
Game-游戏主体
在这个函数中,利用上面的棋盘封装了一个游戏界面。
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
通过 Props 传递数据
每个定义的组件之间可以通过 Props
传递参数。我们尝试将数据从 Board 组件传递到 Square 组件中。
在 Board
组件的 renderSquare
方法中,我们将代码改写成下面这样,传递一个名为 value
的 prop
到 Square
当中:
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
···
}
修改 Square
组件中的 render
方法,把 {/*TODO*/}
替换为 {this.props.value}
,以显示上文中传入的值:
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value}
</button>
);
}
}
修改前:
修改后,每个格子中都会存在一个数字:
我们刚刚成功地把一个 prop
从父组件 Board
“传递”给了子组件 Square
。在 React 应用中,数据通过 props
的传递,从父组件流向子组件。也就是说,我们成功 Board 中传递的参数值(0~8)给了子组件进行处理,并显示出来。
给组件添加交互功能
接下来我们试着让棋盘的每一个格子在点击之后能落下一颗 X 作为棋子。 首先,我们把 Square
组件中 render()
方法的返回值中的 button
标签修改为如下内容:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={function () { alert('click') }}>
{this.props.value}
</button>
);
}
}
如果此刻点击某个格子,浏览器会弹出提示框。触发事件 onClick
,显示出提示:“click”。在这里 alert
是弹出事件,括号里面的是弹出参数。
为了少输入代码,同时为了避免 this
造成的困扰,我们在这里使用箭头函数 来进行事件处理,如下所示:
onClick={() => alert('click')}
此处使用了 onClick={() => alert('click')}
的方式向 onClick
这个 prop
传入一个函数。 React 将在单击时调用此函数。但很多人经常忘记编写 () =>
,而写成了 onClick={alert('click')}
,这种常见的错误会导致每次这个组件渲染的时候都会触发弹出框。
接下来,我们希望 Square
组件可以“记住”它被点击过,然后用 “X” 来填充对应的方格。我们用 state 来实现所谓“记忆”的功能。
可以通过在 React 组件的构造函数中设置 this.state
来初始化 state。this.state
应该被视为一个组件的私有属性。我们在 this.state
中存储当前每个方格(Square)的值,并且在每次方格被点击的时候改变这个值。
首先,我们向这个 class
中添加一个构造函数,用来初始化 state:
constructor 函数时组件最先执行的函数。
class childen extends react.Component{
constructor(props){
super(props);
this.state={
attr1:"",
}
}
}
spuer(): 注意在定义组件的时候可以没用constructor方法,一旦定义,就必须使用spuer方法,这不是react规定的而是es6要求,具体原因如下:
首先,我们向这个 class 中添加一个构造函数,用来初始化 state:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
// 用于记忆状态
value: null,
}
}
render() {
return (
<button className="square" onClick={() => alert("Click!")}>
{this.props.value}
</button>
);
}
}
现在,我们来修改一下 Square 组件的 render 方法,这样,每当方格被点击的时候,就可以显示当前 state 的值了:
- 在
<button>
标签中,把this.props.value
替换为this.state.value
。 - 将
onClick={...}
事件监听函数替换为onClick={() => this.setState({value: 'X'})}
。 - 为了更好的可读性,将
className
和onClick
的prop
分两行书写。
修改之后,Square 组件中 render
方法的返回值中的 <button>
标签就变成了下面这样:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
// 用于记忆状态
value: null,
}
}
render() {
return (
<button
className="square"
onClick={() => this.setState({ value: 'x' })}>
{this.state.value}
</button>
);
}
}
每次在组件中调用 setState
时,React 都会自动更新其子组件。