React 基本api

67 阅读4分钟

关键字

关键字及其作用

  1. React

    • 作用:从 React 库导入 React 对象,用于创建 React 组件、元素等。
    • 示例:import React from 'react';
  2. Component

    • 作用:创建类组件时,从 React 库中导入的基类。
    • 示例:class MyComponent extends React.Component { ... }
  3. useState

    • 作用:React 钩子,用于在函数组件中添加状态。
    • 示例:const [state, setState] = useState(initialState);
  4. useEffect

    • 作用:React 钩子,用于在函数组件中执行副作用(如数据获取、订阅等)。
    • 示例:useEffect(() => { ... }, [dependencies]);
  5. useContext

    • 作用:React 钩子,用于在函数组件中访问上下文。
    • 示例:const value = useContext(MyContext);
  6. useReducer

    • 作用:React 钩子,用于在函数组件中管理复杂状态逻辑。
    • 示例:const [state, dispatch] = useReducer(reducer, initialState);
  7. Fragment

    • 作用:React 组件,用于返回多个元素而不在 DOM 中添加额外节点。
    • 示例:<React.Fragment>...</React.Fragment><></>
  8. JSX

    • 作用:一种 JavaScript 语法扩展,用于在 React 中描述 UI。
    • 示例:const element = <h1>Hello, world!</h1>;
  9. render

    • 作用:ReactDOM 的方法,用于将 React 元素渲染到 DOM 中。
    • 示例:ReactDOM.render(<App />, document.getElementById('root'));

代码实例

类组件

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default MyComponent;

函数组件

