React基础知识点开发井字游戏
我们一起用 React 开发一个井字棋(tic-tac-toe)。游戏规则,在九宫格内输入棋子“X”或“O”,首先在纵向、横向或斜方向上三个同一棋子连成一线为胜出。 通过这个简单的棋盘游戏我们学习下面这些知识点;
环境搭建 create-react-app
介绍React的基础知识:组件、props和state
React开发过程最常用的技术
时间旅行 深刻了解React的独特优势
1、环境搭建
环境搭建流程如下:
1、确保安装了较新版本的Node.js。
2、按照Create React App安装指南创建一个新项目,步骤如下:
2.1 执行指令:npx create-react-app tic-tac-toe(项目名称)
2.2 执行指令:cd tic-tac-toe
2.3 执行指令:npm install
3、在工程项目中src/文件夹下创建index.css文件
4、保留原工程项目中src/文件夹下的index.js文件
1.1、新建index.css文件,如下
index.css
App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
1.2、修改index.js文件
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App"; // 将导入的App组件删除
const rootElement = document.getElementById("root");
ReactDOM.render(
{/* 将引入的app组件删除 */}
,
rootElement
);
1.3、创建三个新组件
Square:渲染单独的一个棋子组件
Borad:渲染棋盘组件
Game:渲染默认值的一个棋盘
Game.js
import React from 'react';
import Board from './Board';
class Game extends React.Component {
render() {
return (
);
}
}
export default Game;
Board.js
import React from 'react';
import Square from './Square';
class Board extends React.Component {
// 返回一个组件,React元素
renderSquare(i) {
return ;
}
render() {
const status = '下一个棋手: X';
return (
);
}
}
export default Board;
Square.js
import React from 'react';
class Square extends React.Component {
render() {
return (
);
}
}
export default Square;
上面新增加的三个组件,只是初始化的文件,在后面的开发流程中增加新的代码。
对index.js文件做如下修改
import React from "react";
import ReactDOM from "react-dom";
import './index.css';
import Game from './Game';
const rootElement = document.getElementById("root");
ReactDOM.render(
{/* 加载Game组件 */}
{/* Game组件是组装的React元素 */}
,
{/* rootElement 是整个项目唯一的一个挂载点,这对应的单页面工程只有一个页面 页面都是由数据控制 */}
rootElement
);
关于index.js文件中存在的知识点
1、引入react-dom包的作用: react-dom的 package 提供了可在应用顶层使用的 DOM(DOM-specific)方法,如果有需要,你可以把这些方法用于 React 模型以外的地方。不过一般情况下,大部分组件都不需要使用这个模块。
2、ReactDOM.render()就是react-dom包中的方法,它用来渲染一个React元素,并返回对该组件的引用。
ReactDOM.render() 会控制你传入容器节点里的内容。当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 DOM 差分算法(DOM diffing algorithm)进行高效的更新。
对React做一个简单的总结
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
下面是我们对新增加的这三个组件进行特定业务的编码
2、Square、Board等组件的开发
2.1、单纯测试props属性
在 Board 组件的 renderSquare 方法中,我们将代码改写成下面这样,传递一个名为 value 的 prop 到 Square 当中:
class Board extends React.Component {
renderSquare(i) {
// 在父组件中给子组件传递一个props属性value。
return ;
}
}
修改 Square 组件中的 render 方法,把 {/* TODO */} 替换为 {this.props.value},以显示上文中传入的值:
class Square extends React.Component {
render() {
return (
);
}
}
经过上面代码的修改,页面修改前后展现的对比。如下图:
修改前:
1583560819929.png
修改后:
1583560863857.png
成功地把一个 prop 从父组件 Board “传递”给了子组件 Square。在 React 应用中,数据通过 props 的传递,从父组件流向子组件。
2.2、给组件添加交互功能
棋盘上的每一个格子对一个就是一个"Square"组件,给Square组件增加一个点击事件。当点击小格子时,页面会弹出一个信息提示框。
2.2.1、给Square组件增加点击事件
修改 Square.js文件
import React from 'react';
class Square extends React.Component {
render() {
return (
);
}
}
export default Square;
2.2.2、点击Square组件,格子显示“X”
我们希望 Square 组件可以“记住”它被点击过,然后用 “X” 来填充对应的方格。我们用 state 来实现所谓“记忆”的功能。
可以通过在 React 组件的构造函数中设置 this.state 来初始化 state。this.state 应该被视为一个组件的私有属性。我们在 this.state 中存储当前每个方格(Square)的值,并且在每次方格被点击的时候改变这个值。
state是组件内部的属性。组件本身是一个状态机,它可以在constructor中通过this.state直接定义它的值,然后根据这些值来渲染不同的UI。当state的值发生改变时,可通过this.setState方法让组件再次调用render方法,来渲染新的UI。
修改 Square.js文件
import React from 'react';
class Square extends React.Component {
constructor(props){
super(props);
// 初始化棋盘格子中要显示的值“value”
this.state = {
value: null
};
}
render() {
return (
// state是组件内部的“状态”属性,从外部不能改变,只能通过setState方法更新
);
}
}
export default Square;
现在再点击棋盘中的格子,在格子里就会展示“X”,如图所示:
1583572520960.png
3、增加游戏规则
井字棋游戏规则:两位选手,各执祺子为“X”或“O”。在九宫格内,任一选手的棋子在纵向、横向或斜方向上能排成一条直线,即为胜方。
剩下的功能就是需要交替在棋盘上放置“X”或“O”,并且判断出胜者。
Square为格子组件,Board为棋盘组件,Board组件是Square组件的父组件。棋子Square上的数据都通过state保存到Board上。
3.1、将棋子的数据保存到Board组件中state上
为Board组件添加构造函数,将Board组件的初始状态设置为长度为9的空数组。
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
// fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素
squares: Array.fill(null)
}
}
// 返回一个组件,React元素
renderSquare(i) {
// 在父组件中给子组件传递一个props属性value。
return ;
}
// ....省略其它源码
}
让我们再一次使用 prop 的传递机制。我们通过修改 Board 来指示每一个 Square 的当前值('X', 'O', 或者 null)。我们在 Board 的构造函数中已经定义好了 squares 数组,这样,我们就可以通过修改 Board 的 renderSquare 方法来读取这些值了。
renderSquare(i) {
return ;
}
这样,每个 Square 就都能接收到一个 value prop 了,这个 prop 的值可以是 'X'、 'O'。
上面的代码,在Board组件中对Square组件进行初始化,用Board组件来维护那些被填充的格子。 我们需要想办法让 Square 去更新 Board 的 state。由于 state 对于每个组件来说是私有的,因此我们不能直接通过 Square 来更新 Board 的 state。
state是组件的内部状态,对外是私有,从外部是不能改变state的状态,只能在组件内部使用setState方法来更新
在Board组件中向Square组件传递一个props的函数,当Square组件被点击时就调用这个函数。
renderSquare(i) {
return (
this.handleClick(i)}
/>
);
}
现在我们从 Board 组件向 Square 组件中传递两个 props 参数:value 和 onClick。onClick prop 是一个 Square 组件点击事件监听函数。接下来,我们需要修改 Square 的代码:
Square.js
class Square extends React.Component {
render() {
return (
);
}
}
1、将Square组件的render方法中的this.state.value替换为this.props.value;
2、将Square组件的render方法中的this.setState替换为this.props.onClic();
3、删掉Square组件中的构造函数constructor,因为该组件不再保存游戏的state;
Board组件和Square组件间的交互流程如下:
每一个 Square 被点击时,Board 提供的 onClick 函数就会触发。我们回顾一下这是怎么实现的:
1、向 DOM 内置元素
{/* 加载棋盘组件 */}
{/* status */}
- {/* TODO */}
{status}
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
发表评论 (审核通过后显示评论):