React_实践进阶 - zen0822/interview GitHub Wiki

React 实践进阶

React 组件哲学

[图片]

最小可用 State

State 变量保证最小可以,其他通过逻辑计算获得

好处

  • 减少内存开销
  • 降低组件复杂度
    • State 变量是个会产生副作用的东西
    • State 如果在 useMome 等第二个依赖参数使用不好会产生意想不到的 UI 更新

场景

思考下 a 和 b 是否可以存在一种逻辑关系,从而只声明一个 state 并且使用 useMemo 缓存这个复杂的计算

const Modal = () => {
  const [a] = useState(false)
  const [b] = useState(false)
    
  return (<>{a} {b}</>)
}

单向数据流

React 单向数据流(也叫单向绑定)的思想使得组件模块化,易于快速开发。 自下而上的通过 props 传递数据,让组件在后续的维护上能很容易看清楚 UI 是如何被更新的,以及是在哪里被更新的。

受控组件

概述

通常来说,尽量使用,组件内部的 UI 通过 props 控制

  • 提高组件的灵活性
  • 提高组件的可测试性
什么时候不使用?

以下例子就生动展示了为啥不

Modal = ({display}) => {
    return (
        <div style={{display: display ? '' : 'none'}}>content</div>
    )
}

变量提升 & 局部变量

  • 保证变量最小可用
  • 保证可测试性
  • 提高维护性 ####状态管理
  • 方便使用
  • 对顶层的兄弟组件的数据通信只能采取这个用法
  • 方便的一方面带来的就是不易维护,需要严格代码组织和规范约束
  • 变量到处飞,不好排除数据变化的来源

变量提升

  • 在业务组件的编写时使用,可以解决组件通信问题
  • 推荐在兄弟组件的数据通信采取这个用法从而达到数据回溯的可能性
  • 变量提升意味着需要写更多的组件层级

局部变量

对于实行组件化开发的设计思想必须要严格遵循


                  /\
                 /  \
                状态管理
               /______\
              / 变量提升\
             /__________\
            /  局部变量   \
           /______________\           

语义化

变量方法命名语义化

  1. 众所周知的简写才能使用 ex:tpl、val

  2. 描述变量和方法的主要功能 ex:onInputChange、overideConsole

  3. 取名字要有预判性 ex:colorBlue 格局小了,colorPrimary 就可以指定其他颜色了

组件语义化

  1. 组件名字根据父文件夹的命名来命名,保持简单易懂整洁(KISS 原则)

ex: components | |- base | |- Input |- business | |- SearchInput Pages | |- Goods | |- GoodsList |- GoodsEdit 2. 组件使用

配置语义化

场景

// initData.js

const specificationColumns = [{
    type: 'input',
    defaultValue: 'defalultName',
    min: 3,
}]

// ConfigurationForm
import { specificationColumns } from './initData';

() => (
    <Form {...specificationColumns} />
)

弊端

initData.js 将所有的配置信息放到一块,耦合 了所有的配置信息

Json 的数据结构不好继承也不好分离和抽象,不能共享 react 上下文

分析是否对当前业务有需要,是否是过度设计,不必要的设计

如果未来的确有需要可以提前架构,保留可以扩展的接口,例如:规范化配置格式,即每个组件有自己的 config.comp.json 的配置文件,后续通过工具将每个组件的配置聚合起来。

优化

将配置高度内聚到业务组件里面

// initData.js

const inputBase= [{
    type: 'input',
    min: 3,
}]

// ConfigurationForm
import { inputBase} from './initData';

() => {
    specificationColumns = [{
        ...inputBase,
        defaultValue: 'defalultName',
    }]
    
    return (
        <Form {...specificationColumns} />
    )
}

进一步优化

使用更为直观的 XML 语言代替晦涩难懂的 json 语言来配置,特别是在业务组件,这可以降低代码阅读成本。

() => {    
    const formRef = useRef()
    
    console.log(formRef.getValue())
    
    return (
        <Form ref='form'>
            <Input defaultValue='defalultName' />
        </Form>
    )
}

##设计模式

策略者模式、单例、享元模式这些平时就会在编写 JS 代码的时候使用到,我重点讲几个 React 组件相关的设计模式

高聚低耦

高内聚,模块内容高度聚合,低耦合,模块与模块之间相互独立并能通过接口等形式联系

思考

怎么编写一个 Modal 组件

  1. 分析业务功能
  2. 分析业务基础功能
  3. 分析基础功能
Slide = (
    <Transition type='slide'>
        <div>滑动</div>
    </Transition>
)
    
Pop= (
    <Slide>
        <div>内容</div>
    </Slide>
)

Modal = (
    <Pop dirction='west' postion='center' type='slide'>
        <div>内容</div>
    </Pop>
)
    
Alert = (
    <Modal dirction='west' postion='center' type='slide'>
        <div>alert 内容</div>
    </Modal>
)

Confirm= (
    <Modal dirction='west' postion='center' type='slide'>
        <div>confirm 内容</div>
    </Modal>
)
总结
  • 业务组件
    • 聚合变量
    • 发送请求
    • 执行较多的具有副作用的操作
  • 业务基础组件
    • 有较多的局部变量和较少的变量提升
    • 业务副作用较少
    • 可以服用同一个项目的多个业务中
  • 基础组件
    • 纯 UI 组件,局部变量占据主要位置,具有数据隔离的能力
    • 无业务状态
    • 可跨项目使用

纯函数

概述
  1. 不产生副作用
  • 基础组件里面不请求数据
  • 基础组件里面不改变全局的数据
  1. 输入 1 就输出 1,输入 1+1 就输出 2
  • 通过 props 生成不同类型的组件
  1. 纯 UI
  • 如果是基础组件保证里面没有业务逻辑(接口请求或者全局状态)
  • Js 逻辑使用 hook 封装或者变量提升到业务组件处理
场景
// Modal.jsx
export default function Modal ({type, content}) => {
    if (type === 1) {
       const data = fetch('https://example.com/edit/data')
    }
    
    return (
        <>{content}</>
    )
}

// Business.jsx
export default function Business ({type, content}) => {
    if (type === 1) {
       const data = fetch('https://example.com/data')
    }
    
    return (
        <Modal content={data?content}></Modal>
    )
}

渲染劫持 / 反向代理

#####场景

善用渲染劫持减少组件体积,降低复杂度,降低使用者的学习成本

const Select = ({children}) => {
    return cloneElement(children, {
        onChange(){
            props?.onChange()
        }
    }, children.props.children)
}

DateSelect = (
    <Select>
        <Date />
    </Select>
)

NationalSelect = (
    <Select>
        <National />
    </Select>
)

发布订阅 & 观察者

区别

发布订阅的处理方式跟中介模式类似,是由统一的事件分发调度中心,而观察者模式是独立的形式。

思考

工作站底层架构怎么设计才能让各个组件相互独立又相互联系,达到以不变应万变?

⚠️ **GitHub.com Fallback** ⚠️