import React, { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default MyComponent;

使用 useEffect 钩子

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default MyComponent;

使用 Context API

import React, { useContext } from 'react';

const MyContext = React.createContext();

function MyComponent() {
  const value = useContext(MyContext);

  return <div>{value}</div>;
}

function App() {
  return (
    <MyContext.Provider value="Hello, world!">
      <MyComponent />
    </MyContext.Provider>
  );
}

export default App;

这些示例展示了 React 的核心功能和用法,包括类组件和函数组件的创建,以及常用的 React 钩子如 useStateuseEffect 的使用。

定义变量和类型推断

在 JavaScript 中,变量的定义和类型推断是编程的基本概念。以下是变量定义和类型推断的详细说明和示例。

变量定义

JavaScript 提供了三种方式来定义变量:varletconst

使用 var 定义变量

var 是最早的变量定义方式,具有函数作用域,但没有块作用域。

var x = 10;
console.log(x); // 输出: 10

if (true) {
  var y = 20;
  console.log(y); // 输出: 20
}
console.log(y); // 输出: 20,因为 var 没有块作用域

使用 let 定义变量

let 定义块作用域变量,解决了 var 的一些问题。

let x = 10;
console.log(x); // 输出: 10

if (true) {
  let y = 20;
  console.log(y); // 输出: 20
}
console.log(y); // ReferenceError: y is not defined,因为 let 有块作用域

使用 const 定义常量

const 定义块作用域常量,值一旦定义就不能改变。

const x = 10;
console.log(x); // 输出: 10

x = 20; // TypeError: Assignment to constant variable

const obj = { name: 'Alice' };
obj.name = 'Bob'; // 允许修改对象的属性
console.log(obj.name); // 输出: Bob

类型推断

JavaScript 是一种动态类型语言,变量类型是在运行时确定的。

自动类型推断

JavaScript 会根据赋值自动推断变量类型。

let x = 10; // x 被推断为 number 类型
console.log(typeof x); // 输出: number

let y = 'Hello'; // y 被推断为 string 类型
console.log(typeof y); // 输出: string

let z = true; // z 被推断为 boolean 类型
console.log(typeof z); // 输出: boolean

动态类型

变量的类型可以在运行时更改。

let x = 10;
console.log(typeof x); // 输出: number

x = 'Hello';
console.log(typeof x); // 输出: string

x = true;
console.log(typeof x); // 输出: boolean

类型检查

可以使用 typeof 操作符检查变量的类型。

let x = 10;
console.log(typeof x); // 输出: number

let y = 'Hello';
console.log(typeof y); // 输出: string

let z;
console.log(typeof z); // 输出: undefined

let obj = { name: 'Alice' };
console.log(typeof obj); // 输出: object

let func = function() {};
console.log(typeof func); // 输出: function

let symbol = Symbol();
console.log(typeof symbol); // 输出: symbol

变量提升

var 声明的变量会提升到函数或全局作用域的顶部,但赋值不会提升。

console.log(x); // 输出: undefined,因为 x 被提升了,但还没有赋值
var x = 10;
console.log(x); // 输出: 10

letconst 声明的变量不会提升。

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;
console.log(y); // 输出: 10

小结

  1. var 定义的变量具有函数作用域,不具有块作用域,并且会发生变量提升。
  2. letconst 定义的变量具有块作用域,不会发生变量提升。
  3. const 定义的变量一旦赋值不能改变,但可以修改对象的属性。
  4. JavaScript 是动态类型语言,变量的类型可以在运行时更改。
  5. 可以使用 typeof 操作符检查变量的类型。

基本数据类型和数组

好的,以下是 JavaScript 中基本数据类型和数组的定义及代码实例,使用表格进行展示:

数据类型定义示例代码
Number表示整数或浮点数let age = 25; let price = 99.99;
String表示文本数据let name = "John Doe"; let greeting = 'Hello, world!';
Boolean表示 truefalselet isActive = true; let isAdmin = false;
Undefined表示变量已声明但尚未赋值let myVar; console.log(myVar); // 输出: undefined
Null表示空值或不存在的值let emptyValue = null;
Symbol表示唯一且不可变的值,通常用于对象属性的唯一标识符let sym = Symbol('unique');
BigInt表示任意精度的整数let bigInt = BigInt(9007199254740991);

数组

数组是一种特殊的对象,可以存储多个值,并且这些值可以是不同的数据类型。数组使用方括号表示,数组中的元素使用逗号分隔。

功能定义示例代码
数组定义使用方括号定义数组,数组中的元素使用逗号分隔let numbers = [1, 2, 3, 4, 5]; let mixedArray = [1, 'two', true, null];
访问元素通过索引访问数组中的元素,索引从0开始let firstNumber = numbers[0]; console.log(firstNumber); // 输出: 1
修改元素通过索引修改数组中的元素numbers[1] = 22; console.log(numbers); // 输出: [1, 22, 3, 4, 5]
添加元素使用 push 方法在数组末尾添加元素numbers.push(6); console.log(numbers); // 输出: [1, 2, 3, 4, 5, 6]
删除元素使用 pop 方法删除数组末尾的元素let lastNumber = numbers.pop(); console.log(numbers); // 输出: [1, 2, 3, 4, 5]
数组长度使用 length 属性获取数组的长度let length = numbers.length; console.log(length); // 输出: 5
数组遍历使用 for 循环遍历数组中的元素for (let i = 0; i < numbers.length; i++) { console.log(numbers[i]); }
高阶函数使用数组的高阶函数如 forEach, map, filter 等进行操作numbers.forEach(num => console.log(num)); let doubled = numbers.map(num => num * 2); console.log(doubled); // 输出: [2, 4, 6, 8, 10]

列表和集合容器

列表(数组)

功能定义示例代码
数组定义使用方括号定义数组,数组中的元素使用逗号分隔let numbers = [1, 2, 3, 4, 5]; let mixedArray = [1, 'two', true, null];
访问元素通过索引访问数组中的元素,索引从0开始let firstNumber = numbers[0]; console.log(firstNumber); // 输出: 1
修改元素通过索引修改数组中的元素numbers[1] = 22; console.log(numbers); // 输出: [1, 22, 3, 4, 5]
添加元素使用 push 方法在数组末尾添加元素numbers.push(6); console.log(numbers); // 输出: [1, 2, 3, 4, 5, 6]
删除元素使用 pop 方法删除数组末尾的元素let lastNumber = numbers.pop(); console.log(numbers); // 输出: [1, 2, 3, 4, 5]
数组长度使用 length 属性获取数组的长度let length = numbers.length; console.log(length); // 输出: 5
数组遍历使用 for 循环遍历数组中的元素for (let i = 0; i < numbers.length; i++) { console.log(numbers[i]); }
高阶函数使用数组的高阶函数如 forEach, map, filternumbers.forEach(num => console.log(num)); let doubled = numbers.map(num => num * 2); console.log(doubled); // 输出: [2, 4, 6, 8, 10]

Set

功能定义示例代码
Set 定义使用 Set 构造函数创建一个新的 Set 对象let mySet = new Set();
添加元素使用 add 方法向 Set 添加元素mySet.add(1); mySet.add(5); mySet.add(5); // 重复元素不会被添加
删除元素使用 delete 方法从 Set 中删除元素mySet.delete(1);
检查元素使用 has 方法检查 Set 中是否包含某个元素console.log(mySet.has(5)); // 输出: true
Set 大小使用 size 属性获取 Set 中元素的数量console.log(mySet.size); // 输出: 1
遍历 Set使用 forEach 方法或 for...of 循环遍历 SetmySet.forEach(value => console.log(value)); for (let value of mySet) { console.log(value); }

Map

功能定义示例代码
Map 定义使用 Map 构造函数创建一个新的 Map 对象let myMap = new Map();
设置键值对使用 set 方法向 Map 添加键值对myMap.set('key1', 'value1'); myMap.set(2, 'value2');
获取值使用 get 方法根据键获取值console.log(myMap.get('key1')); // 输出: 'value1'
删除键值对使用 delete 方法从 Map 中删除键值对myMap.delete(2);
检查键使用 has 方法检查 Map 中是否包含某个键console.log(myMap.has('key1')); // 输出: true
Map 大小使用 size 属性获取 Map 中键值对的数量console.log(myMap.size); // 输出: 1
遍历 Map使用 forEach 方法或 for...of 循环遍历 MapmyMap.forEach((value, key) => console.log(key, value)); for (let [key, value] of myMap) { console.log(key, value); }

WeakSet

功能定义示例代码
WeakSet 定义使用 WeakSet 构造函数创建一个新的 WeakSet 对象let myWeakSet = new WeakSet();
添加元素使用 add 方法向 WeakSet 添加对象let obj = {}; myWeakSet.add(obj);
删除元素使用 delete 方法从 WeakSet 中删除对象myWeakSet.delete(obj);
检查元素使用 has 方法检查 WeakSet 中是否包含某个对象console.log(myWeakSet.has(obj)); // 输出: true

WeakMap

功能定义示例代码
WeakMap 定义使用 WeakMap 构造函数创建一个新的 WeakMap 对象let myWeakMap = new WeakMap();
设置键值对使用 set 方法向 WeakMap 添加键值对let obj = {}; myWeakMap.set(obj, 'value');
获取值使用 get 方法根据键获取值console.log(myWeakMap.get(obj)); // 输出: 'value'
删除键值对使用 delete 方法从 WeakMap 中删除键值对myWeakMap.delete(obj);
检查键使用 has 方法检查 WeakMap 中是否包含某个键console.log(myWeakMap.has(obj)); // 输出: true

锁的使用和并发控制及异步能力

锁的使用和并发控制

JavaScript 本身不直接提供锁的机制,但可以通过一些库和新的 API 实现并发控制。以下是一些常用的并发控制方法及代码示例:

Mutex(互斥锁)

使用第三方库 async-mutex 实现互斥锁:

import { Mutex } from 'async-mutex';

const mutex = new Mutex();

async function criticalSection() {
  await mutex.runExclusive(async () => {
    // 关键代码
    console.log('Executing critical section');
    await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步操作
  });
}

criticalSection();
criticalSection();

Atomic 操作

使用 AtomicsSharedArrayBuffer 进行低级并发控制:

const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);

