React初识&&JSX&&函数式组件

学习react的笔记


react 项目 npm i 安装依赖报错

1
创建react工程,提示错误The engine "node" is incompatible with this module. Expected version "^8.10.0 || ^10.13.0 || >=11.10.1". Got "10.5.0",导致一直没有创建成功

node版本不一致

  1. 升级node
1
2
3
4
5
6
7
8
// 第一步:使用npm安装n模块,n模块是专门用来管理nodejs版本的,名字就叫n执行命令
npm install -g n // (注意不带sudo,我按照网上带sudo提示错误)
// 出现此日志表示添加n包成功
// 第二步:使用n模块升级node
// 第一种是升级到最新版本
sudo n latest
// 第二种是升级到稳定版本(建议用稳定版本)
sudo n stable
  1. 如果更新了node或许或导致其他工程有问题,不能轻易更新,可以输入命令行,也可以恢复正常
1
yarn config set ignore-engines true

采用了第二种安装,别人的项目拉取node版本不同也能解决

参考

项目初始–vite配置项目路径别名

参考

核心思想

Vue 早期定位是尽可能的降低前端开发的门槛(这跟 Vue 作者是独立开发者也有关系)。所以 Vue 推崇灵活易用(渐进式开发体验),数据可变,双向数据绑定(依赖收集)。
React 早期口号是 Rethinking Best Practices(重新思考最佳实践)。背靠大公司 Facebook 的 React,从开始起就不缺关注和用户,而且 React 想要做的是用更好的方式去颠覆前端开发方式(事实上跟早期 jquery 称霸前端,的确是颠覆了)。所以 React 推崇函数式编程(纯组件),数据不可变以及单向数据流。函数式编程最大的好处是其稳定性(无副作用)和可测试性(输入相同,输出一定相同),所以通常大家说的 React 适合大型应用,根本原因还是在于其函数式编程。
由于两者核心思想的不同,所以导致 Vue 和 React 许多外在表现不同(从开发层面看)。

掘金

总体感受–一些区别

  • vue 更简单,更方便,熟悉了 api 以后,实现某些简单功能更快。react 写法更偏向于原生 JS,Class 的写法不是很舒服,个人更喜欢 hooks。
  • 熟悉了 hooks 以后,写起来很自由,不用关心 vue 中固定的 options api
  • react 做中后台优势更大,有大厂加持,生态更好,组件库功能也更多,解决方案也更多
  • vue2.x 对 typescript 不太友好,react + typescript 更加舒适,两者写起来风格差距较大。
  • react JSX 写起来还是不够熟练,onClick、style、className 等等,没有 v-if,v-for,All in JS。Vue 则推崇 html、js、css 分离的写法,当然 vue 也可以写 JSX
  • vue 的 prop 必须在子组件 props 字段里声明。React 的 prop 不强制声明,直接使用,如果用 TS 的话还是要声明的

React 是什么?

React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。

JSX基础

  1. JSX介绍
    概念:JSX是 JavaScript XML(HTML)的缩写,表示在 JS 代码中书写 HTML 结构
    作用:在React中创建HTML结构(页面UI结构)

优势

  1. 采用类似于HTML的语法,降低学习成本,会HTML就会JSX
  2. 充分利用JS自身的可编程能力创建HTML结构
    注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法

2. JSX中使用js表达式

语法
{ JS 表达式 }

1
2
3
const name = '柴柴'

<h1>你好,我叫{name}</h1> // <h1>你好,我叫柴柴</h1>

可以使用的表达式

  1. 字符串、数值、布尔值、null、undefined、object( [] / {} )
  2. 1 + 2、’abc’.split(‘’)、[‘a’, ‘b’].join(‘-‘)
  3. fn()
    特别注意

if 语句/ switch-case 语句/ 变量声明语句,这些叫做语句,不是表达式,不能出现在 {} 中!!

3. JSX列表渲染

页面的构建离不开重复的列表结构,比如歌曲列表,商品列表等,我们知道vue中用的是v-for,react这边如何实现呢?

实现:使用数组的map 方法

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 来个列表
const songs = [
{ id: 1, name: '痴心绝对' },
{ id: 2, name: '像我这样的人' },
{ id: 3, name: '南山南' }
]
function App() {
return (
<div className="App">
<ul>
{
songs.map(item => <li>{item.name}</li>)
}
</ul>
</div>
)
}
export default App

