React
简介:
React 是一个用于构建用户界面的 JAVASCRIPT 库。
- 声明式设计 −React采用声明范式,可以轻松描述应用。
- 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
- 灵活 −React可以与已知的库或框架很好地配合。
- JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
- 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
- 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
JSX:
- React 并没有采用将标记与逻辑进行分离到不同文件这种人为地分离方式,而是通过将二者共同存放在称之为“组件”的松散耦合单元之中,来实现关注点分离。
嵌入表达式
大括号可以是各种js表达式
如:1+1,user.nickName,getName(user)
const name = 'Josh Perez'; const element = <h1> Hello, {name} </h1>; const element = <h1> Hi, {getName(user)} </h1>;
指定属性
你可以通过使用引号,来将属性值指定为字符串字面量:
const element = <a href="https://www.reactjs.org"> link </a>;
也可以使用大括号,来在属性值中插入一个 JavaScript 表达式:
const element = <img src={user.avatarUrl}></img>;
但是注意不能同时使用
{}
和“”
防止注入攻击
React DOM 在渲染所有输入内容之前,默认会进行转义。
const title = response.potentiallyMaliciousInput; // 直接使用是安全的: const element = <h1>{title}</h1>;
元素渲染
-
元素是构成React应用的最小砖块
-
将元素渲染为DOM
将元素传入render,能够将其渲染到对应的根DOM节点下
const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root'));
-
更新已渲染元素
React元素是不可变对象,一旦被创建,就无法更改它的子元素或者属性,它表示了某个时刻的UI
// 计时器例子 function time() { const element =( <div> <p>The Time Is {new Date().toLocaleTimeString()}</p> </div> ); ReactDOM.render( element, document.getElementById('clock') ) } // setInterval() 方法会不停地调用函数(按照指定周期),直到 clearInterval() 被调用或窗口被关闭。 // 这里表示每1000ms调用一次tick函数 setInterval(tick, 1000);
虽然每次都新建了整个div,但是只有不要变化的位置被刷新了
组件&Props
-
函数组件
-
class组件
-
父组件可以向传递props
-
提取可复用的组件,组成复杂组件
-
props只读,在任何时候都不能被修改
-
详见Demo
-
生命周期方法
- 装载-componentDidMount()
- 卸载-componentWillUnmount()
- 下图来优化上文的时间,用class组件完成,详细描述了组件实例被创建并插入到DOM的生命周期调用顺序
-
关于State
- 不能直接修改State,使用setState
this.props
和this.state
可能会异步更新,所以不要依赖他们的值来更新下一个状态。- State的更新会被合并,state的成员可以单独被更新,react会将他们
数据向下流动
- 不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。
- state是局部的、封装的。除了拥有和设置他的组件,其他组件都无法访问
- 组件可以将他的state作为props向下传递到子组件中,但子组件也不会知道props的来源是怎样的
事件处理
- 小驼峰命名
- 传入函数作为事件,而不是字符串
- 不能返回false的方式阻止默认行为,而是使用preventDefault
- 回调函数问题见补充
条件渲染
- 简单条件
-
双目运算符
-
true && expression
总是会返回
expression, 而
false && expression总是会返回
false(即跳过后面的内容不渲染)<!--count不为空的时候才会显示<h1>标签--> <div> { count && <h1>Messages: {count}</h1>} </div>
-
-
三目运算符
- condition ? true : false
- 没啥好举例的同上
-
阻止组件渲染
return null
或者return;
来阻止组件渲染(Demo中的落子函数handleClick有体现)
列表和key
- map和key的应用在Demo中体现的挺好的(感觉结合例子更好理解),这里提取几点注意
- 元素的 key 只有放在就近的数组上下文中才有意义。
- key在兄弟节点之间必须唯一
- 提取key基本是必须的,如果提取不了key,可能数据结构就有问题
表单
-
受控组件
在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用
setState()
来更新。渲染表单的 React 组件控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
组合
-
React 有十分强大的组合模式。推荐使用组合而非继承来实现组件间的代码重用。
-
通过JSX嵌套,将任意组件传递给子组件
-
特殊与一般
-
继承
Facebook官方建议:
-
在 Facebook,我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。
-
Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
-
生命周期
-
挂载和卸载-(见’组件&Props‘中有详细过程)
constructor()
: 在 React 组件挂载之前,会调用它的构造函数。getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。render()
: render() 方法是 class 组件中唯一必须实现的方法。componentDidMount()
: 在组件挂载后(插入 DOM 树中)立即调用。
-
更新
getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。shouldComponentUpdate()
:当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。render()
: render() 方法是 class 组件中唯一必须实现的方法。getSnapshotBeforeUpdate()
: 在最近一次渲染输出(提交到 DOM 节点)之前调用。componentDidUpdate()
: 在更新后会被立即调用。
官方Demo
初始文件结构:
https://zh-hans.reactjs.org/
-
传递React参数
在 React 应用中,数据通过 props(propertis)的传递,从父组件流向子组件。
-
添加交互
-
使用数组存储棋盘
选择使用一个长度为9的数组来保存棋盘的状态,又Board来维护,并由Board来将此数组传递给子组件Square
-
到此, Square 组件不再持有 state,因此每次它们被点击的时候,会从父组件接收值,并通知父组件。在 React 术语中,我们把目前的 Square 组件称做“受控组件”。在这种情况下,Board 组件完全控制了 Square 组件。
-
调用了
.slice()
方法创建了squares
数组的一个副本,而不是直接在现有的数组上进行修改。涉及到了React的不变性。不变性,不直接修改而是使用新数据代替旧数据的好处;
- 简化复杂功能
- 跟踪数据的改变
- 确定在React中何时重新渲染
-
函数组件
-
状态提升
当你遇到需要同时获取多个子组件数据,或者两个组件之间需要相互通讯的情况时,需要把子组件的 state 数据提升至其最近的共同的父组件当中保存。之后父组件可以通过 props 将状态数据传递到子组件当中。这样应用当中所有组件的状态数据就能够更方便地同步共享了。
-
-
轮流落子并判断输赢
- 维护一个布尔值判断落子者
- 写一个判断输赢的函数,在落子前进行判断,如果已经决出胜负就不能再落子了
-
保存历史记录
- 使用一个history数组保存每次更新的棋盘
- 提升状态,将棋盘的所有信息(落子者、历史信息、落子处理)从Board移到其父组件Game
-
实现时间回退和前进
- 实现页面显示历史列表
-
棋盘处理
-
落子处理
-
补充:
-
slice()
语法:array.slice(start, end)
参数:
start:从何处开始截取,负数表示倒数第几个
end:从何处结束截取,负数表示倒数第几个
只有一个参数:从第几个截取到最后
slice() 方法可从已有的数组中返回选定的元素。
slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。
注意: slice() 方法不会改变原始数组。
-
map()
在 JavaScript 中,数组拥有
map()
方法,该方法通常用于把某数组映射为另一个数组 -
concat()
concat() 方法用于连接两个或多个数组。
concat() 方法不会更改现有数组,而是返回一个新数组,其中包含已连接数组的值。
补充
-
回调函数要手动绑定this
或者用Demo中的解决方法,用
箭头函数
去解除this带来的歧义但这种写法的问题也有可能带来性能问题,官方说法如下:
此语法问题在于每次渲染时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
-
组件中的html语法
- 与传统html不同的是,class要改写成className