Atomics.add(sharedArray, 0, 1);
console.log(Atomics.load(sharedArray, 0)); // 输出: 1

Web Workers

使用 Web Workers 实现并发执行:

// worker.js
onmessage = function(event) {
  if (event.data === 'start') {
    let result = 0;
    for (let i = 0; i < 1e9; i++) {
      result += i;
    }
    postMessage(result);
  }
};

// main.js
const worker = new Worker('worker.js');
worker.postMessage('start');
worker.onmessage = (event) => {
  console.log(event.data); // 输出计算结果
};

异步能力

JavaScript 的异步能力主要通过回调函数、Promise、async/await 以及事件循环实现。

回调函数

通过回调函数处理异步操作:

function asyncOperation(callback) {
  setTimeout(() => {
    callback('done');
  }, 1000);
}

asyncOperation(result => {
  console.log(result); // 输出: done
});

Promise

使用 Promise 对象进行异步操作:

function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('done');
    }, 1000);
  });
}

asyncOperation().then(result => {
  console.log(result); // 输出: done
});

async/await

使用 async/await 语法糖简化异步操作:

function asyncOperation() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('done');
    }, 1000);
  });
}

async function run() {
  const result = await asyncOperation();
  console.log(result); // 输出: done
}

run();

事件循环

通过事件循环机制管理异步操作:

console.log('start');

setTimeout(() => {
  console.log('timeout');
}, 0);

Promise.resolve().then(() => {
  console.log('promise');
});

console.log('end');
// 输出: start, end, promise, timeout

