React——基础

文章目录

React 基础

一、基础概念

声明式 UI,ui=render(state)

  • 声明式,定义状态,修改状态,将状态编写入 jsx
  • 组件化,(区块化,原子应用)
  • 虚拟 DOM,但是可以这样说,这个方案放在当下已经不是一个了不起的方案,FragmentDocument,没有虚拟 dom 的代表作 solidjs
  • 单向数据流。(Vue -> MVVM)
  • JSX,对开发者友好,但加重编译器的负担

二、组件化

jsx 复制代码
// 类组件
class Welcome extends React.Component {
  render() {
    return <div>123</div>;
  }
}

// 函数组件
function Welcome() {
  return <div>123</div>;
}

三、状态

jsx 复制代码
// 类组件
class Welcome extends React.Component {
  constructor() {
    this.state = {
      name: "heyi",
    };
  }
  render() {
    return <div>123</div>;
  }
}

// 函数组件
function Welcome() {
  const [count, setCount] = useState(0);
  return <div>123</div>;
}

四、属性

jsx 复制代码
function Welcome(props) {
  return <div>{props.name}</div>;
}

五、项目初始化

项目初始化其实就是我们平常所说的工程化

在初始化时,我们需要考虑这个项目用什么语言(js、ts),打包(webpack、vite),编译(babel、swc、rspack、esbuild),技术栈 React

接下来如果是企业级项目,除了技术层内容考虑以外,还需要重点关注:流程化、自动化、规范化等。

  • 使用 vite 自己从零搭建

    • 1、安装依赖

      json 复制代码
      {
        "dependencies": {
          "react": "^19.1.0",
          "react-dom": "^19.1.0"
        },
        "devDependencies": {
          "@vitejs/plugin-react": "^4.6.0",
          "vite": "^7.0.2"
        }
      }
    • 2、编写 vite.config.js 文件

      js 复制代码
      import { defineConfig } from "vite";
      import react from "@vitejs/plugin-react";
      
      export default defineConfig({
        plugins: [react()],
        server: {
          port: 3000,
        },
      });
    • 3、在项目根目录创建 index.html 文件

      html 复制代码
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>Document</title>
        </head>
        <body>
          <div id="root"></div>
          <script type="module" src="/src/main.js"></script>
        </body>
      </html>
    • 4、创建 src/main.js

      js 复制代码
      import { StrictMode } from 'react'
      import { createRoot } from 'react-dom/client'
      import './index.css'
      import App from './App.tsx'
      
      createRoot(document.getElementById('root')!).render(
        <StrictMode>
          <App />
        </StrictMode>,
      )
    • 5、创建 App.tsx

      tsx 复制代码
      import { useState } from "react";
      function App() {
        return <div>123</div>;
      }
      
      export default App;
    • 6、编写启动脚本

      json 复制代码
        "scripts": {
        "dev": "vite",
        "build": "vite build",
        "preview": "vite preview",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
    • 7、启动项目

      bash 复制代码
        pnpm run dev
  • 使用脚手架,不用关心初始化的细节

    • vite

      bash 复制代码
      pnpm create vite react-vite-cli-demo --template react-ts
    • create-react-app

      bash 复制代码
      npx create-react-app react-cra-demo --template typescript

六、jsx

jsx 全称是:javaScript and xml,在 javascript 代码中编写 html 代码是一种规范。因为规范是为编辑器设计的。

jsx 复制代码
const element = <h1>Hello world!</h1>;

jsx 通过编译器进行转化,babel(@babel/preset-react、plugin-transform-react-jsx)

通过 jsx 转化后,代码就变成 js 引擎可以执行的代码了。

js 复制代码
import { jsx as _jsx } from "react/jsx-runtime";
var element = _jsx("h1", {
  children: "Hello world!",
});

再在 react 运行时,通过 react 定义的 jsx 创建出 ReactElement

js 复制代码
return ReactElement(
  type,
  key,
  ref,
  undefined,
  undefined,
  getOwner(),
  props,
  undefined,
  undefined
);

七、创建 React 组件的两种方式

函数式组件(推荐)

tsx 复制代码
import React, { useState } from "react";

interface FCCBasicProps {
  name: string;
}

export const FCCBasic: React.FC<FCCBasicProps> = (props: FCCBasicProps) => {
  const [count, setCount] = useState(0);
  return (
    <div
      onClick={() => {
        setCount(count + 1);
      }}
    >
      Hello, Functional Component Basic! count: {count}
    </div>
  );
};

类组件(不推荐)

tsx 复制代码
import React from "react";
export interface BasicProps {
  name: string;
}
export interface BasicState {
  count: number;
}

export class Basic extends React.Component<BasicProps, BasicState> {
  constructor(props: BasicProps) {
    super(props);
    this.state = {
      count: 0,
    };
  }
  render() {
    console.log(this.props.name);
    return (
      <div
        onClick={() => {
          this.setState({
            count: this.state.count + 1,
          });
        }}
      >
        Hello, Class Component Basic!{this.state.count}
      </div>
    );
  }
}

八、常用的 hooks

  1. useState: 用于状态管理
  2. useEffect: 用于数据获取、定时器、订阅、动画
  3. useContext: 用于数据共享
  4. useReducer: 状态管理
  5. useMemo: 缓存数据
  6. useRef: 获取 DOM 元素
  7. useCallback: 缓存函数

1、useState:用来修改状态值

ts 复制代码
import React, { useState } from "react";

interface UseStateDemoProps {}

export const UseStateDemo: React.FC<UseStateDemoProps> = () => {
  const [count, setCount] = useState(0);
  return (
    <div onClick={() => setCount((c) => c + 1)}>
      Hello, Functional Component UseStateDemo!{count}
    </div>
  );
};

2、useReducer:用来修改状态值,比 useState 更适合处理复杂逻辑

ts 复制代码
import React, { useReducer } from "react";

type INCREMENT = "increment";
type DECREMENT = "decrement";

type Action = {
  type: INCREMENT | DECREMENT;
};
type State = {
  count: number;
};

interface ReducerProps {}

const reducer = (state: State, action: Action) => {
  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + 1 };
    case "decrement":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

export const UseReducerDemo: React.FC<ReducerProps> = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <div onClick={() => dispatch({ type: "increment" })}>
      Hello,Functional Component useReducerDemo{state.count}
    </div>
  );
};

