react 是一个用于构建用户界面的前端框架,具有很丰富的生态工具,包括 ReactRouter AntDesign Redux Next

https://zh-hans.react.dev/learn/creating-a-react-app

项目创建

create-react-app

create-react-app 是一个用于快速构建 react app 的脚手架,底层是基于webpack构建!

https://create-react-app.bootcss.com/docs/getting-started

创建一个 react app 项目

npx create-react-app my-app

自定义好一个项目目录文件夹内,通过bash窗口来执行npx命令来创建一个react app项目!

cd my-app
npm start

进入项目内部,执行 npm start 即可启动项目!

项目目录

创建项目后,除了index.jsApp.js以外多余的删掉即可,记得把引用的静态资源文件路径在App.js删除掉,避免无法找到该文件报错!

├── package-lock.json
├── package.json
├── public   # 静态文件
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
└── src      # 源代码文件
    ├── App.js 
    └── index.js  # 入口文件

index.js

index.js入口文件reactreact-dom 是核心包!

ReactDom 获取 publc -> index.htmlidroot根元素,并将App.js内容挂载到根节点!

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

App.js

function App() {
  return (
    <div className="App">
      <center>
        <h1>Welcome to React!</h1>
      </center>
    </div>
  );
}

export default App;

基础内容

jsx 基础

jsxxmlhtml 的一个缩写方式,可以通过标签的写法,在其内部可通过{ } 大括号来访问js中提供的变量信息,是React UI编写模版的一种方式!

const message = "Welcome to React!";
function App() {
  return (
    <div className="App">
      <center>
        <h1>{message}</h1>
      </center>
    </div>
  );
}

jsx 语法本身在浏览器端不被识别的,需要通过babel工具,将jsx转换为浏览器能够认识的代码!

识别js表达式

jsx 中使用表达式,只需要在 { 表达式 } 花括号中使用即可,类似于模版字符串${ } ,可识别字符串、js变量、函数调用返回值、以及对象方法的调用!

const message = "Welcome to React!";
function test(){
  return 'test text';
}
function App() {
  return (
    <div className="App">
      <center>
        <h1>{"Hello, World!"}</h1>
        <h1>{message}</h1>
        <h1>{test()}</h1>
        <h1>{new Date().toLocaleString()}</h1>
        <h1>{Math.random()}</h1>
        <h1>{[1, 2, 3, 4, 5].map(num => <p key={num}>{num}</p>)}</h1>
        <h1 style={{color: 'blue'}}>测试样式</h1>
      </center>
    </div>
  );
}

列表渲染

列表渲染,key属性必要的,必须是唯一值!

const list = [
  {id: 1, name: 'apple'},
  {id: 2, name: 'banana'},
  {id: 3, name: 'orange'},
  {id: 4, name: 'grape'},
]
<ul>
   {list.map(item => <li key={item.id}>{item.name}</li>)}
</ul>

条件渲染

{flag && <h1>Flag is true</h1>}
{!flag && <h1>Flag is false</h1>}
{flag ? <h1>Flag is false</h1> : <h2>Flag is true</h2>}

事件绑定

function handleClick(){
  console.log('clicked');
}

<button onClick={handleClick}>Click me</button>

参数绑定

function handleClick(name, e){
  console.log('clicked');
}
<button onClick={(e) => handleClick("name", e)}>Click me</button>

React 函数组件

组件,一般用来将重复的视图部分,提取出一个公共的组件,供其他视图模块使用,在React中,定义一个组件都可视为一个函数(Function) 且函数名首字母需要大写!

定义一个button组件, components -> button -> index.js

function Button(props) {
  return <button className="btn">{props.text}</button>;
}

export default Button;

在其它组件中使用