综合示例:并发控制与异步能力结合

使用 async 函数和互斥锁管理异步任务队列:

import { Mutex } from 'async-mutex';

const mutex = new Mutex();

async function asyncTask(name) {
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log(name + ' done');
}

async function run() {
  await mutex.runExclusive(async () => {
    await asyncTask('Task 1');
  });

  await mutex.runExclusive(async () => {
    await asyncTask('Task 2');
  });
}

run();

定义函数和方法

在 JavaScript 中,方法和函数是基本的编程构造,用于定义可重用的代码块。函数是独立的代码块,而方法是对象的属性。以下是函数和方法的定义及其用法示例:

函数定义

函数声明

通过 function 关键字定义一个命名函数:

function greet(name) {
  return `Hello, ${name}!`;
}

console.log(greet('Alice')); // 输出: Hello, Alice!

函数表达式

将函数赋值给一个变量,可以是命名或匿名函数:

const greet = function(name) {
  return `Hello, ${name}!`;
};

console.log(greet('Bob')); // 输出: Hello, Bob!

箭头函数

使用箭头函数语法定义函数,箭头函数不能作为构造函数使用,也没有自己的 this

const greet = (name) => `Hello, ${name}!`;

console.log(greet('Charlie')); // 输出: Hello, Charlie!

方法定义

方法是对象的属性,可以使用函数表达式或箭头函数定义:

对象方法

使用函数表达式定义对象的方法:

const person = {
  name: 'Alice',
  greet: function() {
    return `Hello, my name is ${this.name}`;
  }
};

console.log(person.greet()); // 输出: Hello, my name is Alice

简写方法定义

ES6 允许在对象中直接定义方法:

const person = {
  name: 'Bob',
  greet() {
    return `Hello, my name is ${this.name}`;
  }
};

console.log(person.greet()); // 输出: Hello, my name is Bob

箭头函数作为方法

箭头函数没有自己的 this,它会捕获定义时所在上下文的 this 值,不适合作为对象的方法:

const person = {
  name: 'Charlie',
  greet: () => {
    return `Hello, my name is ${this.name}`; // this.name 是 undefined
  }
};

console.log(person.greet()); // 输出: Hello, my name is undefined

函数与方法的区别

  1. 函数 是独立的代码块,定义后可以在任何地方调用。函数可以使用函数声明、函数表达式或箭头函数语法定义。
  2. 方法 是对象的属性,其值是一个函数。方法可以使用函数表达式或对象简写语法定义,但箭头函数不适合作为方法,因为它没有自己的 this

OOP

在 React 中,面向对象编程(OOP)主要通过类组件来实现。尽管函数组件和 Hooks 现在更流行,但理解类组件及其与 OOP 的结合仍然非常重要。以下是 React 中如何应用 OOP 的详细说明和示例。

类组件

类组件是 React 中的一种组件定义方式,通过扩展 React.Component 类来创建。

定义类组件

使用 ES6 类语法定义类组件,并在其中定义状态和生命周期方法。

import React, { Component } from 'react';

class Greeting extends Component {
  constructor(props) {
    super(props);
    this.state = { name: 'Alice' };
  }

  render() {
    return (
      <div>
        Hello, {this.state.name}!
      </div>
    );
  }
}

export default Greeting;

状态管理

类组件使用 this.state 定义状态,并使用 this.setState 更新状态。

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default Counter;

生命周期方法

类组件提供了一系列生命周期方法,用于在组件的不同阶段执行特定操作。

import React, { Component } from 'react';

class LifecycleDemo extends Component {
  constructor(props) {
    super(props);
    console.log('Constructor');
  }

  componentDidMount() {
    console.log('Component did mount');
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('Component did update');
  }

  componentWillUnmount() {
    console.log('Component will unmount');
  }

  render() {
    console.log('Render');
    return (
      <div>
        Check the console for lifecycle method logs.
      </div>
    );
  }
}

export default LifecycleDemo;

继承与组合

在 React 中,推荐使用组合而不是继承来重用代码和逻辑。

组件继承

虽然在 React 中继承较少见,但可以实现简单的继承。

import React, { Component } from 'react';

class Button extends Component {
  render() {
    return (
      <button>{this.props.label}</button>
    );
  }
}

class SubmitButton extends Button {
  render() {
    return (
      <button type="submit">{this.props.label}</button>
    );
  }
}

export default SubmitButton;

组件组合

组合是 React 中更推荐的代码重用方式。

import React from 'react';

const FancyBorder = (props) => {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
};

