React与响应式系统|青训营笔记

59 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的的第10天

一、React的历史与应用

1. React怎么来的?

Facebook的工程师在做大型项目时,,由于他们非常巨大的代码库和庞大的组织,使得MVC很快变得非常复杂,每当需要添加一项新的功能或特性时,系统的复杂度就成级数增长,致使代码变得脆弱和不可预测,结果导致他们的MVC正在土崩瓦解,为了解决上述问题需要“以某种方式组织代码,使其更加可预测”,于是他们提出的Flux和React来实现。

2. React是什么?

A JavaScript library for building user interfaces. (React 是一个用于构建用户界面的JavaScript 库

3. React有什么用?

  • 框架既可以在客户端渲染,也可以在服务端渲染
  • 响应式,状态变更时UI也自动更新
  • 高效性,减少性能消耗
  • 组件化的开发构思,页面实现基本就是组件嵌套组件来实现页面的开发

4. 为什么要学React?

可以实现组件化的高效率开发,可以适应很多交互性高,复杂程度大的开发,便于维护,让代码可读性变高,是开发人员 “上阵杀敌” 的利器!

二、React的设计思路

1、UI编程痛点

如果使用原生JavaScript实现点击对应的型号和颜色卡片,顶部的价格也动态改变,如下图,那要怎么做呢?

image.png 先声明一个currentValue变量,然后可以给那些卡片绑定onClick事件然后写一个Callback,当Callback被触发的时候就更新currentValue变量,最后手动调用DOM接口更新UI。

看起来似乎不是很难?那如果再在底部添加一个动态价格呢?

很明显UI编程有着一些问题:

  1. 状态更新,UI不会自动更新,需要手动地调用DOM 进行更新。
  2. 欠缺基本的代码层面的封装和隔离,代码层面没有组件化。
  3. UI之间的数据依赖关系,需要手动维护,如果依赖链路长,则会遇到 "Callback Hell"。

2、响应式编程

针对这些问题,设计思路就是搞不同于转换式系统(给定输入求输出)响应式系统(监听事件,消息驱动),即让状态和UI自动更新;让代码组件化,可复用可封装;让状态之间有只需声明即可的相互的依赖关系;

所以在敲代码的时候可以有如下规划: image.png

其实这样以及算组件化了,比如: Root 组件内容组件及其原子组件(橱窗和配置面板)顶栏组件组成;为了实现响应式布局,组件内部拥有状态,外部不可见 并且 父组件可以传入状态到组件内部(这样才能控制子组件)

3、问题

1. 当前价格的状态属于谁?

属于Root 因为组件状态是由父向子传递的,我们要在顶栏里显示价格,只能从 Root 传递,但是价格是从下往上传的,我们称其为状态的不合理上升

2. 当前价格如何改变?

可以在父组件Root里写一个价格改变的函数,通过父向子传递变量到底层也就是价格改变的地方,让变量变化来改变Root的函数,从而传递到顶栏组件

3. React是单向数据流还是双向数据流?

单项,只能由父向子传递,子组件可以通过控制父组件的函数变量来改变父组件状态

4. 如何解决状态不合理上升

5. 组件状态改变后如何更新DOM

小结

  1. 组件声明了状态和UI的映射。
  2. 组件有Props/ State两种状态。
  3. “组件"可由其他组件拼装而成。
  4. 大致生命周期由:挂载——状态改变——卸载,构成

三、React(hooks)的写法

先看代码:

// 导入React库
import React, { useState } from ' react' ;
function Example() {
// 声明一个新的状态变量,我们称之为 count
const [ count, setCount] = useState(0) ;
return (
    <div>
    //通过{}调用,直接渲染
    <p>You clicked {count} times</p>
    //点击按钮 count+1
    <button onClick={() => setCount(count + 1) }>
         Click me
    </ button>
    </div>
);

可以看出在声明co0unt变量时,我们不是直接改变count而是通过setCount来进行调用 count+1,因为要实现内部和外部两种状态,所以react要封装一层,避免一些变量直接暴露在外

Hooks写法入门

  • useState传入一个初始值,返回一个状态,和set该状态的函数,用户可以通过调用该函数,来实现状态的修改。
const [value, setValue] = useState(initValue);

setValue(newValue);
setValue((value) => newValue);
  • useEffect副作用函数,在依赖的数据发生变化时执行,可用来监听状态变化,模拟生命周期等
useEffect(() => {
    // 依赖发生变化了...

    return () => {
        // 下一次useEffect运行前被执行
    };
}, [依赖的状态]);

useEffect可以在同一个函数组件中被调用多次, 第一个参数是函数,返回值函数在下一次执行前执行, 第二个参数是数组,可以指定绑定的关联数据,不传每次更新都会执行,为空数组则只执行一次

useEffect 里面使用到的state的值, 固定在了useEffect内部,不会被改变,除非useEffect刷新,重新固定state的值。在依赖中添加count即可触发刷新

 const [count, setCount] = useState(0)
useEffect(() => {
    console.log('use effect...',count)
    const timer = setInterval(() => {
        console.log('timer...count:', count) // 一直是
        setCount(count + 1)
    }, 1000)
    return ()=> clearInterval(timer)
},[])

四、React的实现

  1. 如何更新DOM

Virtual DOM(虚拟DOM)是一种用于和真实DOM同步,而在JS内存中维护的一个对象,它具有和D0M类似的树状结构,并和DOM可以建立一 一 对应的关系。

image.png

  1. Diff

Diff算法用于比较原始虚拟DOM和新的虚拟DOM的区别,即两个js对象该如何比对。diff算法是同级比较,假设第一层两个虚拟DOM节点不一致,就不会往下比了,就会将原始页面虚拟DOM全部删除掉,然后用新的虚拟DOM进行全部的替换,虽然这有可能有一些性能的浪费,但是由于同层比对的算法性能很高,因此又弥补了性能的损耗。

image.png

diff方法
不同类型的元素替换
同类型的DOM元素更新
同类型的组件元素递归

React的状态管理库

当某个状态被整个app所应用, 就适合放在状态管理库里 image.png 缺点:会降低组件复用性

类型: image.png 状态机: 当前状态,收到外部事件,迁移到下一个状态

总结

这次讲了react与响应式系统,对react有一些深刻的认识,下次尝试在开发中使用react响应式布局加深理解

如果有错误欢迎指出