3、useContext:传递数据

ts 复制代码
import React, { useContext } from "react";

interface UseContextProps {}

// 一定要结合 Context
const UserContext = React.createContext({ name: "default" });

// 提供者和消费者

export const UseContextDemo: React.FC<UseContextProps> = () => {
  return (
    <UserContext.Provider value={{ name: "jack" }}>
      <Child />
    </UserContext.Provider>
  );
};

// 老版本写法
// const Child = () => {
//   return (
//     <UserContext.Consumer>
//       {(value) => <div>{value.name}</div>}
//     </UserContext.Consumer>
//   );
// };

// 新版本基于 hooks 的写法
const Child = () => {
  const user = useContext(UserContext);
  return <div>{user.name}</div>;
};

4、useMemo: 缓存计算结果

ts 复制代码
import React, { useMemo, useState } from "react";

interface UseMemoProps {}

export const UseMemoDemo: React.FC<UseMemoProps> = () => {
  const [count, setCount] = useState(0);
  const [price, setPrice] = useState(0);
  // 这个时候我想计算一个 Count 的二倍数,并且我们假设这个计算非常复杂,只在count更新时重新计算
  const doubleCount = useMemo(() => {
    console.log("只有count改变时才计算...");
    return count * 2;
  }, [count]);
  return (
    <div
      onClick={() => {
        if (price % 2 === 0) {
          setCount((c) => c + 1);
        }
        setPrice((p) => p + 1);
      }}
    >
      {doubleCount}----{price}
    </div>
  );
};

5、useCallback:缓存函数

ts 复制代码
import React, { useCallback, useState } from "react";

interface UseCallbackProps {}

export const UseCallbackDemo: React.FC<UseCallbackProps> = () => {
  const [count, setCount] = useState(0);
  const [price, setPrice] = useState(0);
  // 只有count发生变化后才重新创建函数
  const handleClick = useCallback(() => {
    console.log("~ handleClick ~ count: ", count);
  }, [count]);
  return (
    <div onClick={handleClick}>
      UseCallbackDemo,count:{count}-------{price}
      <div
        onClick={() => {
          setCount((c) => c + 1);
        }}
      >
        + count
      </div>
      <div
        onClick={() => {
          setPrice((c) => c + 1);
        }}
      >
        + price
      </div>
    </div>
  );
};

6、useEffect:副作用函数

ts 复制代码
import React, { useEffect, useState } from "react";