注意点:需要为遍历项添加 key 属性

  1. key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用
  2. key 在当前列表中要唯一的字符串或者数值(String/Number)
  3. 如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值
  4. 如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值

4. JSX条件渲染

作用:根据是否满足条件生成HTML结构,比如Loading效果

实现:可以使用 三元运算符 或 逻辑与(&&)运算符

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 来个布尔值
const flag = true
function App() {
return (
<div className="App">
{/*条件渲染字符串*/}
{flag ? 'react真有趣' : 'vue真有趣'}
{/*条件渲染标签/组件*/}
{flag ? <span>this is span</span> : null}
</div>
)
}
export default App

5. JSX样式处理

目标任务: 能够在JSX中实现css样式处理

行内样式 - style

1
2
3
4
5
6
7
8
9
function App() {
return (
<div className="App">
<div style={{ color: 'red' }}>this is a div</div>
</div>
)
}

export default App

行内样式 - style - 更优写法

1
2
3
4
5
6
7
8
9
10
11
12
13
const styleObj = {
color:red
}

function App() {
return (
<div className="App">
<div style={ styleObj }>this is a div</div>
</div>
)
}

export default App

类名 - className(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.css

.title {
font-size: 30px;
color: blue;
}
app.js

import './app.css'

function App() {
return (
<div className="App">
<div className='title'>this is a div</div>
</div>
)
}
export default App

类名 - className - 动态类名控制

1
2
3
4
5
6
7
8
9
10
import './app.css'
const showTitle = true
function App() {
return (
<div className="App">
<div className={ showTitle ? 'title' : ''}>this is a div</div>
</div>
)
}
export default App

官网
参考文档

总结

  1. 文件名可以是jsx 或者js,不影响文件中的代码
  2. 组件名称必须大写
  3. Js中出现 ()代表其中想要写的html
  4. HTML中出现 {} 代表其中想要写的js
JSX注意事项

JSX必须有一个根节点,如果没有根节点,可以使用<></>(幽灵节点)替代

所有标签必须形成闭合,成对闭合或者自闭合都可以

JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class -> className for -> htmlFor

JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现

React组件基础

函数式组件

1.函数式组件没有声明周期
2.函数式组件没有this
3.函数式组件没有state状态
函数组件

使用 JS 的函数(或箭头函数)创建的组件,就叫做函数组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
组件定义与渲染

// 定义函数组件
function HelloFn () {
return <div>这是我的第一个函数组件!</div>
}

// 定义类组件
function App () {
return (
<div className="App">
{/* 渲染函数组件 */}
<HelloFn />
<HelloFn></HelloFn>
</div>
)
}
export default App

约定说明

  1. 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
  2. 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null
  3. 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
  4. 使用函数名称作为组件标签名称,可以成对出现也可以自闭合

类组件

使用 ES6 的 class 创建的组件,叫做类(class)组件

组件定义与渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 引入React
import React from 'react'

// 定义类组件
class HelloC extends React.Component {
render () {
return <div>这是我的第一个类组件!</div>
}
}

function App () {
return (
<div className="App">
{/*渲染类组件*/}
<HelloC />
<HelloC></HelloC>
</div>
)
}
export default App

约定说明

  1. 类名称也必须以大写字母开头
  2. 类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性
  3. 类组件必须提供 render 方法render 方法必须有返回值,表示该组件的 UI 结构

事件绑定

  1. 如何绑定事件
    语法
1
on + 事件名称 = { 事件处理程序 } ,比如:<div onClick={()=>{}}></div>

注意点
react事件采用驼峰命名法,比如:onMouseEnter、onFocus
样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 函数组件
function HelloFn () {
// 定义事件回调函数
const clickHandler = () => {
console.log('事件被触发了')
}
return (
// 绑定事件
<button onClick={clickHandler}>click me!</button>
)
}
// 类组件
class HelloC extends React.Component {
// 定义事件回调函数
clickHandler = () => {
console.log('事件被触发了')
}
render () {
return (
// 绑定事件
<button onClick={this.clickHandler}>click me!</button>
)
}
}
  1. 获取事件对象
    通过事件处理程序的参数获取事件对象e
1
2
3
4
5
6
7
8
9
10
11
12
// 函数组件
function HelloFn () {
// 定义事件回调函数
const clickHandler = (e) => {
e.preventDefault()
console.log('事件被触发了', e)
}
return (
// 绑定事件
<a href="http://www.baidu.com/" onClick={clickHandler}>百度</a>
)
}

组件状态

目标任务: 能够为组件添加状态和修改状态的值

一个前提:在react hook出来之前,函数式组件是没有自己的状态的,所以我们统一通过类组件来讲解
组件状态

  1. 初始化状态
    通过class的实例属性state来初始化

state的值是一个对象结构,表示一个组件可以有多个数据状态

1
2
3
4
5
6
7
8
9
class Counter extends React.Component {
// 初始化状态
state = {
count: 0
}
render() {
return <button>计数器</button>
}
}
  1. 读取状态
    通过this.state来获取状态
1
2
3
4
5
6
7
8
9
10
class Counter extends React.Component {
// 初始化状态
state = {
count: 0
}
render() {
// 读取状态
return <button>计数器{this.state.count}</button>
}
}
  1. 修改状态
  • 语法

this.setState({ 要修改的部分数据 })

  • setState方法作用
    修改state中的数据状态
    更新UI

  • 思想
    ​ 数据驱动视图,也就是只要修改数据状态,那么页面就会自动刷新,无需手动操作dom

  • 注意事项
    不要直接修改state中的值,必须通过setState方法进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Counter extends React.Component {
// 定义数据
state = {
count: 0
}
// 定义修改数据的方法
setCount = () => {
this.setState({
count: this.state.count + 1
})
}
// 使用数据 并绑定事件
render () {
return <button onClick={this.setCount}>{this.state.count}</button>
}
}

this

this问题说明

这里我们作为了解内容,随着js标准的发展,主流的写法已经变成了class fields,无需考虑太多this问题

React的状态不可变

概念:不要直接修改状态的值,而是基于当前状态创建新的状态值

  1. 错误的直接修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
state = {
count : 0,
list: [1,2,3],
person: {
name:'jack',
age:18
}
}
// 直接修改简单类型Number
this.state.count++
++this.state.count
this.state.count += 1
this.state.count = 1

// 直接修改数组
this.state.list.push(123)
this.state.list.spice(1,1)

// 直接修改对象
this.state.person.name = 'rose'
  1. 基于当前状态创建新值
1
2
3
4
5
6
7
8
9
this.setState({
count: this.state.count + 1
list: [...this.state.list, 4],
person: {
...this.state.person,
// 覆盖原来的属性 就可以达到修改对象中属性的目的
name: 'rose'
}
})

表单处理

使用React处理表单元素,一般有俩种方式:

受控组件 (推荐使用)
非受控组件 (了解)

1. 受控表单组件

什么是受控组件? input框自己的状态被React组件状态控制

React组件的状态的地方是在state中,input表单元素也有自己的状态是在value中,React将state与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,从而保证单一数据源特性

实现步骤

以获取文本框的值为例,受控组件的使用步骤如下:

  1. 在组件的state中声明一个组件的状态数据
  2. 将状态数据设置为input标签元素的value属性的值
  3. 为input添加change事件,在事件处理程序中,通过事件对象e获取到当前文本框的值(即用户当前输入的值)
  4. 调用setState方法,将文本框的值作为state状态的最新值

代码落地

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React from 'react'

class InputComponent extends React.Component {
// 声明组件状态
state = {
message: 'this is message',
}
// 声明事件回调函数
changeHandler = (e) => {
this.setState({ message: e.target.value })
}
render () {
return (
<div>
{/*绑定value 绑定事件*/}
<input value={this.state.message} onChange={this.changeHandler} />
</div>
)
}
}

function App () {
return (
<div className="App">
<InputComponent />
</div>
)
}
export default App
2. 非受控表单组件

什么是非受控组件?

非受控组件就是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值

实现步骤

  1. 导入createRef 函数
  2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中
  3. 为input添加ref属性,值为msgRef
  4. 在按钮的事件处理程序中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值
    代码落地
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React, { createRef } from 'react'

class InputComponent extends React.Component {
// 使用createRef产生一个存放dom的对象容器
msgRef = createRef()

changeHandler = () => {
console.log(this.msgRef.current.value)
}

render() {
return (
<div>
{/*ref绑定 获取真实dom*/}
<input ref={this.msgRef} />
<button onClick={this.changeHandler}>click</button>
</div>
)
}
}
function App () {
return (
<div className="App">
<InputComponent />
</div>
)
}
export default App

参考文档

gitee
官方文档


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!