持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
前言
昨天上boss想找找合适的岗位的时候,发现react还是不可或缺的技术栈啊 😅 😅,于是决定为了提高自己的上限,静下心来沉淀这一门技术学问。
一开始习惯性找了好多杂七杂八的教程,但我觉得都不太适合自己,要说掏心窝子的话佬儿们,跟着官方文档学习入门教程,强过讲得再好的视频,b站免费的也好,去慕课网还是拉构花重金买的也好,都敌不过原汁原味的官方文档教程好使。
我以前每每打算开始学一个新东西的时候,第一反应就是去b站搜,这不能说不好,但大伙儿明白,无论是你看到的视频讲解还是我写的博客,都是别人自己对于知识点的理解,而官方文档是标准、是规范,是万物起源。
所以无论如何,你看我的博客也好,看b站的视频也罢,我都真心推荐大家耐心地去跟着官方文档学习,希望你能够拥有自己对于react的理解,积累属于你自己的经验,在职场中能够建立个人的判断去完成你的工作。
新征程,开始react之旅吧!
先用满满的敬畏之心献上官方文档地址: react.docschina.org/tutorial/tu…
文档中通过举例一个“井字棋”小游戏的开发过程来帮助我们了解react最常见的工作机制,比如组件传参、状态管理等,我们直接在他给的浏览器环境中学习尝试。
初始状态下,我们能看到基础代码中预设了三个react组件,分别是Square、Board、Game,Square渲染了每一个方格中的button,Board渲染了整个棋盘,Game中记录逻辑
第一步:初识props传递数据
我们都知道,组件化开发中最常见的一个问题就是如何实现组件传参,有父传子,子传父,兄弟组件通信,全局通信等等,在这里我们先只看最简单的父子传参
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
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>
);
}
}
这里需要把作为父组件的Board中的数据传递到子组件Square中去,逻辑如下:
-
在Board中定义一个
renderSquare方法,返回一个带参数的Square组件 -
定义的Square组件中,render函数返回一个button,并将
renderSquare传递过来的value渲染在其中,通过this.props.value获取传递的参数 -
在Board中render函数返回了9个div,每一个div中通过
this.renderSquare(0)传递了一个实参 -
最后通过
{this.renderSquare(3)}渲染返还的button
如果还不明了的小伙伴可以看下面这个图帮助理解
当我们审查元素可以证实这个逻辑的正确性:
也如大家所见,目前出来的效果就是成功地把携带的0123等实参输送到了Suqare中并渲染出来,井字棋中是规整排列的数字
第二步:添加事件实现交互功能
当我们需要实现用户和网页的交互时,不可避免地要通过添加事件来实现,这在我们日常开发中也是基操,所以放在这里让大家熟悉熟悉
在这里,我们需要给这个井字棋实现的功能是,让棋盘的每一个格子在点击之后能落下一颗 “X” 作为棋子,这里直接上这一步骤源码的传送门,大家可以跟着官方文档的详细解说一步步去跟着敲,我在这里只负责解析思路和划重点
首先我们明确功能,点击格子之后要让格子内出现一个X标记
所以我们要把事件添加在Square中,给Square设定一个state,用来存放它被标记的初始值
然后给Square返回的button上添加一个onClick事件,让它在每一次被点击的时候通知react去重新渲染Square组件
组件更新之后,Square组件的this.state.value的值会变为X,于是我们便能看到格子上出现X
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>
);
}
}
第三步:实现简单的状态管理
这个小游戏最终是要分出胜负的,那么我们根据什么来分胜负呢,玩过井字棋的小伙伴都知道,对角线或连续且相同的3个横竖标记就算赢,为了实现这一功能,我们就需要记录玩家下棋每一步的状态
当前每一个Square组件都维护了游戏的状态,如果我们把所有的Square值都放在同一个地方,就能比较出胜者了
也就意味着,我们需要集中管理Square的状态
所以我们将所有的state状态数据存储在Board父组件当中,之后Board组件可以将这些数据通过props传递给各个Square子组件
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
//将 Board 组件的初始状态设置为长度为 9 的空值数组
squares: Array(9).fill(null),
};
}
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
renderSquare(i) {
return (
//更新 Board 中的 state
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>
);
}
render() {...}
}
由于我们集中在Board组件中管理Square的状态,所以之前在Square组件中单独记录状态的constructor舍弃不再使用
在这里需要注意的是,我们需要想办法解决一个问题,集中在Board管理之后,如何让Square组件去更新Board中的state呢?
由于state对于每个组件来说是私有的,因此我们不能直接通过Square来更新Board的state;相反,从Board组件向Square组件传递一个函数,当Square被点击的时候,这个函数就会被调用
注:
fill()方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。
slice()方法返回一个新的数组对象,这一对象是一个由begin和end决定的原数组的浅拷贝(包括begin,不包括end)。原始数组不会被改变。
第四步:轮流落子功能实现
为了完善这个游戏的体验,我们需要区分玩家落子,如果第一步下的是X,那么下一步就下O
这一个功能比较简单,在上一个步骤中我们已经实现了handleClick函数,这个功能只需在该函数中反转Boolean值即可
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
squares: squares,
//两者切换通用逻辑
xIsNext: !this.state.xIsNext,
});
}
然后在Board组件中渲染status的值,就可以显示下一步是哪个玩家的了
render() {
const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
return (
// 其他部分没有改变
总结
上面四步已经涵盖了react中大部分的基础,看完官方文档入门教程我大概对react开发就已经有点思路了,我自己之前是一直用vue的,从vue中很多思想都能迁移到react上,理解起来并不难,只是语法上有些差异。
而且vue3已经和react有点像了,函数式编程,在这里我只想说一句,运用框架的逻辑其实都差不多,掌握核心的东西就能很快地融会贯通。
后期我也会继续深入学习,努力更新关于react的知识库~喜欢的小伙伴可以点赞+关注不迷路!😘