import Button from "./components/button";
const message = "Welcome to React!";
let flag = true;
function test(){
  return 'test text';
}
const list = [
  {id: 1, name: 'apple'},
  {id: 2, name: 'banana'},
  {id: 3, name: 'orange'},
  {id: 4, name: 'grape'},
]
function handleClick(){
  console.log('clicked');
}
function App() {
  return (
    <div className="App">
      <center>
        <h1>{"Hello, World!"}</h1>
        <h1>{message}</h1>
        <h1>{test()}</h1>
        <h1>{new Date().toLocaleString()}</h1>
        <h1>{Math.random()}</h1>
        <h1>
          {[1, 2, 3, 4, 5].map((num) => (
            <p key={num}>{num}</p>
          ))}
        </h1>
        <h1 style={{ color: "blue" }}>测试样式</h1>
        <ul>
          {list.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
        {flag && <h1>Flag is true</h1>}
        {!flag && <h1>Flag is false</h1>}
        {flag ? <h1>Flag is false</h1> : <h2>Flag is true</h2>}
        <button onClick={(e) => handleClick("name", e)}>Click me</button>
        <div className="">
          自定义组件
          <Button text="Hello, World!" />
        </div>
      </center>
    </div>
  );
}

export default App;

useState 状态变量

useStateReact中是一个状态变量,主要用于更新视图,可理解为响应式,当数据发生变化时,通过useState函数来更新视图!

类似于vue3中的refreactive变量转换为响应式!

// const [count, setCount] = useState(0);  不能在这定义 useState ❎
function App() {
  const [count, setCount] = useState(0);  // 必须在函数组件内部定义 useState ✅

  return (
    <div className="App">
      <center>
        <div className="custom-component">
          {count + 1}
          <button onClick={() => setCount(count + 1)}>useState 状态变量</button>
        </div>
      </center>
    </div>
  );
}

useState 可接受一个初始值的参数,返回一个数组,其中第一个是状态变量,第二个是更新状态变量的函数,当数据需要改变时,调用setCount函数更新视图!

需要注意的是,useState Hooks函数必须在 组件函数内部使用!

修改复杂类型

修改引用类型时,需要始终返回一个新的对象地址修改才行,避免修改时影响对源对象引用地址!

import { useState } from "react";

function App() {
  const [ user, setUser ] = useState({ name: "Miao", age: 25 });
  function modifyUser(){
    // 参数传递一个新的对象,并对原有的属性进行覆盖,而不是在原有对象的地址上进行修改
    setUser({...user, age: user.age + 1});
  }
  return (
    <div className="App">
      <center>
        <div className="custom-component">
          <p>{user.name} {user.age}</p>
          <button onClick={modifyUser}>useState 修改对象</button>
        </div>
      </center>
    </div>
  );
}

export default App;

组件样式控制

  1. 使用行内样式

    行内样式,多单词之间使用驼峰命名!

    <ul style={{ listStyle: "none", fontSize: "20px", color: "red" }}>
       {list.map((item) => (
          <li key={item.id}>{item.name}</li>
       ))}
    </ul>
    
  2. 使用类样式className

    .app{
      border: 1px solid red;
    }
    
    import './index.css'
    fucntion App(){
      return (
        <div className = "app">
            类样式
        </div>
      )
    }
    

todoListDemo

index.css

.list{
  border: 1px solid #ccc;
}
.list .item{
  padding: 10px;
}
.list .item:not(:last-child){
  border-bottom: 1px solid #ccc;
}
import { useState } from "react";
import "./styles/index.css"
const list = [
  { id: 1, name: "apple" },
  { id: 2, name: "banana" },
  { id: 3, name: "orange" },
  { id: 4, name: "grape" },
];
function App() {
  const [items, setItems] = useState(list);
  return (
    <div className="App">
      <center>
        <h1>{"Hello, World!"}</h1>
        <button onClick={() => setItems([...items, { id: items.length + 1, name: "new item" }])}>新增</button>
        <div className="list">
          {items.map((item) => (
            <div key={item.id} className="item">
              <h2>{item.name}</h2>
              <div className="btn" style={{textAlign: "right"}}>
                <button onClick={() => setItems(items.filter((i) => i.id !== item.id))}>
                  Delete
                </button>
              </div>
            </div>
          ))}
        </div>
      </center>
    </div>
  );
}

export default App;

classname动态类名优化

React中,避免不了有些类名是需要条件切换的,通常会根据某一个条件来判断当前类名是否显示与否,比如,当前激活元素active设置为高亮,通常会通过${ currentIndex === index && 'active' } ,如果动态类名多了,看起来就比较费劲,这时需要classname这个第三方插件,来改写下方式,减少复杂度!

安装插件

npm install classname

插件用法

表单数据绑定

表单数据绑定,就是数据双向绑定,当输入内容时,将数据同步状态变量中,当状态变量发生变化时,会同时修改input中的value值!

大概原理与vue中的v-model语法糖一致,vue中会通过v-bind的方式,来绑定value值,且通过input事件值$emit出去并修改!

const [value, setValue] = useState("");
<input value = {value} @change="(e) => setValue(e.target.value)" type="text" />

useRef获取dom

React中,获取dom需要通过useRef Hooks函数来生成一个ref引用,对目标元素进行一个ref绑定!

import { useRef, useState } from "react";
function App() {
  const appRef = useRef(null);
  return (
    <div className="App" ref={appRef}>
      <center>
        <button onClick={() => console.log(appRef.current.getBoundingClientRect())}>点击获取dom</button>
      </center>
    </div>
  );
}
export default App;

通过 appRef.current 属性来获取dom,操作dom时需要等待dom完全渲染完成后才能使用!

组件传递

父子通信

通过props里实现父子组件通信

function Button(props) {
  return <button className="btn">{props.text}</button>;
}
function Parent() {
  let text = "新增";  
  return <Button text={text} />;
}

子父通信

子传父,在React中,通过父组件向子组件传递函数,且在子组件来当做参数来调用父组件函数!

function Button(props) {
  let childrenMsg = 'children msg!';
  return <button className="btn" onClick={() => props.getMessage(childrenMsg)}>{props.text}</button>;
}
function Parent() {
  let text = "新增";  
  function getMessage(msg){

  }
  return <Button text={text} onGetMessage={ getMessage } />;
}

兄弟通信

使用状态提升,可以完成兄弟之间通信状态提升,就是父组件,将子组件中的数据,获取到,并通过父传子的方式,到另一个子组件,便可完成跨兄弟组件通信!

import { useState } from "react";

function Children1(props) {
  console.log("Children1 props", props);
  return <div>children1 {props.childrenMsg}</div>;
}

function Children2(props) {
  let childrenMsg = 'Children2'
  return (
    <div>
      {childrenMsg}
      <button
        className="btn"
        onClick={() => props.onGetChildren2Msg(childrenMsg)}
      >
        Children2
      </button>
    </div>
  );
}
function Button(props) {
  const [ msg, setMsg ] = useState('');
  const getChildren2Msg = ( msg ) => {
    setMsg(msg);
    console.log('parent Button', msg);
  }
  return (
    <div>
      <Children1 childrenMsg={msg} />
      <Children2 onGetChildren2Msg={getChildren2Msg} />
      <button className="btn">{props.text}</button>
    </div>
  );
}

export default Button;

跨组件通信

跨组件通信使用Context机制实现跨层传递!

  1. 创建 createContext() 返回一个上下文

  2. 根组件中,使用Provider来提供数据

  3. 子组件孙子组件中,通过useContext获取上下文提供的数据!

import { createContext, use, useContext, useState } from "react";
// 创建 createContext 上下文
const MsgContext = createContext();
function Children1(props) {
  console.log("Children1 props", props);
  // 使用useContext获取上下文,获取 provider 提供的数据
  const msg = useContext(MsgContext);
  console.log("Children1 msg 跨层传递消息 useContext", msg);
  return <div>children1 {props.childrenMsg}</div>;
}

function Children2(props) {
  let childrenMsg = 'Children2'
  // props.getChildren2Msg(childrenMsg);
  return (
    <div>
      {childrenMsg}
      <button
        className="btn"
        onClick={() => props.onGetChildren2Msg(childrenMsg)}
      >
        Children2
      </button>
    </div>
  );
}
function Button(props) {
  const [ msg, setMsg ] = useState('');
  const getChildren2Msg = ( msg ) => {
    setMsg(msg);
    console.log('parent Button', msg);
  }
  return (
    <div>
      <!-- 在根组件中使用 provider 向外提供数据-->
      <MsgContext.Provider value={{ msg, setMsg }}>
        <Children1 childrenMsg={msg} />
        <Children2 onGetChildren2Msg={getChildren2Msg} />
      </MsgContext.Provider>

      <button className="btn">{props.text}</button>
    </div>
  );
}

export default Button;

useEffect

useEffect 主要用来处理副作用的,主要处理与渲染无关的逻辑,比如数据请求、dom操作、事件监听等相关逻辑!

useEffect 有两个参数,一个是回调函数,另一个是依赖项数组!

useEffect(()=> {}, [])

useEffect 会在每次渲染完成后执行调用,当设置第二个参数依赖空数组时,则只会在第一次渲染结束后执行!

当依赖项数组不为空时,则会在依赖项发生变化时,触发调用!

依赖项: 就是页面中所展示依赖的数据项,页面数据发生变化时,触发操作!

import { useEffect, useState } from "react";
import "./styles/index.css"
function App() {
  const  [ counter, setCounter ] = useState(0);
  // 不传入依赖项,useEffect在每次渲染时都会执行
  /* useEffect(() => {
    console.log("Hello, World!");
  }); */
  // 传入空数组,useEffect只会在第一次渲染时执行
  /* useEffect(() => {
    console.log("Hello, World!");
  }, []); */
  // 传入依赖项,useEffect只在counter变化时才会执行
  useEffect(() => {
    console.log("counter changed:", counter);
  }, [counter]);
  return (
    <div className="App">
      <center>
        <h1>{("Hello, World!", counter)}</h1>
        <button onClick={() => setCounter(counter + 1)}>+</button>
      </center>
    </div>
  );
}

export default App;

useEffect 清除副作用

useEffect内部可以返回一个函数,在函数内部可以实现组件卸载后,执行一些逻辑操作,比如清除定时器,以及移除事件监听相关操作!

useEffect(() => {
    console.log("counter changed:", counter);
   const timer = setInterval(() => {
      setCounter(counter + 1);
    }, 1000);
    return () => {
      // 清除副作用
      clearInterval(timer);
    }
  }, [counter]);

Hooks

Hooks函数封装的一个概念,将复用逻辑提取函数中进行处理后返回结果!

Hooks 函数的定义需要以useXxx方式去定义!

import { useState } from "react";
import "./styles/index.css"
function useToggle(){
  const [ isOn, setIsOn ] = useState(false);
  const toggle = () => setIsOn(!isOn);
  return [ isOn, toggle ];
}
function App() {
  const  [ counter, setCounter ] = useState(0);
  const [ isOn, toggle ] = useToggle();
  return (
    <div className="App">
      <center>
        <h1>{isOn ? "ON" : "OFF"}</h1>
        <button onClick={toggle}>Toggle</button>
      </center>
    </div>
  );
}

export default App;

Hooks 使用规范

Hooks只能在组件函数内部使用,且必须是顶层不能包含在 if for以及其他函数模块中使用!

  1. 不能在组件外部使用
    import { useState } from "react";
     import "./styles/index.css"
     function useToggle(){
       const [ isOn, setIsOn ] = useState(false);
       const toggle = () => setIsOn(!isOn);
       return [ isOn, toggle ];
     }
     const [ isOn, toggle ] = useToggle();  // no ❎
     function App() {
       const  [ counter, setCounter ] = useState(0);
       // const [ isOn, toggle ] = useToggle();  // yes ✅
       return (
         <div className="App">
           <center>
             <h1>{isOn ? "ON" : "OFF"}</h1>
             <button onClick={toggle}>Toggle</button>
           </center>
         </div>
       );
     }
    
  2. 不能在条件语句内使用
     import { useState } from "react";
     import "./styles/index.css"
     function useToggle(){
       const [ isOn, setIsOn ] = useState(false);
       const toggle = () => setIsOn(!isOn);
       return [ isOn, toggle ];
     }
     function App() {
       if( true ){
         const [ counter, setCounter ] = useState(0);
         const [ isOn, toggle ] = useToggle();  // yes ❎
       }
       return (
         <div className="App">
           <center>
             <h1>{isOn ? "ON" : "OFF"}</h1>
             <button onClick={toggle}>Toggle</button>
           </center>
         </div>
       );
     }