普通视图

发现新文章,点击刷新页面。
昨天 — 2026年4月26日首页

古法编程: React思维模型快速建立

作者 Pkmer
2026年4月26日 16:54

d179110117a86708aa993eb40f13a3c4_720.jpg

使用React的方法论与思想,方便我这个古法编程者Vibe Coding的时候更能游刃有余。

从原始操作DOM API,编写每个操作步骤的命令式,到使用React负责处理DOM的细节,程序员只描述最终结果的声明式编程,最终实现DOM响应数据的变化而自发做出更改的响应式。其中声明式编程,以现代的我的视角了看很像Vibe Coding,我们只需要描述需求即可。

一层一层的抽象出来。命令式->声明式->Vibe Coding(氛围编程)

React思维模型

HTML上凿洞,动态数据露脸

就像旅游景区镂空的拍照墙,我们只需要露个头,一个完美的姿势拍照就好了。

import { useState } from "react";

export default function App() {
  let [who, setWho] = useState("Pkmer");
  return (
    <div className="icons">💪{who}👊</div>
  );
}

JSX是伪装成HTML的JavaScript代码

React开发工具将JSX标签自动转化为相应的JavaScript代码

function App(){
    let [username] = userState(() => "Pkmer")
    return <div className="container">
        <text className="name">💪{username}👊</text>
        <button />
    <div>
}

转化成的JavaScript

import {jsx as _jsx} from "react"

function App(){
    let [username] = userState(() => "Pkmer")
    return _jsx("div",{
        className: "container",
        children: [
            _jsx("text",{className: "name",children: ["💪",username,"👊"]}),
            _jsx("button")
        ]
    })
}

一次组件渲染,一页手翻书

组件每次渲染的时候,都会重新执行一次

function ComponentXxx(){
    console.log("run run run...")
    return // ...jsx...
}

不可变特性(快照)

组件的state数据是不可变的,每一次都只是一个快照,要想更新数据,需要推倒重建。一个组件就像一座大厦一样,就算只给窗户换一种颜色,那么这个大厦就得重建(重新渲染)

下面的代码第一种错误的示范中:在React看来house还是原来的那个house,同一个引用,React会不会重新渲染。第二种正确的方式: house已经不再是原来的house了,尽管大部分内容是一样的,但是它已经是一个新的house.

const [house,setHouse] = useState({windowColor: "蓝色",floors: 2});

// ❌️这样修改不会有效果,house只是快照
house.windowColor = "白色"
setHouse(house)  


// 正确✅️
const newHouse = [...hourse,windowColor: "白色"]
setHouse(newHouse)

可以这样理解:React为了性能考虑,没有进行深层比较,这里只是浅层的比较,发现house的引用变了,才更新。

组件的动态组合方式:children

相比搭积木一样一个一个组件固定的组合方式,children这个特殊的属性,提供了动态组件组合方式

function Dialog({children}){
    return <div>
        <div>{children}</div>
    </div>
}

// 使用
function App(){
    return <Dialog>
        <Heading>I Love Coding</Heading>
        <Slogon>此刻我在深圳图书馆-北馆,充电学习</Slogon>
    </Dialog>
}

传送工程师的接力:单向数据流

props层层传递

数据所有者要想将数据传递给消费者,需要进行层层传递,尽管中间传递者并不消费这个数据

image.png

context电梯,按需取货

在提供数据的楼层(上层)将包裹(数据),放入电梯(context)。电梯往下走,下面的哪个楼层需要这个包裹(数据),自己在对应的楼层打开电梯取走。

const ThemeContext = React.createContext("light")

// 上层数据所有者,提供数据,放入电梯
function Home(){
    const [theme,setTheme] = useState("light")
    
    return <ThemeContext.Provider value={theme}>
        <Page />
    </ThemeContext.Provider>
}

App->Page->Header->Logo,中间层Page,Header都不需要数据,只有Logo需要。

function Page(){
    return <div>
        <Header />
        <Content />
        <Footer />
    </div>
}

function Header(){
    return <div>
        <Logo />
        <Title />
    </div>
}

function Logo(){
    // 在我这层,打开电梯取出包裹(数据)
    const theme = useContext(ThemContext)
    
    return theme === "dark" ? <DarkLogo /> : <LightLogo />
}

便携式虫洞

由于数据是单向传递的,如果子组件要想改变数据,需要数据提供层进行修改。想要修改上层数据,上层提供则需要将权力下放。而这个下放的过程,就像虫洞一样,子组件员工可以将手伸向上层老板的办公室,直接进行签合同。

function BossOffice() {
  const [contract, setContract] = useState('初始合同');

  // 老板下放修改权限的方法
  const signContract = (newContract) => {
    setContract(newContract);
  };

  return (
    <div>
      <h1>老板办公室 - 当前合同: {contract}</h1>
      {/* 将修改权限通过 props 下放给子组件 */}
      <EmployeePortal onSignContract={signContract} />
    </div>
  );
}

function EmployeePortal({ onSignContract }) {
  const handleSign = () => {
    // 子组件员工直接调用上层传来的方法,就像穿过虫洞伸手改数据
    onSignContract('员工新签的合同');
  };

  return (
    <div>
      <h2>员工虫洞通道</h2>
      <button onClick={handleSign}>伸向老板办公室签合同</button>
    </div>
  );
}

Hook勾子将数据放入React大海又勾回来

Hook将函数组件内的数据保存到外部环境,以备下次渲染所用。

  • 保存只读数据: useMemo(保存函数的返回值),useCallback(保存的是回调函数本身)
  • 保存可变数据,更改时触发渲染: useState和useReducer(更底层)
  • 保存可变数据,更改时不触发渲染: useRef

useEffect与生命周期回调方法

useEffect完全可以代替类组件中的三个生命周期回调方法

class Xxx extends Component{
    // 挂载以后运行
    componentDidMount{}
    
    // 每次更新以后运行
    componentDidUpdate(prevProps){}
    
    // 将要卸载前运行
    componentWillUnmount(){}

}

useEffect对应的行为方式

useEffect(() => {
    // 数组参数为空,只在组件第一次渲染时调用
},[])

useEffect(() => {
    // 当数组中的元素变化更新时会执行
},[要变化的值,或者函数]) // 要诚实的告诉哪些值会变。


useEffect(() => {
    // 省略数组。将在组件每次渲染后运行此处的代码
})

useEffect的真正职责:管理组件副作用

药物的副作用并不是药物的目的,当然是越少越好。而程序里的副作用却是我们有意而为,是程序的功能之一

所谓副作用,是函数组件与其周边环境发生了交互的额外任务,比如操作window对象,访问网络请求后端api,读取本地文件等,这些作用都超出了当前函数组件的范围。函数组件关心的是state和props

function Boat(props){
    useEffect(() => {
        const listener = ...
        window.addEventListener('keydown',listener)
        ...
        return () => {
            window.removeListener('keydown',listener)
        }
    },[])

}
❌
❌