const WelcomeDialog = () => {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">Welcome</h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
};

export default WelcomeDialog;

高阶组件(HOC)

高阶组件是一种函数,接受一个组件并返回一个新的组件,以增强其功能。

import React, { Component } from 'react';

const withLogging = (WrappedComponent) => {
  return class extends Component {
    componentDidMount() {
      console.log(`Component ${WrappedComponent.name} mounted`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

class MyComponent extends Component {
  render() {
    return <div>MyComponent</div>;
  }
}

export default withLogging(MyComponent);

结论

在 React 中,类组件提供了实现面向对象编程的途径,支持状态管理、生命周期方法以及通过继承和组合实现代码重用。虽然函数组件和 Hooks 现在更为流行,但理解类组件和 OOP 的概念依然对全面掌握 React 开发非常重要。

以下是总结的关键点:

  1. 类组件:通过继承 React.Component 创建类组件,定义状态和生命周期方法。
  2. 状态管理:使用 this.statethis.setState 管理组件状态。
  3. 生命周期方法:使用 componentDidMountcomponentDidUpdatecomponentWillUnmount 等方法控制组件生命周期中的不同阶段。
  4. 继承与组合:推荐使用组合而非继承来重用代码。
  5. 高阶组件:通过高阶组件增强组件功能,实现逻辑重用。

项目目录结构

在 React 项目中,良好的目录结构可以提高代码的可读性和可维护性。以下是一个典型的 React 项目目录结构示例,适用于中小型项目。在大型项目中,您可能需要根据具体需求进行调整和扩展。

my-react-app/
├── node_modules/
├── public/
│   ├── index.html
│   ├── favicon.ico
│   └── ...
├── src/
│   ├── assets/
│   │   ├── images/
│   │   └── styles/
│   ├── components/
│   │   ├── common/
│   │   │   ├── Button.js
│   │   │   ├── Header.js
│   │   │   └── ...
│   │   ├── layout/
│   │   │   ├── Navbar.js
│   │   │   ├── Sidebar.js
│   │   │   └── ...
│   │   └── ...
│   ├── pages/
│   │   ├── HomePage.js
│   │   ├── AboutPage.js
│   │   ├── ContactPage.js
│   │   └── ...
│   ├── hooks/
│   │   ├── useAuth.js
│   │   ├── useFetch.js
│   │   └── ...
│   ├── services/
│   │   ├── api.js
│   │   └── ...
│   ├── utils/
│   │   ├── helpers.js
│   │   ├── constants.js
│   │   └── ...
│   ├── App.js
│   ├── index.js
│   └── ...
├── .gitignore
├── package.json
├── README.md
└── ...

目录结构详细说明

  1. node_modules/:

    • 存放项目的所有依赖包。这个目录是由 npmyarn 自动生成的,不需要手动管理。
  2. public/:

    • 存放公共资源,例如 index.html 是项目的主 HTML 文件,favicon.ico 是网站图标,其他静态资源也可以放在这里。
  3. src/:

    • 项目源代码的根目录,所有 React 组件、样式和业务逻辑都在这里。

    • assets/:

      • 存放静态资源,如图片、字体、全局样式等。
      • images/: 存放图片文件。
      • styles/: 存放全局样式文件。
    • components/:

      • 存放可重用的组件。
      • common/: 通用组件,如按钮、输入框等。
      • layout/: 布局相关组件,如导航栏、侧边栏等。
    • pages/:

      • 存放页面组件,每个页面对应一个文件或文件夹。
    • hooks/:

      • 存放自定义 Hooks,用于抽离和复用状态逻辑。
      • useAuth.js: 示例自定义 Hook,用于处理认证逻辑。
      • useFetch.js: 示例自定义 Hook,用于处理数据获取。
    • services/:

      • 存放与服务器通信的代码,例如 API 请求。
      • api.js: 示例 API 服务文件。
    • utils/:

      • 存放工具函数、常量等辅助功能代码。
      • helpers.js: 示例工具函数文件。
      • constants.js: 示例常量文件。
    • App.js:

      • 主应用组件,是应用的根组件,包含路由定义和全局状态管理等。
    • index.js:

      • 应用的入口文件,将 App 组件挂载到 DOM 中。
  4. .gitignore:

    • Git 忽略文件,定义哪些文件和目录不应该被提交到版本控制系统。
  5. package.json:

    • 项目的配置文件,定义项目的依赖、脚本和其他项目信息。
  6. README.md:

    • 项目说明文件,通常包含项目的简介、安装和使用说明等。