Skip to main content

关于React的思考

声明式开发

我们在写jquery代码的时候都是直接操作dom,直接操作dom的这种编程方式我们叫做命令式编程,也就是我们创建一个页面的时候,要一点点告诉Dom,你要怎么去挂载,你要怎么去做

声明式开发也就是react这种开发模式

你可以这么理解:假如我们去盖一个楼

jquery模式: 你要告诉施工队每一步该怎么做 它会涉及到大量的dom操作

react模式: 你只要提供楼的数据信息(图纸),react 会自动帮你构建这座大楼. 使用react会避免大量使用dom操作

可以与其他框架共存

其实react可以与其他框架(如angular,vue,jquery)完美共存,那么是怎么做到的呢?

我们来分析页面的入口:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter>
<App />
</BrowserRouter>
)

结合react的入口文件(把页面根组件App挂载到了id='root'的节点上),我们知道--其实只有<div id="root"></div>会受react的影响, 你可以在其他dom节点中做任何你想要的操作, 比如像下面这样:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<div id="vue-test">do sth with vue</div>
<div id="angular-test">do sth with angular</div>
<div id="jquery-test">do sth with jquery</div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

组件化

react开发是组件式的开发方式

怎么辨别: 不同于一般的h5标签,组件的首字母必须大写

组件通信:

  • 父=>子: 父组件直接将属性传给子组件,子组件就可以通过props拿到该属性
  • 子=>父: 父组件将方法传给子组件,子组件就可以通过props拿到该方法,从而间接操作父组件

单向数据流

父组件可以向子组件传递任何数据,但是子组件一定不要去改变这些数据, 否则会报错

我们来看下面这个demo:

import React, { memo } from 'react'
import { useState } from 'react'

const Father = memo(() => {
const [fruit, setFruit] = useState(['apple','pear'])
return (
<div>
<h3>Father</h3>
<Child fruit={fruit}/>
</div>
)
})

const Child = memo((props) => {
return (
<div>child</div>
)
})

export default Father

可以看到页面可以正常显示

但是当我们在子组件中修改父组件的数据后,发现页面报错了 -- 给常量(只读属性)赋值

import React, { memo } from 'react'
import { useState } from 'react'

const Father = memo(() => {
const [fruit, setFruit] = useState(['apple','pear'])
return (
<div>
<h3>Father</h3>
<Child fruit={fruit}/>
</div>
)
})

const Child = memo((props) => {
const { fruit } = props
fruit=[]
return (
<div>child</div>
)
})

export default Father

react为什么要使用单向数据流?

目的是使我们开发起来更方便,不容易遇到坑

假设我们的父组件有5个子组件:

const Father = memo(() => {
const [fruit, setFruit] = useState(['apple','pear'])
return (
<div>
<h3>Father</h3>
<ChildA fruit={fruit}/>
<ChildB fruit={fruit}/>
<ChildC fruit={fruit}/>
<ChildD fruit={fruit}/>
<ChildE fruit={fruit}/>
</div>
)
})

假设react没有单向数据流,如果你在<ChildE/>里更改了fruit的数据,那么<ChildA/>-<ChildE/>都会受影响,此时一旦页面发生bug是很难定位的 - 到底是哪个组件改了fruit而使页面发生故障的呢?你还要一个组件 一个组件去看 这样调试代码是很麻烦的

所以react使用单向数据流:只允许父组件向子组件传递数据 但是绝不允许子组件直接修改父组件的数据

如果子组件就是想修改父组件的数据,怎么办呢?前面讲了,父组件给子组件传递方法,子组件调用父组件方法间接修改父组件的数据。其实仔细想想 “子组件调用父组件方法” - 本质上还是父组件在操作数据

一言以蔽之:只能单向传递 不能反向修改

视图层框架

为什么react不把自己定义成一个大型框架,而只是一个视图层框架呢?

我们知道父子组件通信可以通过属性传值,但是如果我们的项目很大,有很多数据要共享,这时你会发现实际中的项目并不像<TodoList/>那么简单,如下图:

假如紫色组件要与右上角的组件通信,可就麻烦了: 首先需要找共同的父组件,也就是图中的根组件,然后一层层传递,可以看到相当的恶心人 - 本来只是简单的2个组件通信,结果沿途的组件都“被牵扯进来”

如果项目大部分事情都是在做数据传递,很多代码都是冗余的,大型项目的话,开发者可能会疯掉

所以react只是把自己定义成一个视图层框架,我并不会帮你解决所有事情,我只解决ui的渲染问题 如果想开发大型项目,必须搭配数据层框架

函数式编程

可以用函数来定义组件,不仅简化了代码,还方便了测试(直接调用function看是否符合预期)