export const UseEffectDemo: React.FC = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("useEffect");
    document.title = `you clicked ${count} times`;

    // 返回一个方法用来清除副作用
    return () => {
      console.log("clean up");
    };
  }, [count]);
  return (
    <div>
      <p>UseEffectDemo</p>
      <button onClick={() => setCount((c) => c + 1)}>click me</button>
    </div>
  );
};

7、useId:设置id

ts 复制代码
import React, { useId } from "react";

export const UseIdDemo = () => {
  const id = useId();
  return (
    <div>
      <label htmlFor={id}>Name</label>
      <input id={id} type="text" />
    </div>
  );
};

8、useImperativeHandle:用来给子组件绑定一些方法,让父组件可以直接调用子组件的方法

ts 复制代码
import React, { forwardRef, useImperativeHandle, useRef } from "react";

interface FancyInputHandle {
  focus: () => void;
  select: () => void;
}

// 但如果你希望父组件可以调用子组件中定义的方法(而不是直接访问 DOM),就需要使用 useImperativeHandle 来定制 ref 的内容。
const FancyInput = forwardRef<FancyInputHandle>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current?.focus();
    },
    select: () => {
      inputRef.current?.select();
    },
  }));
  return <input ref={inputRef} />;
});

export function UseImperativeHandleDemo() {
  const inputRef = useRef<HTMLInputElement>(null);
  return (
    <div>
      <FancyInput ref={inputRef} />
      <button
        onClick={() => {
          inputRef.current?.focus();
        }}
      >
        Focus the input
      </button>
    </div>
  );
}

9、useTransition:主要用于在组件中实现非阻塞式更新(异步 UI 更新)

ts 复制代码
import React, { useState, useTransition } from "react";

export function UseTransitionDemo() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);
  const handleClick = () => {
    startTransition(() => {
      setCount((c) => c + 1);
    });
  };
  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      {isPending ? "Loading..." : <p>{count}</p>}
    </div>
  );
}

10、useDeferedValue:用于将状态更新延迟到更紧急的更新之后执行

ts 复制代码
import React, { useDeferredValue } from "react";

export const UseDeferedValueDemo: React.FC = () => {
  const [text, setText] = React.useState("");
  const deferredText = useDeferredValue(text);

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <div>{deferredText}</div>
    </div>
  );
};

12、useSyncExternalStore:是 React 官方推荐的方式,用于安全、高效地接入外部状态源,尤其适用于构建状态管理库或需要响应外部数据变化的场景

ts 复制代码
import React, { useSyncExternalStore } from "react";

function useWindowWidth() {
  return useSyncExternalStore(
    (cb) => {
      window.addEventListener("resize", cb);
      return () => window.removeEventListener("resize", cb);
    },
    () => window.innerWidth,
    () => window.innerWidth
  );
}

export const UseSyncExternalStoreDemo: React.FC = () => {
  const width = useWindowWidth();
  return (
    <div>
      <div>window width: {width} </div>
    </div>
  );
};

样式方案

  1. 内联方案 <i style={``{color: "blue"}}>哈哈</i>
  2. module css
  3. css in js
  4. tailwind css

状态方案

  1. useState
  2. useReducer
  3. useContext
  4. useSyncExternalStore
  5. redux
  6. mobx
  7. zustand
  8. jotai
  9. recoil
相关推荐
懋学的前端攻城狮16 分钟前
深入浅出Vue源码 - 剖析diff算法的核心实现
前端·vue.js·前端框架
小沙盒28 分钟前
使用js把txt的url列表转换成html列表
前端·javascript·html
超浪的晨39 分钟前
JavaWeb 进阶:Vue.js 与 Spring Boot 全栈开发实战(Java 开发者视角)
java·开发语言·前端·javascript·vue.js·html·个人开发
开开心心就好1 小时前
PDF转图片工具,一键转换高清无损
服务器·前端·智能手机·r语言·pdf·excel·batch
Java手札1 小时前
安装如下依赖(package.json 未包含):vueelement-plusecharts@element-plus/icons-vue
前端·javascript·vue.js
EndingCoder1 小时前
Three.js + AI:结合 Stable Diffusion 生成纹理贴图
开发语言·前端·javascript·人工智能·stable diffusion·ecmascript·three.js
haruma sen1 小时前
VUE前端
前端
汪叽家的兔子羡1 小时前
vue模块化导入
前端·javascript·vue.js·typescript·vue3·vue2·vite
植物系青年2 小时前
300 行代码!手把手教你写一个简版 Vue3 框架 📣
前端·vue.js
秉承初心2 小时前
Vue3与ES6+的现代化开发实践(AI)
前端·vue.js·es6