React + Java 技术面试完整指南

📚 Java 面试题(30道)

一、Java基础(10道)

1. Java中的八大基本数据类型及其包装类是什么?

答案:

  • byte (Byte) - 8位
  • short (Short) - 16位
  • int (Integer) - 32位
  • long (Long) - 64位
  • float (Float) - 32位
  • double (Double) - 64位
  • char (Character) - 16位
  • boolean (Boolean) - 1位

示例代码:

复制代码
public class PrimitiveTypes {
    public static void main(String[] args) {
        // 基本类型
        int num = 10;
        double price = 99.99;
        
        // 自动装箱
        Integer numObj = num;
        
        // 自动拆箱
        int value = numObj;
        
        System.out.println("Value: " + value);
    }
}
2. == 和 equals() 的区别是什么?

答案:

  • == 比较的是引用地址(对于对象)或值(对于基本类型)
  • equals() 比较的是对象的内容

示例代码:

复制代码
public class EqualsDemo {
    public static void main(String[] args) {
        String s1 = new String("hello");
        String s2 = new String("hello");
        String s3 = "hello";
        String s4 = "hello";
        
        System.out.println(s1 == s2);        // false(不同对象)
        System.out.println(s1.equals(s2));   // true(内容相同)
        System.out.println(s3 == s4);        // true(字符串常量池)
    }
}
3. String、StringBuilder、StringBuffer的区别?

答案:

  • String: 不可变,线程安全,适合少量字符串操作
  • StringBuilder: 可变,非线程安全,性能最好,适合单线程
  • StringBuffer: 可变,线程安全(synchronized),适合多线程

示例代码:

复制代码
public class StringComparison {
    public static void main(String[] args) {
        // String - 每次操作都创建新对象
        String str = "Hello";
        str += " World"; // 创建新对象
        
        // StringBuilder - 单线程最佳选择
        StringBuilder sb = new StringBuilder("Hello");
        sb.append(" World");
        
        // StringBuffer - 多线程安全
        StringBuffer sbf = new StringBuffer("Hello");
        sbf.append(" World");
        
        System.out.println(str);
        System.out.println(sb.toString());
        System.out.println(sbf.toString());
    }
}
4. Java中的四种引用类型?

答案:

  1. 强引用(Strong Reference): 普通引用,不会被GC回收
  2. 软引用(Soft Reference): 内存不足时会被回收
  3. 弱引用(Weak Reference): GC时会被回收
  4. 虚引用(Phantom Reference): 无法通过引用获取对象

示例代码:

复制代码
import java.lang.ref.*;

public class ReferenceDemo {
    public static void main(String[] args) {
        // 强引用
        Object strongRef = new Object();
        
        // 软引用
        SoftReference<Object> softRef = new SoftReference<>(new Object());
        
        // 弱引用
        WeakReference<Object> weakRef = new WeakReference<>(new Object());
        
        // 虚引用
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
        
        System.out.println("Soft: " + softRef.get());
        System.out.println("Weak: " + weakRef.get());
        System.out.println("Phantom: " + phantomRef.get()); // 总是null
    }
}
5. final、finally、finalize的区别?

答案:

  • final: 关键字,修饰类(不可继承)、方法(不可重写)、变量(常量)
  • finally: 异常处理中的代码块,总是执行
  • finalize(): Object类的方法,GC前调用

示例代码:

复制代码
public class FinalDemo {
    // final变量
    private final int MAX_SIZE = 100;
    
    // final方法
    public final void display() {
        System.out.println("Cannot be overridden");
    }
    
    // 异常处理
    public void testFinally() {
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            System.out.println("Exception caught");
        } finally {
            System.out.println("Finally always executes");
        }
    }
    
    // finalize方法
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Object is being garbage collected");
        super.finalize();
    }
}

// final类
final class ImmutableClass {
    // 不能被继承
}
6. Java中的异常体系?

答案:

  • Throwable : 所有异常的父类
    • Error: 系统错误,无法处理(如OutOfMemoryError)
    • Exception : 可以捕获处理的异常
      • Checked Exception: 编译时检查(如IOException)
      • Unchecked Exception: 运行时异常(如NullPointerException)

示例代码:

复制代码
public class ExceptionDemo {
    // 自定义异常
    static class CustomException extends Exception {
        public CustomException(String message) {
            super(message);
        }
    }
    
    // 抛出checked异常
    public void readFile(String path) throws IOException {
        if (path == null) {
            throw new IOException("Path cannot be null");
        }
        // 读取文件逻辑
    }
    
    // 处理异常
    public void handleException() {
        try {
            readFile(null);
        } catch (IOException e) {
            System.err.println("IO Error: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("General Error: " + e.getMessage());
        } finally {
            System.out.println("Cleanup code");
        }
    }
    
    // try-with-resources (Java 7+)
    public void autoClose() {
        try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
            String line = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
7. Java中的接口和抽象类的区别?

答案:

特性 接口 抽象类
方法 默认public abstract(Java 8+可有default/static) 可以有具体实现
变量 public static final 可以有实例变量
构造器 没有 可以有
继承 支持多继承 单继承

示例代码:

复制代码
// 接口
interface Animal {
    void makeSound(); // 抽象方法
    
    // Java 8+ default方法
    default void sleep() {
        System.out.println("Animal is sleeping");
    }
    
    // Java 8+ static方法
    static void info() {
        System.out.println("This is Animal interface");
    }
}

// 抽象类
abstract class Vehicle {
    protected String brand;
    
    // 构造器
    public Vehicle(String brand) {
        this.brand = brand;
    }
    
    // 抽象方法
    abstract void start();
    
    // 具体方法
    public void stop() {
        System.out.println(brand + " stopped");
    }
}

// 实现
class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

class Car extends Vehicle {
    public Car(String brand) {
        super(brand);
    }
    
    @Override
    void start() {
        System.out.println(brand + " started");
    }
}
8. Java中的重载(Overload)和重写(Override)的区别?

答案:

  • 重载: 同一个类中,方法名相同,参数不同(编译时多态)
  • 重写: 子类重新定义父类方法,方法签名必须相同(运行时多态)

示例代码:

复制代码
class Calculator {
    // 方法重载 - 参数个数不同
    public int add(int a, int b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    // 方法重载 - 参数类型不同
    public double add(double a, double b) {
        return a + b;
    }
}

class Parent {
    public void display() {
        System.out.println("Parent display");
    }
    
    public void show(String msg) {
        System.out.println("Parent: " + msg);
    }
}

class Child extends Parent {
    // 方法重写
    @Override
    public void display() {
        System.out.println("Child display");
    }
    
    @Override
    public void show(String msg) {
        System.out.println("Child: " + msg);
    }
}
9. Java中的访问修饰符?

答案:

修饰符 同类 同包 子类 其他包
private
default
protected
public

示例代码:

复制代码
package com.example;

public class AccessModifiers {
    private int privateVar = 1;
    int defaultVar = 2;
    protected int protectedVar = 3;
    public int publicVar = 4;
    
    private void privateMethod() {
        System.out.println("Private method");
    }
    
    void defaultMethod() {
        System.out.println("Default method");
    }
    
    protected void protectedMethod() {
        System.out.println("Protected method");
    }
    
    public void publicMethod() {
        System.out.println("Public method");
        // 同类中可以访问所有
        privateMethod();
    }
}
10. Java中的泛型是什么?

答案: 泛型提供了编译时类型安全检测机制,允许在编译时检测非法类型。

示例代码:

复制代码
// 泛型类
class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

// 泛型方法
class GenericMethod {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

// 泛型接口
interface Pair<K, V> {
    K getKey();
    V getValue();
}

// 有界类型参数
class NumberBox<T extends Number> {
    private T number;
    
    public NumberBox(T number) {
        this.number = number;
    }
    
    public double getDoubleValue() {
        return number.doubleValue();
    }
}

// 使用示例
public class GenericsDemo {
    public static void main(String[] args) {
        // 泛型类使用
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello");
        
        Box<Integer> intBox = new Box<>();
        intBox.set(123);
        
        // 泛型方法使用
        Integer[] intArray = {1, 2, 3};
        GenericMethod.printArray(intArray);
        
        // 有界类型
        NumberBox<Integer> nb = new NumberBox<>(100);
        System.out.println(nb.getDoubleValue());
    }
}

二、集合框架(5道)

11. ArrayList和LinkedList的区别?

答案:

  • ArrayList: 基于动态数组,随机访问快O(1),插入删除慢O(n)
  • LinkedList: 基于双向链表,插入删除快O(1),随机访问慢O(n)

示例代码:

复制代码
import java.util.*;

public class ListComparison {
    public static void main(String[] args) {
        // ArrayList - 适合查询多
        List<String> arrayList = new ArrayList<>();
        arrayList.add("A");
        arrayList.add("B");
        arrayList.add("C");
        System.out.println(arrayList.get(1)); // O(1)
        
        // LinkedList - 适合插入删除多
        List<String> linkedList = new LinkedList<>();
        linkedList.add("X");
        linkedList.add(0, "Y"); // 头部插入快
        linkedList.remove(0);
        
        // 性能测试
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            arrayList.add(0, "item"); // 头部插入慢
        }
        System.out.println("ArrayList: " + (System.currentTimeMillis() - start));
        
        start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            linkedList.add(0, "item"); // 头部插入快
        }
        System.out.println("LinkedList: " + (System.currentTimeMillis() - start));
    }
}
12. HashMap的底层实现原理?

答案:

  • Java 8之前:数组 + 链表
  • Java 8之后:数组 + 链表/红黑树(链表长度>8时转为红黑树)
  • 默认容量16,负载因子0.75
  • 扩容:容量翻倍,重新hash

示例代码:

复制代码
import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        
        // 基本操作
        map.put("Apple", 100);
        map.put("Banana", 200);
        map.put("Cherry", 300);
        
        // 获取
        System.out.println(map.get("Apple"));
        
        // 遍历
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // Java 8+ Lambda
        map.forEach((k, v) -> System.out.println(k + " = " + v));
        
        // 常用方法
        map.putIfAbsent("Date", 400);
        map.computeIfAbsent("Elderberry", k -> k.length());
        map.merge("Apple", 50, Integer::sum);
        
        // 自定义对象作为Key
        Map<Person, String> personMap = new HashMap<>();
        personMap.put(new Person("John", 25), "Engineer");
    }
}

// 自定义Key需要重写equals和hashCode
class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
13. HashMap、HashTable、ConcurrentHashMap的区别?

答案:

  • HashMap: 非线程安全,允许null key和value,性能最好
  • HashTable: 线程安全(synchronized),不允许null,已过时
  • ConcurrentHashMap: 线程安全(分段锁/CAS),性能好,不允许null

示例代码:

复制代码
import java.util.concurrent.*;
import java.util.*;

public class MapComparison {
    public static void main(String[] args) throws InterruptedException {
        // HashMap - 非线程安全
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put(null, 1); // 允许null
        hashMap.put("key", null);
        
        // ConcurrentHashMap - 线程安全
        Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        // concurrentMap.put(null, 1); // 不允许null,会抛异常
        
        // 多线程测试
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        for (int i = 0; i < 100; i++) {
            final int value = i;
            executor.submit(() -> {
                concurrentMap.put("key" + value, value);
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.SECONDS);
        
        System.out.println("Size: " + concurrentMap.size());
    }
}
14. HashSet和TreeSet的区别?

答案:

  • HashSet: 基于HashMap,无序,允许null,查找O(1)
  • TreeSet: 基于TreeMap(红黑树),有序,不允许null,查找O(log n)

示例代码:

复制代码
import java.util.*;

public class SetDemo {
    public static void main(String[] args) {
        // HashSet - 无序
        Set<Integer> hashSet = new HashSet<>();
        hashSet.add(5);
        hashSet.add(2);
        hashSet.add(8);
        hashSet.add(1);
        System.out.println("HashSet: " + hashSet); // 无序输出
        
        // TreeSet - 自然排序
        Set<Integer> treeSet = new TreeSet<>();
        treeSet.add(5);
        treeSet.add(2);
        treeSet.add(8);
        treeSet.add(1);
        System.out.println("TreeSet: " + treeSet); // [1, 2, 5, 8]
        
        // TreeSet自定义排序
        Set<String> customSet = new TreeSet<>(Comparator.reverseOrder());
        customSet.add("Apple");
        customSet.add("Banana");
        customSet.add("Cherry");
        System.out.println("Custom: " + customSet); // 倒序
        
        // TreeSet with custom object
        Set<Student> students = new TreeSet<>((s1, s2) -> s1.getScore() - s2.getScore());
        students.add(new Student("Alice", 85));
        students.add(new Student("Bob", 92));
        students.add(new Student("Charlie", 78));
        students.forEach(System.out::println);
    }
}

class Student {
    private String name;
    private int score;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    
    public int getScore() {
        return score;
    }
    
    @Override
    public String toString() {
        return name + ": " + score;
    }
}
15. Java集合的fail-fast和fail-safe机制?

答案:

  • fail-fast: 快速失败,迭代时检测到修改立即抛ConcurrentModificationException(如ArrayList)
  • fail-safe: 安全失败,在集合副本上迭代,不会抛异常(如ConcurrentHashMap)

示例代码:

复制代码
import java.util.*;
import java.util.concurrent.*;

public class FailFastDemo {
    public static void main(String[] args) {
        // Fail-fast示例
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        
        try {
            for (String item : list) {
                System.out.println(item);
                if ("B".equals(item)) {
                    list.remove(item); // 抛出ConcurrentModificationException
                }
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("Fail-fast detected!");
        }
        
        // 正确的删除方式
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if ("B".equals(item)) {
                iterator.remove(); // 使用迭代器删除
            }
        }
        
        // Fail-safe示例
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);
        
        // 不会抛异常
        for (String key : map.keySet()) {
            System.out.println(key);
            if ("B".equals(key)) {
                map.remove(key); // 安全删除
            }
        }
    }
}

三、多线程与并发(8道)

16. 创建线程的几种方式?

答案:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口(有返回值)
  4. 线程池

示例代码:

复制代码
import java.util.concurrent.*;

public class ThreadCreation {
    public static void main(String[] args) throws Exception {
        // 方式1: 继承Thread
        Thread thread1 = new MyThread();
        thread1.start();
        
        // 方式2: 实现Runnable
        Thread thread2 = new Thread(new MyRunnable());
        thread2.start();
        
        // 方式3: Lambda表达式
        Thread thread3 = new Thread(() -> {
            System.out.println("Lambda thread");
        });
        thread3.start();
        
        // 方式4: 实现Callable(有返回值)
        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();
        System.out.println("Callable result: " + futureTask.get());
        
        // 方式5: 线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.submit(() -> System.out.println("Thread pool task"));
        executor.shutdown();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("MyThread running");
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("MyRunnable running");
    }
}

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return "Callable completed";
    }
}
17. synchronized和Lock的区别?

答案:

  • synchronized: 关键字,自动释放锁,不可中断
  • Lock: 接口,手动释放,可中断,更灵活

示例代码:

复制代码
import java.util.concurrent.locks.*;

public class LockDemo {
    private int count = 0;
    private final Object lock = new Object();
    private final Lock reentrantLock = new ReentrantLock();
    
    // synchronized方法
    public synchronized void incrementSync() {
        count++;
    }
    
    // synchronized代码块
    public void incrementBlock() {
        synchronized (lock) {
            count++;
        }
    }
    
    // ReentrantLock
    public void incrementLock() {
        reentrantLock.lock();
        try {
            count++;
        } finally {
            reentrantLock.unlock(); // 必须手动释放
        }
    }
    
    // 尝试获取锁
    public void tryLockDemo() {
        if (reentrantLock.tryLock()) {
            try {
                System.out.println("Lock acquired");
                count++;
            } finally {
                reentrantLock.unlock();
            }
        } else {
            System.out.println("Lock not available");
        }
    }
    
    // 读写锁
    static class ReadWriteDemo {
        private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
        private int value = 0;
        
        public int read() {
            rwLock.readLock().lock();
            try {
                return value;
            } finally {
                rwLock.readLock().unlock();
            }
        }
        
        public void write(int newValue) {
            rwLock.writeLock().lock();
            try {
                value = newValue;
            } finally {
                rwLock.writeLock().unlock();
            }
        }
    }
}
18. volatile关键字的作用?

答案:

  1. 保证可见性:一个线程修改,其他线程立即可见
  2. 禁止指令重排序
  3. 不保证原子性

示例代码:

复制代码
public class VolatileDemo {
    private volatile boolean flag = false;
    private int count = 0;
    
    // 可见性示例
    public void visibilityDemo() {
        new Thread(() -> {
            while (!flag) {
                // 等待flag变为true
            }
            System.out.println("Flag is true!");
        }).start();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        flag = true; // volatile保证其他线程立即可见
    }
    
    // volatile不保证原子性
    public void atomicityDemo() throws InterruptedException {
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    count++; // 非原子操作
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("Count: " + count); // 可能小于10000
    }
    
    // 单例模式 - 双重检查锁定
    static class Singleton {
        private static volatile Singleton instance;
        
        private Singleton() {}
        
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}
19. 线程池的核心参数和工作原理?

答案: 核心参数:

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间
  • workQueue:任务队列
  • threadFactory:线程工厂
  • handler:拒绝策略

示例代码:

复制代码
import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,                      // 核心线程数
            5,                      // 最大线程数
            60L,                    // 空闲线程存活时间
            TimeUnit.SECONDS,       // 时间单位
            new LinkedBlockingQueue<>(10),  // 任务队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
        );
        
        // 提交任务
        for (int i = 0; i < 20; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " executed by " + 
                    Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 监控线程池
        System.out.println("Active threads: " + executor.getActiveCount());
        System.out.println("Pool size: " + executor.getPoolSize());
        System.out.println("Queue size: " + executor.getQueue().size());
        
        executor.shutdown();
        
        // 常用线程池
        ExecutorService fixedPool = Executors.newFixedThreadPool(5);
        ExecutorService cachedPool = Executors.newCachedThreadPool();
        ExecutorService singlePool = Executors.newSingleThreadExecutor();
        ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
        
        // 定时任务
        scheduledPool.scheduleAtFixedRate(() -> {
            System.out.println("Periodic task");
        }, 0, 5, TimeUnit.SECONDS);
    }
}
20. 死锁的条件和如何避免?

答案: 死锁四个必要条件:

  1. 互斥条件
  2. 请求和保持条件
  3. 不可剥夺条件
  4. 循环等待条件

避免方法:

  • 按顺序获取锁
  • 使用tryLock超时
  • 死锁检测

示例代码:

复制代码
import java.util.concurrent.locks.*;

public class DeadlockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    
    // 死锁示例
    public static void deadlockExample() {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: holding lock1");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 1: waiting for lock2");
                synchronized (lock2) {
                    System.out.println("Thread 1: acquired lock2");
                }
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: holding lock2");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 2: waiting for lock1");
                synchronized (lock1) {
                    System.out.println("Thread 2: acquired lock1");
                }
            }
        });
        
        t1.start();
        t2.start();
    }
    
    // 避免死锁 - 按顺序获取锁
    public static void avoidDeadlock() {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: holding lock1");
                synchronized (lock2) {
                    System.out.println("Thread 1: acquired lock2");
                }
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (lock1) { // 相同顺序
                System.out.println("Thread 2: holding lock1");
                synchronized (lock2) {
                    System.out.println("Thread 2: acquired lock2");
                }
            }
        });
        
        t1.start();
        t2.start();
    }
    
    // 使用tryLock避免死锁
    static class TryLockExample {
        private final Lock lock1 = new ReentrantLock();
        private final Lock lock2 = new ReentrantLock();
        
        public void transfer() {
            while (true) {
                if (lock1.tryLock()) {
                    try {
                        if (lock2.tryLock()) {
                            try {
                                // 执行转账操作
                                System.out.println("Transfer completed");
                                return;
                            } finally {
                                lock2.unlock();
                            }
                        }
                    } finally {
                        lock1.unlock();
                    }
                }
                // 重试
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
21. ThreadLocal的原理和应用场景?

答案: ThreadLocal为每个线程提供独立的变量副本,避免线程安全问题。

应用场景:

  • 数据库连接管理
  • Session管理
  • 日期格式化
  • 用户身份信息传递

示例代码:

复制代码
import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadLocalDemo {
    // ThreadLocal变量
    private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
    
    // 日期格式化(SimpleDateFormat非线程安全)
    private static ThreadLocal<SimpleDateFormat> dateFormat = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    
    public static void main(String[] args) {
        // 基本使用
        Thread t1 = new Thread(() -> {
            threadLocal.set(100);
            System.out.println("Thread 1: " + threadLocal.get());
            threadLocal.remove(); // 避免内存泄漏
        });
        
        Thread t2 = new Thread(() -> {
            threadLocal.set(200);
            System.out.println("Thread 2: " + threadLocal.get());
            threadLocal.remove();
        });
        
        t1.start();
        t2.start();
        
        // 日期格式化应用
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                String formatted = dateFormat.get().format(new Date());
                System.out.println(Thread.currentThread().getName() + ": " + formatted);
            }).start();
        }
    }
    
    // 用户上下文示例
    static class UserContext {
        private static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
        
        public static void setUser(User user) {
            userThreadLocal.set(user);
        }
        
        public static User getUser() {
            return userThreadLocal.get();
        }
        
        public static void clear() {
            userThreadLocal.remove();
        }
    }
    
    static class User {
        private String username;
        
        public User(String username) {
            this.username = username;
        }
        
        public String getUsername() {
            return username;
        }
    }
}
22. CountDownLatch、CyclicBarrier、Semaphore的区别?

答案:

  • CountDownLatch: 倒计时门闩,一次性,等待N个线程完成
  • CyclicBarrier: 循环栅栏,可重用,等待N个线程到达屏障点
  • Semaphore: 信号量,控制同时访问资源的线程数

示例代码:

复制代码
import java.util.concurrent.*;

public class ConcurrencyUtils {
    // CountDownLatch示例
    public static void countDownLatchDemo() throws InterruptedException {
        int threadCount = 5;
        CountDownLatch latch = new CountDownLatch(threadCount);
        
        for (int i = 0; i < threadCount; i++) {
            final int id = i;
            new Thread(() -> {
                System.out.println("Thread " + id + " working");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread " + id + " finished");
                latch.countDown();
            }).start();
        }
        
        latch.await(); // 等待所有线程完成
        System.out.println("All threads completed!");
    }
    
    // CyclicBarrier示例
    public static void cyclicBarrierDemo() {
        int threadCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("All threads reached barrier!");
        });
        
        for (int i = 0; i < threadCount; i++) {
            final int id = i;
            new Thread(() -> {
                try {
                    System.out.println("Thread " + id + " working");
                    Thread.sleep(1000);
                    System.out.println("Thread " + id + " waiting at barrier");
                    barrier.await(); // 等待其他线程
                    System.out.println("Thread " + id + " continues");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    
    // Semaphore示例
    public static void semaphoreDemo() {
        Semaphore semaphore = new Semaphore(3); // 最多3个线程同时访问
        
        for (int i = 0; i < 10; i++) {
            final int id = i;
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println("Thread " + id + " is accessing resource");
                    Thread.sleep(2000);
                    System.out.println("Thread " + id + " released resource");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                }
            }).start();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== CountDownLatch ===");
        countDownLatchDemo();
        
        Thread.sleep(3000);
        System.out.println("\n=== CyclicBarrier ===");
        cyclicBarrierDemo();
        
        Thread.sleep(3000);
        System.out.println("\n=== Semaphore ===");
        semaphoreDemo();
    }
}
23. CAS(Compare and Swap)原理?

答案: CAS是一种乐观锁机制,包含三个操作数:内存位置V、预期原值A、新值B。只有当V的值等于A时,才会将V更新为B。

示例代码:

复制代码
import java.util.concurrent.atomic.*;

public class CASDemo {
    private AtomicInteger count = new AtomicInteger(0);
    
    // 使用CAS实现原子操作
    public void increment() {
        count.incrementAndGet(); // 内部使用CAS
    }
    
    // 手动CAS操作
    public void casIncrement() {
        int oldValue, newValue;
        do {
            oldValue = count.get();
            newValue = oldValue + 1;
        } while (!count.compareAndSet(oldValue, newValue));
    }
    
    // 常用Atomic类
    public static void atomicDemo() {
        // AtomicInteger
        AtomicInteger atomicInt = new AtomicInteger(0);
        System.out.println(atomicInt.incrementAndGet()); // 1
        System.out.println(atomicInt.addAndGet(5));      // 6
        System.out.println(atomicInt.compareAndSet(6, 10)); // true
        
        // AtomicLong
        AtomicLong atomicLong = new AtomicLong(0);
        atomicLong.incrementAndGet();
        
        // AtomicBoolean
        AtomicBoolean flag = new AtomicBoolean(false);
        flag.compareAndSet(false, true);
        
        // AtomicReference
        AtomicReference<String> ref = new AtomicReference<>("Hello");
        ref.compareAndSet("Hello", "World");
        System.out.println(ref.get()); // World
        
        // LongAdder(高并发下性能更好)
        LongAdder adder = new LongAdder();
        adder.increment();
        adder.add(10);
        System.out.println(adder.sum());
    }
    
    // 自己实现简单的CAS
    static class SimpleCAS {
        private volatile int value;
        
        public synchronized boolean compareAndSwap(int expectedValue, int newValue) {
            if (value == expectedValue) {
                value = newValue;
                return true;
            }
            return false;
        }
        
        public int getValue() {
            return value;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        CASDemo demo = new CASDemo();
        
        // 并发测试
        Thread[] threads = new Thread[100];
        for (int i = 0; i < 100; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    demo.increment();
                }
            });
            threads[i].start();
        }
        
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("Count: " + demo.count.get()); // 10000
        
        atomicDemo();
    }
}

四、Spring框架(4道)

24. Spring IoC容器的原理?

答案: IoC(控制反转)将对象的创建和依赖关系的管理交给Spring容器。通过依赖注入(DI)实现。

示例代码:

复制代码
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.*;

// 配置类
@Configuration
@ComponentScan("com.example")
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

// Service类
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    // 构造器注入(推荐)
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User findById(Long id) {
        return userRepository.findById(id);
    }
}

// Repository类
@Repository
public class UserRepository {
    public User findById(Long id) {
        // 数据库查询逻辑
        return new User(id, "John");
    }
}

// 实体类
class User {
    private Long id;
    private String name;
    
    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }
    
    // getters and setters
}

// 使用
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        User user = userService.findById(1L);
        System.out.println(user.getName());
    }
}
25. Spring AOP的原理和应用?

答案: AOP(面向切面编程)通过动态代理实现横切关注点的分离。Spring AOP使用JDK动态代理或CGLIB代理。

示例代码:

复制代码
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.*;
import org.springframework.stereotype.Component;

// 切面类
@Aspect
@Component
public class LoggingAspect {
    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}
    
    // 前置通知
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
    
    // 后置通知
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }
    
    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("Method returned: " + result);
    }
    
    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        System.out.println("Method threw: " + error);
    }
    
    // 环绕通知
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        System.out.println("Around before: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed(); // 执行目标方法
        System.out.println("Around after: " + joinPoint.getSignature().getName());
        
        long elapsedTime = System.currentTimeMillis() - start;
        System.out.println("Execution time: " + elapsedTime + "ms");
        
        return result;
    }
}

// 业务类
@Service
public class OrderService {
    public Order createOrder(String product, double price) {
        System.out.println("Creating order...");
        return new Order(product, price);
    }
}

class Order {
    private String product;
    private double price;
    
    public Order(String product, double price) {
        this.product = product;
        this.price = price;
    }
}
26. Spring Bean的生命周期?

答案:

  1. 实例化(Instantiation)
  2. 属性赋值(Populate)
  3. 初始化(Initialization)
  4. 使用
  5. 销毁(Destruction)

示例代码:

复制代码
import org.springframework.beans.factory.*;
import org.springframework.context.*;
import org.springframework.stereotype.Component;
import javax.annotation.*;

@Component
public class LifeCycleBean implements InitializingBean, DisposableBean, 
        BeanNameAware, BeanFactoryAware, ApplicationContextAware {
    
    private String name;
    
    // 1. 构造器
    public LifeCycleBean() {
        System.out.println("1. Constructor called");
    }
    
    // 2. 属性注入
    @Autowired
    public void setName(String name) {
        this.name = name;
        System.out.println("2. Properties set");
    }
    
    // 3. BeanNameAware
    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware: " + name);
    }
    
    // 4. BeanFactoryAware
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4. BeanFactoryAware called");
    }
    
    // 5. ApplicationContextAware
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        System.out.println("5. ApplicationContextAware called");
    }
    
    // 6. @PostConstruct
    @PostConstruct
    public void postConstruct() {
        System.out.println("6. @PostConstruct called");
    }
    
    // 7. InitializingBean
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("7. InitializingBean.afterPropertiesSet called");
    }
    
    // 8. 自定义初始化方法
    @Bean(initMethod = "customInit")
    public void customInit() {
        System.out.println("8. Custom init method called");
    }
    
    // 9. @PreDestroy
    @PreDestroy
    public void preDestroy() {
        System.out.println("9. @PreDestroy called");
    }
    
    // 10. DisposableBean
    @Override
    public void destroy() throws Exception {
        System.out.println("10. DisposableBean.destroy called");
    }
}
27. Spring的事务管理?

答案: Spring支持编程式和声明式事务管理。声明式事务通过@Transactional注解实现。

事务传播行为:

  • REQUIRED(默认):有事务则加入,无则创建
  • REQUIRES_NEW:总是创建新事务
  • NESTED:嵌套事务
  • SUPPORTS:有事务则加入,无则非事务
  • NOT_SUPPORTED:非事务执行
  • NEVER:不能有事务
  • MANDATORY:必须有事务

示例代码:

复制代码
import org.springframework.transaction.annotation.*;
import org.springframework.stereotype.Service;

@Service
public class TransactionService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private PaymentRepository paymentRepository;
    
    // 声明式事务
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        paymentRepository.createPayment(order.getId());
        // 如果抛异常,事务会回滚
    }
    
    // 配置事务属性
    @Transactional(
        propagation = Propagation.REQUIRED,
        isolation = Isolation.READ_COMMITTED,
        timeout = 30,
        rollbackFor = Exception.class
    )
    public void complexTransaction() {
        // 业务逻辑
    }
    
    // 只读事务
    @Transactional(readOnly = true)
    public List<Order> findAllOrders() {
        return orderRepository.findAll();
    }
    
    // 事务传播
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void independentTransaction() {
        // 总是在新事务中执行
    }
    
    // 编程式事务
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void programmaticTransaction() {
        transactionTemplate.execute(status -> {
            try {
                orderRepository.save(new Order());
                paymentRepository.createPayment(1L);
                return null;
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

// 事务配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}

五、JVM与性能优化(3道)

28. JVM内存结构?

答案: JVM内存分为:

  1. 堆(Heap) : 存放对象实例,GC主要区域
    • 年轻代(Young Generation):Eden + 2个Survivor
    • 老年代(Old Generation)
  2. 方法区(Method Area/Metaspace): 类信息、常量池、静态变量
  3. 栈(Stack): 线程私有,存放局部变量、方法调用
  4. 程序计数器(PC Register): 当前线程执行字节码的行号
  5. 本地方法栈(Native Method Stack): native方法

示例代码:

复制代码
public class JVMMemoryDemo {
    // 类变量 - 方法区
    private static int staticVar = 100;
    
    // 实例变量 - 堆
    private int instanceVar = 200;
    
    // 常量池 - 方法区
    private static final String CONSTANT = "Hello";
    
    public void method() {
        // 局部变量 - 栈
        int localVar = 300;
        
        // 对象 - 堆
        Object obj = new Object();
        
        // 数组 - 堆
        int[] array = new int[10];
    }
    
    // 查看JVM内存使用
    public static void printMemoryInfo() {
        Runtime runtime = Runtime.getRuntime();
        
        long maxMemory = runtime.maxMemory(); // 最大内存
        long totalMemory = runtime.totalMemory(); // 总内存
        long freeMemory = runtime.freeMemory(); // 空闲内存
        
        System.out.println("Max Memory: " + maxMemory / 1024 / 1024 + "MB");
        System.out.println("Total Memory: " + totalMemory / 1024 / 1024 + "MB");
        System.out.println("Free Memory: " + freeMemory / 1024 / 1024 + "MB");
        System.out.println("Used Memory: " + (totalMemory - freeMemory) / 1024 / 1024 + "MB");
    }
    
    public static void main(String[] args) {
        printMemoryInfo();
        
        // JVM参数示例
        // -Xms512m 初始堆大小
        // -Xmx1024m 最大堆大小
        // -Xss256k 线程栈大小
        // -XX:MetaspaceSize=128m 元空间初始大小
        // -XX:MaxMetaspaceSize=256m 元空间最大大小
    }
}
29. 垃圾回收算法和垃圾收集器?

答案: GC算法:

  1. 标记-清除(Mark-Sweep): 标记存活对象,清除未标记对象
  2. 标记-复制(Mark-Copy): 复制存活对象到另一区域
  3. 标记-整理(Mark-Compact): 标记后整理,移动存活对象

垃圾收集器:

  • Serial GC: 单线程
  • Parallel GC: 多线程,吞吐量优先
  • CMS: 并发标记清除,低延迟
  • G1 GC: 分区收集,可预测停顿
  • ZGC: 低延迟(<10ms)

示例代码:

复制代码
public class GCDemo {
    // 触发GC
    public static void triggerGC() {
        System.gc(); // 建议JVM执行GC
        // 注意:System.gc()只是建议,JVM可能不执行
    }
    
    // 对象可达性分析
    static class GCRootDemo {
        private static Object staticReference; // GC Root
        
        public void method() {
            Object localReference = new Object(); // GC Root(活跃)
            
            new Thread(() -> {
                Object threadReference = new Object(); // GC Root(线程)
            }).start();
        }
    }
    
    // 观察GC
    public static void observeGC() {
        List<byte[]> list = new ArrayList<>();
        
        try {
            while (true) {
                // 分配1MB内存
                byte[] array = new byte[1024 * 1024];
                list.add(array);
                
                // 每100次输出一次
                if (list.size() % 100 == 0) {
                    System.out.println("Allocated: " + list.size() + "MB");
                }
            }
        } catch (OutOfMemoryError e) {
            System.out.println("Out of memory!");
        }
    }
    
    // 引用类型与GC
    public static void referenceTypes() {
        // 强引用
        Object strong = new Object();
        
        // 软引用 - 内存不足时回收
        SoftReference<Object> soft = new SoftReference<>(new Object());
        
        // 弱引用 - GC时回收
        WeakReference<Object> weak = new WeakReference<>(new Object());
        
        // 虚引用 - 无法获取对象
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        PhantomReference<Object> phantom = new PhantomReference<>(new Object(), queue);
        
        // 触发GC
        System.gc();
        
        System.out.println("Soft: " + soft.get());
        System.out.println("Weak: " + weak.get());
        System.out.println("Phantom: " + phantom.get());
    }
    
    public static void main(String[] args) {
        referenceTypes();
        
        // JVM GC参数
        // -XX:+UseSerialGC 使用Serial GC
        // -XX:+UseParallelGC 使用Parallel GC
        // -XX:+UseConcMarkSweepGC 使用CMS GC
        // -XX:+UseG1GC 使用G1 GC
        // -XX:+PrintGCDetails 打印GC详情
    }
}
30. 如何进行Java性能调优?

答案: 调优步骤:

  1. 性能监控(JConsole、VisualVM、JProfiler)
  2. 代码分析(找出热点代码)
  3. 内存优化(减少对象创建、避免内存泄漏)
  4. 并发优化(合理使用线程池)
  5. JVM参数调优

示例代码:

复制代码
import java.util.concurrent.*;

public class PerformanceOptimization {
    // 1. 避免频繁创建对象
    // 不好的做法
    public String badConcat() {
        String result = "";
        for (int i = 0; i < 10000; i++) {
            result += i; // 每次都创建新String对象
        }
        return result;
    }
    
    // 好的做法
    public String goodConcat() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            sb.append(i);
        }
        return sb.toString();
    }
    
    // 2. 使用对象池
    static class ObjectPool<T> {
        private final Queue<T> pool = new ConcurrentLinkedQueue<>();
        private final Supplier<T> factory;
        
        public ObjectPool(Supplier<T> factory, int initialSize) {
            this.factory = factory;
            for (int i = 0; i < initialSize; i++) {
                pool.offer(factory.get());
            }
        }
        
        public T borrow() {
            T object = pool.poll();
            return object != null ? object : factory.get();
        }
        
        public void returnObject(T object) {
            pool.offer(object);
        }
    }
    
    // 3. 延迟初始化
    static class LazyInit {
        private static class Holder {
            static final ExpensiveObject INSTANCE = new ExpensiveObject();
        }
        
        public static ExpensiveObject getInstance() {
            return Holder.INSTANCE;
        }
    }
    
    static class ExpensiveObject {
        public ExpensiveObject() {
            // 昂贵的初始化操作
        }
    }
    
    // 4. 缓存常用数据
    static class Cache {
        private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
        
        public Object get(String key) {
            return cache.computeIfAbsent(key, k -> {
                // 从数据库加载
                return loadFromDatabase(k);
            });
        }
        
        private Object loadFromDatabase(String key) {
            // 数据库查询
            return new Object();
        }
    }
    
    // 5. 批处理
    public void batchProcess(List<Integer> ids) {
        // 不好:逐个处理
        // for (Integer id : ids) {
        //     processOne(id);
        // }
        
        // 好:批量处理
        processBatch(ids);
    }
    
    private void processBatch(List<Integer> ids) {
        // 批量处理逻辑
    }
    
    // 6. 异步处理
    private ExecutorService executor = Executors.newFixedThreadPool(10);
    
    public CompletableFuture<String> asyncProcess(String data) {
        return CompletableFuture.supplyAsync(() -> {
            // 耗时操作
            return processData(data);
        }, executor);
    }
    
    private String processData(String data) {
        return data.toUpperCase();
    }
    
    // 性能测试工具
    public static void benchmark(Runnable task, String name) {
        long start = System.nanoTime();
        task.run();
        long end = System.nanoTime();
        System.out.println(name + " took: " + (end - start) / 1_000_000 + "ms");
    }
    
    public static void main(String[] args) {
        PerformanceOptimization opt = new PerformanceOptimization();
        
        benchmark(opt::badConcat, "Bad concat");
        benchmark(opt::goodConcat, "Good concat");
    }
}

// JVM调优参数示例
// -Xms2g -Xmx2g 设置堆大小
// -XX:NewRatio=2 年轻代与老年代比例
// -XX:SurvivorRatio=8 Eden与Survivor比例
// -XX:+UseG1GC 使用G1垃圾收集器
// -XX:MaxGCPauseMillis=200 最大GC停顿时间
// -XX:+HeapDumpOnOutOfMemoryError 内存溢出时dump

⚛️ React 面试题(30道)

一、React基础(10道)

31. React是什么?它的主要特点是什么?

答案: React是用于构建用户界面的JavaScript库,主要特点:

  1. 声明式编程:描述UI应该是什么样子
  2. 组件化:构建可复用的UI组件
  3. 虚拟DOM:提高渲染性能
  4. 单向数据流:数据从父组件流向子组件
  5. Learn Once, Write Anywhere:可用于Web、移动端等

示例代码:

复制代码
import React from 'react';

// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Counter 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>
    );
  }
}

// App组件
function App() {
  return (
    <div>
      <Welcome name="React" />
      <Counter />
    </div>
  );
}

export default App;
32. JSX是什么?为什么使用JSX?

答案: JSX是JavaScript XML的缩写,是React的语法扩展。它让我们可以在JavaScript中编写HTML结构。

优点:

  • 直观、易读
  • 防止XSS攻击(自动转义)
  • 支持JavaScript表达式
  • 编译时错误检查

示例代码:

复制代码
// JSX语法
const element = <h1>Hello World</h1>;

// 等价于
const element = React.createElement('h1', null, 'Hello World');

// JSX中的表达式
function Greeting({ name, age }) {
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>You are {age} years old</p>
      {age >= 18 ? <p>Adult</p> : <p>Minor</p>}
    </div>
  );
}

// JSX属性
function Image() {
  const url = 'https://example.com/image.jpg';
  const altText = 'Description';
  
  return (
    <img 
      src={url} 
      alt={altText}
      className="responsive-image"
      style={{ width: '100%', borderRadius: '8px' }}
    />
  );
}

// JSX子元素
function List() {
  const items = ['Apple', 'Banana', 'Cherry'];
  
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

// JSX片段
function Fragment() {
  return (
    <>
      <h1>Title</h1>
      <p>Paragraph</p>
    </>
  );
}
33. 虚拟DOM是什么?它是如何工作的?

答案: 虚拟DOM是真实DOM的轻量级JavaScript对象表示。React通过diff算法比较新旧虚拟DOM,只更新变化的部分。

工作流程:

  1. 状态改变时创建新的虚拟DOM树
  2. 对比新旧虚拟DOM(diff算法)
  3. 计算最小更新
  4. 批量更新真实DOM

示例代码:

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

function VirtualDOMDemo() {
  const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
  
  // 添加项目 - 只会更新新增的DOM节点
  const addItem = () => {
    setItems([...items, `Item ${items.length + 1}`]);
  };
  
  // 删除项目 - 只会删除对应的DOM节点
  const removeItem = (index) => {
    setItems(items.filter((_, i) => i !== index));
  };
  
  return (
    <div>
      <button onClick={addItem}>Add Item</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {item}
            <button onClick={() => removeItem(index)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

// Key的重要性
function KeyExample() {
  const [users, setUsers] = useState([
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]);
  
  // 不好:使用index作为key
  return (
    <ul>
      {users.map((user, index) => (
        <li key={index}>{user.name}</li>
      ))}
    </ul>
  );
  
  // 好:使用唯一ID作为key
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default VirtualDOMDemo;
34. React组件的生命周期有哪些?

答案: 类组件生命周期:

  • 挂载:constructor → getDerivedStateFromProps → render → componentDidMount
  • 更新:getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate
  • 卸载:componentWillUnmount

函数组件:使用useEffect Hook替代

示例代码:

复制代码
import React, { Component, useEffect, useState } from 'react';

// 类组件生命周期
class LifecycleClass extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    console.log('1. constructor');
  }
  
  static getDerivedStateFromProps(props, state) {
    console.log('2. getDerivedStateFromProps');
    return null;
  }
  
  componentDidMount() {
    console.log('4. componentDidMount');
    // API调用、订阅、定时器
  }
  
  shouldComponentUpdate(nextProps, nextState) {
    console.log('5. shouldComponentUpdate');
    return true; // 返回false阻止更新
  }
  
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('7. getSnapshotBeforeUpdate');
    return null;
  }
  
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('8. componentDidUpdate');
  }
  
  componentWillUnmount() {
    console.log('9. componentWillUnmount');
    // 清理:取消订阅、清除定时器
  }
  
  render() {
    console.log('3/6. render');
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Increment
        </button>
      </div>
    );
  }
}

// 函数组件 - useEffect模拟生命周期
function LifecycleFunction() {
  const [count, setCount] = useState(0);
  
  // componentDidMount
  useEffect(() => {
    console.log('Component mounted');
  }, []);
  
  // componentDidUpdate(count变化时)
  useEffect(() => {
    console.log('Count updated:', count);
  }, [count]);
  
  // componentWillUnmount
  useEffect(() => {
    return () => {
      console.log('Component will unmount');
    };
  }, []);
  
  // 组合:监听多个状态
  useEffect(() => {
    console.log('Component mounted or updated');
    
    return () => {
      console.log('Cleanup');
    };
  });
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export { LifecycleClass, LifecycleFunction };
35. 受控组件和非受控组件的区别?

答案:

  • 受控组件:表单数据由React组件的state管理
  • 非受控组件:表单数据由DOM自身管理,使用ref获取

示例代码:

复制代码
import React, { useState, useRef } from 'react';

// 受控组件
function ControlledForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: ''
  });
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Submitted:', formData);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="username"
        value={formData.username}
        onChange={handleChange}
        placeholder="Username"
      />
      <input
        type="email"
        name="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="Email"
      />
      <input
        type="password"
        name="password"
        value={formData.password}
        onChange={handleChange}
        placeholder="Password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

// 非受控组件
function UncontrolledForm() {
  const usernameRef = useRef();
  const emailRef = useRef();
  const passwordRef = useRef();
  
  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = {
      username: usernameRef.current.value,
      email: emailRef.current.value,
      password: passwordRef.current.value
    };
    console.log('Submitted:', formData);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        ref={usernameRef}
        defaultValue=""
        placeholder="Username"
      />
      <input
        type="email"
        ref={emailRef}
        defaultValue=""
        placeholder="Email"
      />
      <input
        type="password"
        ref={passwordRef}
        defaultValue=""
        placeholder="Password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

// 文件上传(非受控)
function FileUpload() {
  const fileInputRef = useRef();
  
  const handleUpload = () => {
    const file = fileInputRef.current.files[0];
    if (file) {
      console.log('Uploading:', file.name);
      // 上传逻辑
    }
  };
  
  return (
    <div>
      <input type="file" ref={fileInputRef} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}

export { ControlledForm, UncontrolledForm, FileUpload };
36. props和state的区别?

答案:

  • props: 父组件传递给子组件的数据,只读,不可修改
  • state: 组件内部管理的数据,可变,通过setState/useState修改

示例代码:

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

// Props示例
function ChildComponent({ name, age, onUpdate }) {
  // props是只读的
  // name = 'New Name'; // 错误!
  
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <button onClick={() => onUpdate('Updated')}>
        Update Parent
      </button>
    </div>
  );
}

// State示例
function ParentComponent() {
  const [message, setMessage] = useState('Initial message');
  const [user, setUser] = useState({ name: 'John', age: 25 });
  
  const handleUpdate = (newMessage) => {
    setMessage(newMessage);
  };
  
  const updateAge = () => {
    setUser(prev => ({
      ...prev,
      age: prev.age + 1
    }));
  };
  
  return (
    <div>
      <p>Message: {message}</p>
      <ChildComponent 
        name={user.name}
        age={user.age}
        onUpdate={handleUpdate}
      />
      <button onClick={updateAge}>Increment Age</button>
    </div>
  );
}

// Props默认值
function Button({ text = 'Click Me', variant = 'primary', onClick }) {
  return (
    <button 
      className={`btn btn-${variant}`}
      onClick={onClick}
    >
      {text}
    </button>
  );
}

// Props类型检查(使用PropTypes)
import PropTypes from 'prop-types';

function UserCard({ user, onEdit }) {
  return (
    <div>
      <h3>{user.name}</h3>
      <p>Age: {user.age}</p>
      <button onClick={() => onEdit(user.id)}>Edit</button>
    </div>
  );
}

UserCard.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.number.isRequired,
    name: PropTypes.string.isRequired,
    age: PropTypes.number
  }).isRequired,
  onEdit: PropTypes.func.isRequired
};

export default ParentComponent;
37. 事件处理在React中是如何工作的?

答案: React使用合成事件(SyntheticEvent)包装原生事件,提供跨浏览器一致性。

特点:

  • 事件委托到根节点
  • 自动绑定this(箭头函数或bind)
  • 阻止默认行为需要显式调用preventDefault

示例代码:

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

function EventHandling() {
  const [clicks, setClicks] = useState(0);
  
  // 方式1:箭头函数(推荐)
  const handleClick1 = () => {
    setClicks(clicks + 1);
  };
  
  // 方式2:直接在JSX中使用箭头函数
  // 注意:每次渲染都会创建新函数
  
  // 方式3:传递参数
  const handleClickWithParam = (id) => {
    console.log('Clicked item:', id);
  };
  
  // 阻止默认行为
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted');
  };
  
  // 阻止事件冒泡
  const handleInnerClick = (e) => {
    e.stopPropagation();
    console.log('Inner clicked');
  };
  
  const handleOuterClick = () => {
    console.log('Outer clicked');
  };
  
  // 获取事件对象
  const handleInput = (e) => {
    console.log('Value:', e.target.value);
    console.log('Event type:', e.type);
    console.log('Native event:', e.nativeEvent);
  };
  
  return (
    <div>
      {/* 方式1 */}
      <button onClick={handleClick1}>
        Clicks: {clicks}
      </button>
      
      {/* 方式2 */}
      <button onClick={() => setClicks(clicks + 1)}>
        Increment
      </button>
      
      {/* 传递参数 */}
      <button onClick={() => handleClickWithParam(123)}>
        Click with ID
      </button>
      
      {/* 阻止默认行为 */}
      <form onSubmit={handleSubmit}>
        <button type="submit">Submit</button>
      </form>
      
      {/* 事件冒泡 */}
      <div onClick={handleOuterClick} style={{ padding: '20px', background: '#eee' }}>
        Outer
        <button onClick={handleInnerClick}>Inner</button>
      </div>
      
      {/* 各种事件 */}
      <input
        type="text"
        onChange={handleInput}
        onFocus={() => console.log('Focused')}
        onBlur={() => console.log('Blurred')}
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            console.log('Enter pressed');
          }
        }}
      />
      
      {/* 鼠标事件 */}
      <div
        onMouseEnter={() => console.log('Mouse entered')}
        onMouseLeave={() => console.log('Mouse left')}
        onMouseMove={(e) => console.log('Mouse at:', e.clientX, e.clientY)}
      >
        Hover me
      </div>
    </div>
  );
}

// 类组件中的事件处理
class ClassEventHandling extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    // 方式3:构造器中bind
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }
  
  // 方式4:类属性(箭头函数)
  handleClick2 = () => {
    this.setState({ count: this.state.count + 1 });
  }
  
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          Count: {this.state.count}
        </button>
        <button onClick={this.handleClick2}>
          Increment
        </button>
      </div>
    );
  }
}

export default EventHandling;
38. 如何在React中进行条件渲染?

答案: React支持多种条件渲染方式:if语句、三元运算符、逻辑与&&、switch语句等。

示例代码:

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

function ConditionalRendering() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [userRole, setUserRole] = useState('guest');
  const [items, setItems] = useState([]);
  
  // 方式1:if-else语句
  function renderGreeting() {
    if (isLoggedIn) {
      return <h1>Welcome back!</h1>;
    } else {
      return <h1>Please sign in</h1>;
    }
  }
  
  // 方式2:三元运算符
  const greeting = (
    <div>
      {isLoggedIn ? (
        <h1>Welcome back!</h1>
      ) : (
        <h1>Please sign in</h1>
      )}
    </div>
  );
  
  // 方式3:逻辑与 &&
  const notification = (
    <div>
      {isLoggedIn && <p>You have new messages</p>}
      {!isLoggedIn && <p>Please log in to see messages</p>}
    </div>
  );
  
  // 方式4:立即执行函数
  const complexCondition = (
    <div>
      {(() => {
        if (userRole === 'admin') {
          return <AdminPanel />;
        } else if (userRole === 'user') {
          return <UserPanel />;
        } else {
          return <GuestPanel />;
        }
      })()}
    </div>
  );
  
  // 方式5:switch语句
  function renderByRole() {
    switch (userRole) {
      case 'admin':
        return <AdminPanel />;
      case 'user':
        return <UserPanel />;
      case 'guest':
      default:
        return <GuestPanel />;
    }
  }
  
  // 方式6:元素变量
  let content;
  if (isLoggedIn) {
    content = <Dashboard />;
  } else {
    content = <LoginForm />;
  }
  
  // 方式7:空列表检查
  const listView = (
    <div>
      {items.length > 0 ? (
        <ul>
          {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
      ) : (
        <p>No items found</p>
      )}
    </div>
  );
  
  // 方式8:多条件
  const multiCondition = (
    <div>
      {isLoggedIn && userRole === 'admin' && <AdminSettings />}
    </div>
  );
  
  // 方式9:防止渲染
  if (!isLoggedIn) {
    return null; // 或 return <div>Loading...</div>
  }
  
  return (
    <div>
      {renderGreeting()}
      {greeting}
      {notification}
      {complexCondition}
      {renderByRole()}
      {content}
      {listView}
      {multiCondition}
      
      <button onClick={() => setIsLoggedIn(!isLoggedIn)}>
        Toggle Login
      </button>
      <select onChange={(e) => setUserRole(e.target.value)} value={userRole}>
        <option value="guest">Guest</option>
        <option value="user">User</option>
        <option value="admin">Admin</option>
      </select>
    </div>
  );
}

// 辅助组件
const AdminPanel = () => <div>Admin Panel</div>;
const UserPanel = () => <div>User Panel</div>;
const GuestPanel = () => <div>Guest Panel</div>;
const Dashboard = () => <div>Dashboard</div>;
const LoginForm = () => <div>Login Form</div>;
const AdminSettings = () => <div>Admin Settings</div>;

export default ConditionalRendering;
39. React中的列表和Keys?

答案: 在React中渲染列表时,每个列表项需要唯一的key来帮助React识别哪些项改变了。

Key的要求:

  • 在兄弟节点中唯一
  • 稳定、可预测、不变
  • 不要使用index(除非列表静态且不会重排序)

示例代码:

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

function ListsAndKeys() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React', completed: false },
    { id: 2, text: 'Build a project', completed: false },
    { id: 3, text: 'Deploy', completed: true }
  ]);
  
  const [nextId, setNextId] = useState(4);
  
  // 添加待办事项
  const addTodo = (text) => {
    setTodos([...todos, { id: nextId, text, completed: false }]);
    setNextId(nextId + 1);
  };
  
  // 删除待办事项
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  // 切换完成状态
  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };
  
  return (
    <div>
      <h2>Todo List</h2>
      
      {/* 基本列表渲染 */}
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            <span style={{ 
              textDecoration: todo.completed ? 'line-through' : 'none' 
            }}>
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
      
      {/* 添加新项 */}
      <button onClick={() => addTodo('New task')}>Add Todo</button>
    </div>
  );
}

// 嵌套列表
function NestedList() {
  const categories = [
    {
      id: 1,
      name: 'Fruits',
      items: [
        { id: 11, name: 'Apple' },
        { id: 12, name: 'Banana' }
      ]
    },
    {
      id: 2,
      name: 'Vegetables',
      items: [
        { id: 21, name: 'Carrot' },
        { id: 22, name: 'Broccoli' }
      ]
    }
  ];
  
  return (
    <div>
      {categories.map(category => (
        <div key={category.id}>
          <h3>{category.name}</h3>
          <ul>
            {category.items.map(item => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

// 提取列表组件
function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <li>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={onToggle}
      />
      <span>{todo.text}</span>
      <button onClick={onDelete}>Delete</button>
    </li>
  );
}

function TodoListRefactored() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Task 1', completed: false },
    { id: 2, text: 'Task 2', completed: true }
  ]);
  
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={() => console.log('Toggle', todo.id)}
          onDelete={() => console.log('Delete', todo.id)}
        />
      ))}
    </ul>
  );
}

// 不好的做法:使用index作为key
function BadKeyExample() {
  const [items, setItems] = useState(['A', 'B', 'C']);
  
  // 不好:使用index
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>  // ❌ 问题:重新排序时会出错
      ))}
    </ul>
  );
}

export default ListsAndKeys;
40. React中的Fragments是什么?

答案: Fragments让你可以将子列表分组,而无需向DOM添加额外节点。

使用场景:

  • 返回多个元素
  • 避免额外的div包装
  • 表格行、列表项等

示例代码:

复制代码
import React from 'react';

// 方式1:React.Fragment
function LongForm() {
  return (
    <React.Fragment>
      <h1>Title</h1>
      <p>Paragraph 1</p>
      <p>Paragraph 2</p>
    </React.Fragment>
  );
}

// 方式2:短语法 <>...</>
function ShortForm() {
  return (
    <>
      <h1>Title</h1>
      <p>Paragraph 1</p>
      <p>Paragraph 2</p>
    </>
  );
}

// 带key的Fragment(不能使用短语法)
function ListWithFragments() {
  const items = [
    { id: 1, term: 'React', description: 'A JavaScript library' },
    { id: 2, term: 'Component', description: 'A reusable piece of UI' }
  ];
  
  return (
    <dl>
      {items.map(item => (
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

// 表格中使用Fragment
function TableRows() {
  return (
    <table>
      <tbody>
        <tr>
          <Columns />
        </tr>
      </tbody>
    </table>
  );
}

function Columns() {
  return (
    <>
      <td>Column 1</td>
      <td>Column 2</td>
      <td>Column 3</td>
    </>
  );
}

// 条件Fragment
function ConditionalFragment({ showDetails }) {
  return (
    <div>
      <h1>Title</h1>
      {showDetails && (
        <>
          <p>Detail 1</p>
          <p>Detail 2</p>
          <p>Detail 3</p>
        </>
      )}
    </div>
  );
}

export default ShortForm;

二、React Hooks(10道)

41. useState Hook的使用?

答案: useState是React中最基本的Hook,用于在函数组件中添加状态。

示例代码:

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

function UseStateExamples() {
  // 基本用法
  const [count, setCount] = useState(0);
  
  // 多个状态
  const [name, setName] = useState('John');
  const [age, setAge] = useState(25);
  
  // 对象状态
  const [user, setUser] = useState({
    name: 'Alice',
    email: 'alice@example.com',
    age: 30
  });
  
  // 数组状态
  const [items, setItems] = useState(['Apple', 'Banana']);
  
  // 布尔状态
  const [isLoading, setIsLoading] = useState(false);
  const [isVisible, setIsVisible] = useState(true);
  
  // 函数式更新(基于前一个状态)
  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };
  
  const decrement = () => {
    setCount(prevCount => prevCount - 1);
  };
  
  // 更新对象状态
  const updateUser = () => {
    setUser(prevUser => ({
      ...prevUser,
      age: prevUser.age + 1
    }));
  };
  
  // 更新数组状态
  const addItem = () => {
    setItems(prevItems => [...prevItems, `Item ${prevItems.length + 1}`]);
  };
  
  const removeItem = (index) => {
    setItems(prevItems => prevItems.filter((_, i) => i !== index));
  };
  
  // 延迟初始化(性能优化)
  const [expensiveValue, setExpensiveValue] = useState(() => {
    console.log('Computing expensive value...');
    return computeExpensiveValue();
  });
  
  function computeExpensiveValue() {
    // 昂贵的计算
    return Math.random() * 1000;
  }
  
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
      <button onClick={decrement}>Decrement (functional)</button>
      
      <h2>User Info</h2>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
      <p>Age: {user.age}</p>
      <button onClick={updateUser}>Increase Age</button>
      
      <h2>Items</h2>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {item}
            <button onClick={() => removeItem(index)}>Remove</button>
          </li>
        ))}
      </ul>
      <button onClick={addItem}>Add Item</button>
      
      <h2>Toggle</h2>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? 'Hide' : 'Show'}
      </button>
      {isVisible && <p>This content is visible</p>}
      
      <p>Expensive Value: {expensiveValue}</p>
    </div>
  );
}

// 表单管理
function FormWithState() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
    terms: false
  });
  
  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value
    }));
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted:', formData);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="username"
        value={formData.username}
        onChange={handleChange}
        placeholder="Username"
      />
      <input
        name="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="Email"
      />
      <input
        name="password"
        type="password"
        value={formData.password}
        onChange={handleChange}
        placeholder="Password"
      />
      <label>
        <input
          name="terms"
          type="checkbox"
          checked={formData.terms}
          onChange={handleChange}
        />
        Accept Terms
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

export default UseStateExamples;
42. useEffect Hook的使用?

答案: useEffect用于处理副作用,如数据获取、订阅、DOM操作等。

示例代码:

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

function UseEffectExamples() {
  const [count, setCount] = useState(0);
  const [data, setData] = useState(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);
  
  // 1. 每次渲染后执行
  useEffect(() => {
    console.log('Component rendered');
  });
  
  // 2. 仅在mount时执行(componentDidMount)
  useEffect(() => {
    console.log('Component mounted');
    document.title = 'React App';
  }, []);
  
  // 3. 依赖特定值(count变化时执行)
  useEffect(() => {
    console.log('Count changed:', count);
    document.title = `Count: ${count}`;
  }, [count]);
  
  // 4. 清理函数(componentWillUnmount)
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('Timer tick');
    }, 1000);
    
    // 清理函数
    return () => {
      clearInterval(timer);
      console.log('Timer cleaned up');
    };
  }, []);
  
  // 5. 数据获取
  useEffect(() => {
    let isMounted = true;
    
    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data');
        const result = await response.json();
        
        if (isMounted) {
          setData(result);
        }
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    }
    
    fetchData();
    
    return () => {
      isMounted = false;
    };
  }, []);
  
  // 6. 订阅/取消订阅
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    
    function handleOffline() {
      setIsOnline(false);
    }
    
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);
  
  // 7. 多个依赖
  useEffect(() => {
    console.log('Count or data changed');
  }, [count, data]);
  
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Status: {isOnline ? 'Online' : 'Offline'}</p>
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  );
}

// localStorage同步
function LocalStorageSync() {
  const [name, setName] = useState(() => {
    return localStorage.getItem('name') || '';
  });
  
  useEffect(() => {
    localStorage.setItem('name', name);
  }, [name]);
  
  return (
    <input
      value={name}
      onChange={(e) => setName(e.target.value)}
      placeholder="Enter name"
    />
  );
}

// 窗口大小监听
function WindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });
  
  useEffect(() => {
    function handleResize() {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }
    
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return (
    <div>
      <p>Width: {size.width}px</p>
      <p>Height: {size.height}px</p>
    </div>
  );
}

export { UseEffectExamples, LocalStorageSync, WindowSize };
43. useContext Hook的使用?

答案: useContext用于访问React Context,避免props drilling。

示例代码:

复制代码
import React, { createContext, useContext, useState } from 'react';

// 创建Context
const ThemeContext = createContext();
const UserContext = createContext();
const LanguageContext = createContext();

// Theme Provider
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// User Provider
function UserProvider({ children }) {
  const [user, setUser] = useState({ name: 'John', role: 'user' });
  
  const login = (userData) => {
    setUser(userData);
  };
  
  const logout = () => {
    setUser(null);
  };
  
  return (
    <UserContext.Provider value={{ user, login, logout }}>
      {children}
    </UserContext.Provider>
  );
}

// 使用Context
function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);
  
  return (
    <button
      onClick={toggleTheme}
      style={{
        background: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff'
      }}
    >
      Toggle Theme (Current: {theme})
    </button>
  );
}

function UserProfile() {
  const { user, logout } = useContext(UserContext);
  
  if (!user) {
    return <p>Please log in</p>;
  }
  
  return (
    <div>
      <h3>Welcome, {user.name}!</h3>
      <p>Role: {user.role}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

// 多个Context
function MultiContextComponent() {
  const { theme } = useContext(ThemeContext);
  const { user } = useContext(UserContext);
  
  return (
    <div style={{
      background: theme === 'light' ? '#f0f0f0' : '#222',
      color: theme === 'light' ? '#000' : '#fff',
      padding: '20px'
    }}>
      <p>Theme: {theme}</p>
      <p>User: {user?.name || 'Guest'}</p>
    </div>
  );
}

// 自定义Hook封装Context
function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within UserProvider');
  }
  return context;
}

// App
function App() {
  return (
    <ThemeProvider>
      <UserProvider>
        <div>
          <ThemedButton />
          <UserProfile />
          <MultiContextComponent />
        </div>
      </UserProvider>
    </ThemeProvider>
  );
}

export default App;
44. useReducer Hook的使用?

答案: useReducer是useState的替代方案,适合复杂的状态逻辑。

示例代码:

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

// 简单计数器
function counterReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    case 'RESET':
      return { count: 0 };
    case 'SET':
      return { count: action.payload };
    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });
  
  return (
    <div>
      <h2>Count: {state.count}</h2>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
      <button onClick={() => dispatch({ type: 'SET', payload: 100 })}>
        Set to 100
      </button>
    </div>
  );
}

// Todo列表
const todoReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, {
          id: Date.now(),
          text: action.payload,
          completed: false
        }]
      };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };
    case 'DELETE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload)
      };
    case 'SET_FILTER':
      return {
        ...state,
        filter: action.payload
      };
    default:
      return state;
  }
};

function TodoApp() {
  const initialState = {
    todos: [],
    filter: 'all'
  };
  
  const [state, dispatch] = useReducer(todoReducer, initialState);
  const [input, setInput] = React.useState('');
  
  const addTodo = () => {
    if (input.trim()) {
      dispatch({ type: 'ADD_TODO', payload: input });
      setInput('');
    }
  };
  
  const filteredTodos = state.todos.filter(todo => {
    if (state.filter === 'active') return !todo.completed;
    if (state.filter === 'completed') return todo.completed;
    return true;
  });
  
  return (
    <div>
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Add todo"
      />
      <button onClick={addTodo}>Add</button>
      
      <div>
        <button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'all' })}>
          All
        </button>
        <button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'active' })}>
          Active
        </button>
        <button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'completed' })}>
          Completed
        </button>
      </div>
      
      <ul>
        {filteredTodos.map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
            />
            <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
              {todo.text}
            </span>
            <button onClick={() => dispatch({ type: 'DELETE_TODO', payload: todo.id })}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

// 表单管理
const formReducer = (state, action) => {
  switch (action.type) {
    case 'UPDATE_FIELD':
      return {
        ...state,
        [action.field]: action.value
      };
    case 'RESET_FORM':
      return action.payload;
    default:
      return state;
  }
};

function Form() {
  const initialState = {
    username: '',
    email: '',
    password: ''
  };
  
  const [state, dispatch] = useReducer(formReducer, initialState);
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form data:', state);
    dispatch({ type: 'RESET_FORM', payload: initialState });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={state.username}
        onChange={(e) => dispatch({
          type: 'UPDATE_FIELD',
          field: 'username',
          value: e.target.value
        })}
        placeholder="Username"
      />
      <input
        value={state.email}
        onChange={(e) => dispatch({
          type: 'UPDATE_FIELD',
          field: 'email',
          value: e.target.value
        })}
        placeholder="Email"
      />
      <input
        type="password"
        value={state.password}
        onChange={(e) => dispatch({
          type: 'UPDATE_FIELD',
          field: 'password',
          value: e.target.value
        })}
        placeholder="Password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export { Counter, TodoApp, Form };
45. useRef Hook的使用?

答案: useRef用于访问DOM元素或保存可变值而不触发重新渲染。

示例代码:

复制代码
import React, { useRef, useState, useEffect } from 'react';

function UseRefExamples() {
  const inputRef = useRef(null);
  const countRef = useRef(0);
  const [renderCount, setRenderCount] = useState(0);
  
  // 1. 访问DOM元素
  const focusInput = () => {
    inputRef.current.focus();
  };
  
  const clearInput = () => {
    inputRef.current.value = '';
  };
  
  // 2. 保存可变值(不触发重新渲染)
  useEffect(() => {
    countRef.current = countRef.current + 1;
    console.log('Render count:', countRef.current);
  });
  
  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Enter text" />
      <button onClick={focusInput}>Focus Input</button>
      <button onClick={clearInput}>Clear Input</button>
      <button onClick={() => setRenderCount(renderCount + 1)}>
        Re-render
      </button>
      <p>Component rendered {countRef.current} times</p>
    </div>
  );
}

// 前一个值
function usePrevious(value) {
  const ref = useRef();
  
  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

function PreviousValue() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  
  return (
    <div>
      <p>Current: {count}</p>
      <p>Previous: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

// 计时器
function Timer() {
  const [time, setTime] = useState(0);
  const [isRunning, setIsRunning] = useState(false);
  const intervalRef = useRef(null);
  
  useEffect(() => {
    if (isRunning) {
      intervalRef.current = setInterval(() => {
        setTime(t => t + 1);
      }, 1000);
    } else {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    }
    
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, [isRunning]);
  
  return (
    <div>
      <p>Time: {time}s</p>
      <button onClick={() => setIsRunning(!isRunning)}>
        {isRunning ? 'Pause' : 'Start'}
      </button>
      <button onClick={() => setTime(0)}>Reset</button>
    </div>
  );
}

// 视频播放器
function VideoPlayer() {
  const videoRef = useRef(null);
  
  const play = () => {
    videoRef.current.play();
  };
  
  const pause = () => {
    videoRef.current.pause();
  };
  
  const seek = (time) => {
    videoRef.current.currentTime = time;
  };
  
  return (
    <div>
      <video ref={videoRef} width="400" src="video.mp4" />
      <button onClick={play}>Play</button>
      <button onClick={pause}>Pause</button>
      <button onClick={() => seek(0)}>Restart</button>
    </div>
  );
}

// 滚动到元素
function ScrollToElement() {
  const sectionRef = useRef(null);
  
  const scrollToSection = () => {
    sectionRef.current.scrollIntoView({ behavior: 'smooth' });
  };
  
  return (
    <div>
      <button onClick={scrollToSection}>Scroll to Section</button>
      <div style={{ height: '1000px' }}>Spacer</div>
      <section ref={sectionRef}>
        <h2>Target Section</h2>
      </section>
    </div>
  );
}

export { UseRefExamples, PreviousValue, Timer, VideoPlayer, ScrollToElement };
46. useMemo和useCallback的区别和使用?

答案:

  • useMemo: 缓存计算结果
  • useCallback: 缓存函数引用

两者都用于性能优化,避免不必要的重新计算或重新渲染。

示例代码:

复制代码
import React, { useState, useMemo, useCallback } from 'react';

// useMemo示例
function UseMemoExample() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);
  
  // 昂贵的计算 - 使用useMemo缓存
  const expensiveCalculation = useMemo(() => {
    console.log('Computing expensive value...');
    let result = 0;
    for (let i = 0; i < 1000000000; i++) {
      result += i;
    }
    return result;
  }, [count]); // 只在count变化时重新计算
  
  // 过滤列表 - 使用useMemo
  const filteredItems = useMemo(() => {
    console.log('Filtering items...');
    return items.filter(item => item.active);
  }, [items]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <p>Expensive result: {expensiveCalculation}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Filtered items: {filteredItems.length}</p>
    </div>
  );
}

// useCallback示例
const ChildComponent = React.memo(({ onButtonClick }) => {
  console.log('Child rendered');
  return <button onClick={onButtonClick}>Click Me</button>;
});

function UseCallbackExample() {
  const [count, setCount] = useState(0);
  const [otherState, setOtherState] = useState(0);
  
  // 不使用useCallback - 每次渲染都创建新函数
  const handleClickBad = () => {
    console.log('Clicked');
  };
  
  // 使用useCallback - 缓存函数引用
  const handleClickGood = useCallback(() => {
    console.log('Clicked with count:', count);
  }, [count]); // 只在count变化时创建新函数
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setOtherState(otherState + 1)}>
        Update Other State
      </button>
      <ChildComponent onButtonClick={handleClickGood} />
    </div>
  );
}

// 实际应用:搜索和排序
function SearchAndSort() {
  const [query, setQuery] = useState('');
  const [sortOrder, setSortOrder] = useState('asc');
  const [data] = useState([
    { id: 1, name: 'Alice', age: 25 },
    { id: 2, name: 'Bob', age: 30 },
    { id: 3, name: 'Charlie', age: 20 }
  ]);
  
  // 搜索过滤
  const filteredData = useMemo(() => {
    console.log('Filtering...');
    return data.filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    );
  }, [data, query]);
  
  // 排序
  const sortedData = useMemo(() => {
    console.log('Sorting...');
    return [...filteredData].sort((a, b) => {
      if (sortOrder === 'asc') {
        return a.age - b.age;
      }
      return b.age - a.age;
    });
  }, [filteredData, sortOrder]);
  
  // 缓存事件处理器
  const handleSearch = useCallback((e) => {
    setQuery(e.target.value);
  }, []);
  
  const handleSort = useCallback(() => {
    setSortOrder(prev => prev === 'asc' ? 'desc' : 'asc');
  }, []);
  
  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={handleSearch}
        placeholder="Search by name"
      />
      <button onClick={handleSort}>
        Sort by Age ({sortOrder})
      </button>
      <ul>
        {sortedData.map(item => (
          <li key={item.id}>
            {item.name} - {item.age}
          </li>
        ))}
      </ul>
    </div>
  );
}

// 何时使用
function WhenToUse() {
  const [count, setCount] = useState(0);
  
  // ❌ 不需要useMemo的情况
  const simpleValue = count * 2; // 简单计算,不需要缓存
  
  // ✅ 需要useMemo的情况
  const expensiveValue = useMemo(() => {
    // 昂贵的计算
    return heavyComputation(count);
  }, [count]);
  
  // ❌ 不需要useCallback的情况(没有作为props传递)
  const handleClick1 = () => {
    setCount(count + 1);
  };
  
  // ✅ 需要useCallback的情况(传递给被memo的子组件)
  const handleClick2 = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  
  return (
    <div>
      <p>Simple: {simpleValue}</p>
      <p>Expensive: {expensiveValue}</p>
      <button onClick={handleClick1}>Normal</button>
      <MemoizedChild onClick={handleClick2} />
    </div>
  );
}

function heavyComputation(n) {
  // 模拟昂贵计算
  return n;
}

const MemoizedChild = React.memo(({ onClick }) => {
  return <button onClick={onClick}>Memoized</button>;
});

export { UseMemoExample, UseCallbackExample, SearchAndSort, WhenToUse };
47. 自定义Hooks如何创建和使用?

答案: 自定义Hooks是以"use"开头的函数,用于复用状态逻辑。

示例代码:

复制代码
import { useState, useEffect, useCallback, useRef } from 'react';

// 1. useLocalStorage - 本地存储同步
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });
  
  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };
  
  return [storedValue, setValue];
}

// 使用示例
function LocalStorageExample() {
  const [name, setName] = useLocalStorage('name', 'Guest');
  
  return (
    <input
      value={name}
      onChange={(e) => setName(e.target.value)}
      placeholder="Enter name"
    />
  );
}

// 2. useFetch - 数据获取
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let isMounted = true;
    
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        const json = await response.json();
        
        if (isMounted) {
          setData(json);
          setError(null);
        }
      } catch (err) {
        if (isMounted) {
          setError(err.message);
        }
      } finally {
        if (isMounted) {
          setLoading(false);
        }
      }
    };
    
    fetchData();
    
    return () => {
      isMounted = false;
    };
  }, [url]);
  
  return { data, loading, error };
}

// 使用示例
function FetchExample() {
  const { data, loading, error } = useFetch('https://api.example.com/users');
  
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;
  
  return (
    <ul>
      {data?.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

// 3. useDebounce - 防抖
function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    
    return () => {
      clearTimeout(timer);
    };
  }, [value, delay]);
  
  return debouncedValue;
}

// 使用示例
function SearchWithDebounce() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearch = useDebounce(searchTerm, 500);
  
  useEffect(() => {
    if (debouncedSearch) {
      console.log('Searching for:', debouncedSearch);
      // 执行搜索API调用
    }
  }, [debouncedSearch]);
  
  return (
    <input
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="Search..."
    />
  );
}

// 4. useWindowSize - 窗口尺寸
function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });
  
  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }
    
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return windowSize;
}

// 使用示例
function ResponsiveComponent() {
  const { width } = useWindowSize();
  
  return (
    <div>
      <p>Window width: {width}px</p>
      {width < 768 ? <p>Mobile view</p> : <p>Desktop view</p>}
    </div>
  );
}

// 5. useToggle - 切换布尔值
function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  
  const toggle = useCallback(() => {
    setValue(v => !v);
  }, []);
  
  return [value, toggle, setValue];
}

// 使用示例
function ToggleExample() {
  const [isOn, toggle] = useToggle(false);
  
  return (
    <div>
      <p>Status: {isOn ? 'ON' : 'OFF'}</p>
      <button onClick={toggle}>Toggle</button>
    </div>
  );
}

// 6. useInterval - 定时器
function useInterval(callback, delay) {
  const savedCallback = useRef();
  
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);
  
  useEffect(() => {
    if (delay !== null) {
      const id = setInterval(() => {
        savedCallback.current();
      }, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

// 使用示例
function Clock() {
  const [time, setTime] = useState(new Date());
  
  useInterval(() => {
    setTime(new Date());
  }, 1000);
  
  return <p>{time.toLocaleTimeString()}</p>;
}

// 7. usePrevious - 前一个值
function usePrevious(value) {
  const ref = useRef();
  
  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

// 8. useOnClickOutside - 点击外部
function useOnClickOutside(ref, handler) {
  useEffect(() => {
    const listener = (event) => {
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };
    
    document.addEventListener('mousedown', listener);
    document.addEventListener('touchstart', listener);
    
    return () => {
      document.removeEventListener('mousedown', listener);
      document.removeEventListener('touchstart', listener);
    };
  }, [ref, handler]);
}

// 使用示例
function DropdownMenu() {
  const [isOpen, setIsOpen] = useState(false);
  const ref = useRef();
  
  useOnClickOutside(ref, () => setIsOpen(false));
  
  return (
    <div ref={ref}>
      <button onClick={() => setIsOpen(!isOpen)}>Menu</button>
      {isOpen && (
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
        </ul>
      )}
    </div>
  );
}

export {
  useLocalStorage,
  useFetch,
  useDebounce,
  useWindowSize,
  useToggle,
  useInterval,
  usePrevious,
  useOnClickOutside
};
48-50. 更多React Hooks问题

由于篇幅限制,这里简要列出剩余题目要点:

48. useLayoutEffect vs useEffect?

  • useLayoutEffect: 同步执行,DOM更新后、浏览器绘制前
  • useEffect: 异步执行,浏览器绘制后
  • 用于需要立即读取/修改DOM布局的场景

49. useImperativeHandle的使用?

  • 自定义ref暴露给父组件的实例值
  • 配合forwardRef使用
  • 隐藏组件内部实现细节

50. React 18新增Hooks?

  • useId: 生成唯一ID
  • useTransition: 标记低优先级更新
  • useDeferredValue: 延迟更新值
  • useSyncExternalStore: 订阅外部store
  • useInsertionEffect: CSS-in-JS库使用

三、React高级主题(10道)

51. React性能优化技巧?

答案:

  1. 使用React.memo避免不必要的重新渲染
  2. useMemo和useCallback缓存值和函数
  3. 代码分割(React.lazy和Suspense)
  4. 虚拟化长列表(react-window)
  5. 避免内联对象和函数
  6. 使用生产环境构建
  7. 使用key优化列表渲染

示例代码:

复制代码
import React, { lazy, Suspense, memo, useMemo } from 'react';

// 1. React.memo
const ExpensiveComponent = memo(({ data }) => {
  console.log('Rendering expensive component');
  return <div>{data}</div>;
});

// 2. 代码分割
const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

// 3. 虚拟化长列表
import { FixedSizeList } from 'react-window';

function VirtualList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>Item {items[index]}</div>
  );
  
  return (
    <FixedSizeList
      height={400}
      itemCount={items.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

// 4. 避免内联对象
// ❌ Bad
function Bad() {
  return <MyComponent style={{ margin: 10 }} />;
}

// ✅ Good
const style = { margin: 10 };
function Good() {
  return <MyComponent style={style} />;
}
52. React Router的使用?

答案: React Router是React的标准路由库,用于构建单页应用(SPA)。

核心概念:

  • BrowserRouter: 使用HTML5 history API
  • Routes/Route: 定义路由规则
  • Link/NavLink: 导航链接
  • useNavigate: 编程式导航
  • useParams: 获取URL参数
  • useLocation: 获取当前位置

示例代码:

复制代码
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  NavLink,
  useNavigate,
  useParams,
  useLocation,
  Navigate,
  Outlet
} from 'react-router-dom';
import { useState } from 'react';

// 1. 基本路由设置
function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/users">Users</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users" element={<Users />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

// 2. 动态路由
function UsersApp() {
  return (
    <Routes>
      <Route path="/users" element={<UserList />} />
      <Route path="/users/:userId" element={<UserProfile />} />
      <Route path="/users/:userId/posts/:postId" element={<PostDetail />} />
    </Routes>
  );
}

function UserProfile() {
  const { userId } = useParams();
  const navigate = useNavigate();
  
  return (
    <div>
      <h2>User Profile: {userId}</h2>
      <button onClick={() => navigate('/users')}>Back to List</button>
      <button onClick={() => navigate(-1)}>Go Back</button>
    </div>
  );
}

// 3. 嵌套路由
function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <nav>
        <Link to="overview">Overview</Link>
        <Link to="stats">Statistics</Link>
        <Link to="settings">Settings</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<DashboardLayout />}>
          <Route index element={<Overview />} />
          <Route path="overview" element={<Overview />} />
          <Route path="stats" element={<Statistics />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </div>
  );
}

function DashboardLayout() {
  return (
    <div>
      <aside>Sidebar</aside>
      <main>
        <Outlet /> {/* 子路由渲染位置 */}
      </main>
    </div>
  );
}

// 4. 受保护的路由
function ProtectedRoute({ children }) {
  const { user } = useAuth();
  const location = useLocation();
  
  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  
  return children;
}

function useAuth() {
  const [user] = useState(null); // 实际应从context获取
  return { user };
}

function AppWithAuth() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/public" element={<PublicPage />} />
      <Route
        path="/dashboard"
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        }
      />
    </Routes>
  );
}

// 5. NavLink - 活动链接样式
function Navigation() {
  return (
    <nav>
      <NavLink
        to="/"
        className={({ isActive }) => isActive ? 'active' : ''}
        style={({ isActive }) => ({
          color: isActive ? 'red' : 'black'
        })}
      >
        Home
      </NavLink>
    </nav>
  );
}

// 6. 编程式导航
function LoginForm() {
  const navigate = useNavigate();
  const location = useLocation();
  
  const handleLogin = async (credentials) => {
    // 登录逻辑
    const from = location.state?.from?.pathname || '/';
    navigate(from, { replace: true });
  };
  
  return <form onSubmit={handleLogin}>...</form>;
}

// 7. 查询参数
function SearchPage() {
  const [searchParams, setSearchParams] = useSearchParams();
  const query = searchParams.get('q') || '';
  const page = searchParams.get('page') || '1';
  
  const handleSearch = (newQuery) => {
    setSearchParams({ q: newQuery, page: '1' });
  };
  
  return (
    <div>
      <input value={query} onChange={(e) => handleSearch(e.target.value)} />
      <p>Page: {page}</p>
    </div>
  );
}

// 8. 懒加载路由
import { lazy, Suspense } from 'react';

const LazyAbout = lazy(() => import('./pages/About'));
const LazyDashboard = lazy(() => import('./pages/Dashboard'));

function AppWithLazy() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<LazyAbout />} />
        <Route path="/dashboard" element={<LazyDashboard />} />
      </Routes>
    </Suspense>
  );
}

// 辅助组件
const Home = () => <h1>Home Page</h1>;
const About = () => <h1>About Page</h1>;
const Users = () => <h1>Users Page</h1>;
const UserList = () => <h1>User List</h1>;
const PostDetail = () => {
  const { userId, postId } = useParams();
  return <h1>Post {postId} by User {userId}</h1>;
};
const NotFound = () => <h1>404 - Page Not Found</h1>;
const Overview = () => <div>Overview Content</div>;
const Statistics = () => <div>Statistics Content</div>;
const Settings = () => <div>Settings Content</div>;
const Login = () => <div>Login Page</div>;
const PublicPage = () => <div>Public Page</div>;

53. 状态管理(Redux/Zustand)?

答案: 状态管理库用于管理跨组件的全局状态。

Redux Toolkit - 官方推荐的Redux工具集 Zustand - 轻量级替代方案

示例代码:

复制代码
// ==================== Redux Toolkit ====================
import { configureStore, createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';

// 1. 创建Slice
const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
    status: 'idle'
  },
  reducers: {
    increment: (state) => {
      state.value += 1; // Immer允许直接修改
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
    reset: (state) => {
      state.value = 0;
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserById.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUserById.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.user = action.payload;
      })
      .addCase(fetchUserById.rejected, (state) => {
        state.status = 'failed';
      });
  }
});

export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions;

// 2. 异步操作
const fetchUserById = createAsyncThunk(
  'users/fetchById',
  async (userId) => {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  }
);

// 3. 创建Store
const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
    // 其他reducers...
  }
});

// 4. 在组件中使用
function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
      <button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
      <button onClick={() => dispatch(reset())}>Reset</button>
    </div>
  );
}

// 5. 复杂的Slice示例 - Todo应用
const todosSlice = createSlice({
  name: 'todos',
  initialState: {
    items: [],
    filter: 'all'
  },
  reducers: {
    addTodo: (state, action) => {
      state.items.push({
        id: Date.now(),
        text: action.payload,
        completed: false
      });
    },
    toggleTodo: (state, action) => {
      const todo = state.items.find(t => t.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    },
    deleteTodo: (state, action) => {
      state.items = state.items.filter(t => t.id !== action.payload);
    },
    setFilter: (state, action) => {
      state.filter = action.payload;
    }
  }
});

// 6. Selectors
const selectAllTodos = (state) => state.todos.items;
const selectFilter = (state) => state.todos.filter;
const selectFilteredTodos = (state) => {
  const todos = selectAllTodos(state);
  const filter = selectFilter(state);
  
  if (filter === 'active') return todos.filter(t => !t.completed);
  if (filter === 'completed') return todos.filter(t => t.completed);
  return todos;
};

// 7. App组件
function AppWithRedux() {
  return (
    <Provider store={store}>
      <Counter />
      <TodoList />
    </Provider>
  );
}

// ==================== Zustand ====================
import create from 'zustand';
import { persist } from 'zustand/middleware';

// 1. 基本Store
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 })
}));

// 2. 使用Store
function ZustandCounter() {
  const { count, increment, decrement, reset } = useCounterStore();
  
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

// 3. 带异步操作的Store
const useUserStore = create((set, get) => ({
  user: null,
  loading: false,
  error: null,
  
  fetchUser: async (userId) => {
    set({ loading: true, error: null });
    try {
      const response = await fetch(`/api/users/${userId}`);
      const user = await response.json();
      set({ user, loading: false });
    } catch (error) {
      set({ error: error.message, loading: false });
    }
  },
  
  updateUser: (updates) => {
    set((state) => ({
      user: { ...state.user, ...updates }
    }));
  },
  
  logout: () => {
    set({ user: null });
  }
}));

// 4. 持久化Store
const useTodoStore = create(
  persist(
    (set) => ({
      todos: [],
      addTodo: (text) => set((state) => ({
        todos: [...state.todos, { id: Date.now(), text, completed: false }]
      })),
      toggleTodo: (id) => set((state) => ({
        todos: state.todos.map(t =>
          t.id === id ? { ...t, completed: !t.completed } : t
        )
      })),
      deleteTodo: (id) => set((state) => ({
        todos: state.todos.filter(t => t.id !== id)
      }))
    }),
    { name: 'todo-storage' }
  )
);

// 5. 分割选择器(避免不必要的渲染)
function TodoCount() {
  const count = useTodoStore((state) => state.todos.length);
  return <p>Total todos: {count}</p>;
}

// 6. 中间件
const useStoreWithLog = create((set) => ({
  count: 0,
  increment: () => set((state) => {
    console.log('Before:', state.count);
    const newState = { count: state.count + 1 };
    console.log('After:', newState.count);
    return newState;
  })
}));

// 7. 多个Store组合
const useAuthStore = create((set) => ({
  token: null,
  setToken: (token) => set({ token }),
  clearToken: () => set({ token: null })
}));

const useCartStore = create((set) => ({
  items: [],
  addItem: (item) => set((state) => ({
    items: [...state.items, item]
  })),
  clearCart: () => set({ items: [] })
}));

function ShoppingApp() {
  const { token } = useAuthStore();
  const { items, addItem } = useCartStore();
  
  return (
    <div>
      <p>Auth: {token ? 'Logged in' : 'Guest'}</p>
      <p>Cart items: {items.length}</p>
    </div>
  );
}

54. React Query/SWR数据获取?

答案: 专门用于数据获取、缓存和同步的库。

特点:

  • 自动缓存
  • 后台重新验证
  • 重复数据删除
  • 自动重试
  • 分页/无限滚动支持

示例代码:

复制代码
// ==================== React Query ====================
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
  useMutation,
  useQueryClient,
  useInfiniteQuery
} from '@tanstack/react-query';

// 1. 设置Query Client
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5分钟
      cacheTime: 10 * 60 * 1000, // 10分钟
      retry: 3,
      refetchOnWindowFocus: true
    }
  }
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UserList />
    </QueryClientProvider>
  );
}

// 2. 基本查询
function UserList() {
  const { data, isLoading, error, refetch } = useQuery({
    queryKey: ['users'],
    queryFn: async () => {
      const response = await fetch('/api/users');
      if (!response.ok) throw new Error('Failed to fetch');
      return response.json();
    }
  });
  
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <button onClick={refetch}>Refresh</button>
      <ul>
        {data.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

// 3. 带参数的查询
function UserProfile({ userId }) {
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    enabled: !!userId // 条件查询
  });
  
  return <div>{user?.name}</div>;
}

async function fetchUser(userId) {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
}

// 4. Mutation(增删改)
function CreateUser() {
  const queryClient = useQueryClient();
  
  const mutation = useMutation({
    mutationFn: async (newUser) => {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newUser)
      });
      return response.json();
    },
    onSuccess: () => {
      // 使缓存失效,重新获取
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
    onError: (error) => {
      console.error('Error creating user:', error);
    }
  });
  
  const handleSubmit = (e) => {
    e.preventDefault();
    mutation.mutate({ name: 'John Doe', email: 'john@example.com' });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <button type="submit" disabled={mutation.isLoading}>
        {mutation.isLoading ? 'Creating...' : 'Create User'}
      </button>
      {mutation.isError && <p>Error: {mutation.error.message}</p>}
      {mutation.isSuccess && <p>User created!</p>}
    </form>
  );
}

// 5. 乐观更新
function UpdateUser({ userId }) {
  const queryClient = useQueryClient();
  
  const mutation = useMutation({
    mutationFn: updateUser,
    onMutate: async (newData) => {
      // 取消正在进行的查询
      await queryClient.cancelQueries({ queryKey: ['user', userId] });
      
      // 保存当前数据(用于回滚)
      const previousUser = queryClient.getQueryData(['user', userId]);
      
      // 乐观更新
      queryClient.setQueryData(['user', userId], (old) => ({
        ...old,
        ...newData
      }));
      
      return { previousUser };
    },
    onError: (err, newData, context) => {
      // 回滚
      queryClient.setQueryData(['user', userId], context.previousUser);
    },
    onSettled: () => {
      // 重新获取确保同步
      queryClient.invalidateQueries({ queryKey: ['user', userId] });
    }
  });
  
  return (
    <button onClick={() => mutation.mutate({ name: 'Updated Name' })}>
      Update
    </button>
  );
}

async function updateUser(data) {
  const response = await fetch(`/api/users/${data.id}`, {
    method: 'PUT',
    body: JSON.stringify(data)
  });
  return response.json();
}

// 6. 无限滚动
function InfiniteUserList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage
  } = useInfiniteQuery({
    queryKey: ['users', 'infinite'],
    queryFn: ({ pageParam = 1 }) => fetchUsers(pageParam),
    getNextPageParam: (lastPage, pages) => {
      return lastPage.hasMore ? pages.length + 1 : undefined;
    }
  });
  
  return (
    <div>
      {data?.pages.map((page, i) => (
        <div key={i}>
          {page.users.map(user => (
            <div key={user.id}>{user.name}</div>
          ))}
        </div>
      ))}
      
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? 'Loading...' : 'Load More'}
        </button>
      )}
    </div>
  );
}

async function fetchUsers(page) {
  const response = await fetch(`/api/users?page=${page}`);
  return response.json();
}

// 7. 依赖查询
function UserPosts({ userId }) {
  // 先获取用户
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId)
  });
  
  // 基于用户数据获取帖子
  const { data: posts } = useQuery({
    queryKey: ['posts', user?.id],
    queryFn: () => fetchPosts(user.id),
    enabled: !!user // 只在user存在时执行
  });
  
  return <div>...</div>;
}

// ==================== SWR ====================
import useSWR, { mutate } from 'swr';

// 1. 基本用法
const fetcher = (url) => fetch(url).then(r => r.json());

function SWRUserList() {
  const { data, error, isLoading } = useSWR('/api/users', fetcher);
  
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <ul>
      {data.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

// 2. 自动重新验证
function SWRProfile({ userId }) {
  const { data } = useSWR(
    `/api/users/${userId}`,
    fetcher,
    {
      refreshInterval: 3000, // 每3秒刷新
      revalidateOnFocus: true,
      revalidateOnReconnect: true
    }
  );
  
  return <div>{data?.name}</div>;
}

// 3. 手动触发重新验证
function SWRButton() {
  const handleRefresh = () => {
    mutate('/api/users'); // 重新验证特定key
  };
  
  return <button onClick={handleRefresh}>Refresh</button>;
}

// 4. 条件获取
function ConditionalFetch({ shouldFetch, userId }) {
  const { data } = useSWR(
    shouldFetch ? `/api/users/${userId}` : null,
    fetcher
  );
  
  return <div>{data?.name}</div>;
}

// 5. 分页
function SWRPagination() {
  const [page, setPage] = useState(1);
  const { data, error } = useSWR(`/api/users?page=${page}`, fetcher);
  
  return (
    <div>
      {data?.users.map(user => <div key={user.id}>{user.name}</div>)}
      <button onClick={() => setPage(page + 1)}>Next Page</button>
    </div>
  );
}

// 6. 全局配置
import { SWRConfig } from 'swr';

function AppWithSWR() {
  return (
    <SWRConfig
      value={{
        fetcher,
        revalidateOnFocus: false,
        shouldRetryOnError: true
      }}
    >
      <SWRUserList />
    </SWRConfig>
  );
}

55. 表单处理(React Hook Form/Formik)?

答案: 表单库简化表单验证、错误处理和提交。

React Hook Form - 性能更好,基于非受控组件 Formik - 功能丰富,基于受控组件

示例代码:

复制代码
// ==================== React Hook Form ====================
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

// 1. 基本表单
function BasicForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting }
  } = useForm();
  
  const onSubmit = async (data) => {
    console.log(data);
    await new Promise(resolve => setTimeout(resolve, 1000));
  };
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('username', {
          required: 'Username is required',
          minLength: { value: 3, message: 'Min length is 3' }
        })}
        placeholder="Username"
      />
      {errors.username && <p>{errors.username.message}</p>}
      
      <input
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: 'Invalid email'
          }
        })}
        placeholder="Email"
      />
      {errors.email && <p>{errors.email.message}</p>}
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
}

// 2. 使用Yup验证
const schema = yup.object({
  username: yup.string().min(3).max(20).required(),
  email: yup.string().email().required(),
  password: yup.string().min(8).required(),
  confirmPassword: yup.string()
    .oneOf([yup.ref('password')], 'Passwords must match')
    .required(),
  age: yup.number().positive().integer().min(18).required(),
  terms: yup.boolean().oneOf([true], 'Must accept terms')
}).required();

function FormWithValidation() {
  const {
    register,
    handleSubmit,
    formState: { errors }
  } = useForm({
    resolver: yupResolver(schema),
    defaultValues: {
      username: '',
      email: '',
      password: '',
      confirmPassword: '',
      age: 18,
      terms: false
    }
  });
  
  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <input {...register('username')} />
      <p>{errors.username?.message}</p>
      
      <input {...register('email')} />
      <p>{errors.email?.message}</p>
      
      <input type="password" {...register('password')} />
      <p>{errors.password?.message}</p>
      
      <input type="password" {...register('confirmPassword')} />
      <p>{errors.confirmPassword?.message}</p>
      
      <input type="number" {...register('age')} />
      <p>{errors.age?.message}</p>
      
      <label>
        <input type="checkbox" {...register('terms')} />
        Accept Terms
      </label>
      <p>{errors.terms?.message}</p>
      
      <button type="submit">Submit</button>
    </form>
  );
}

// 3. 动态字段
function DynamicForm() {
  const { register, handleSubmit, watch } = useForm();
  const watchShowAge = watch('showAge', false);
  
  return (
    <form onSubmit={handleSubmit(console.log)}>
      <label>
        <input type="checkbox" {...register('showAge')} />
        Show Age Field
      </label>
      
      {watchShowAge && (
        <input
          type="number"
          {...register('age', { valueAsNumber: true })}
          placeholder="Age"
        />
      )}
      
      <button type="submit">Submit</button>
    </form>
  );
}

// 4. 字段数组
import { useFieldArray } from 'react-hook-form';

function FieldArrayForm() {
  const { register, control, handleSubmit } = useForm({
    defaultValues: {
      items: [{ name: '', quantity: 1 }]
    }
  });
  
  const { fields, append, remove } = useFieldArray({
    control,
    name: 'items'
  });
  
  return (
    <form onSubmit={handleSubmit(console.log)}>
      {fields.map((field, index) => (
        <div key={field.id}>
          <input
            {...register(`items.${index}.name`)}
            placeholder="Item name"
          />
          <input
            type="number"
            {...register(`items.${index}.quantity`, { valueAsNumber: true })}
            placeholder="Quantity"
          />
          <button type="button" onClick={() => remove(index)}>
            Remove
          </button>
        </div>
      ))}
      
      <button type="button" onClick={() => append({ name: '', quantity: 1 })}>
        Add Item
      </button>
      <button type="submit">Submit</button>
    </form>
  );
}

// 5. 受控组件集成(如UI库)
function ControlledComponentForm() {
  const { control, handleSubmit } = useForm();
  
  return (
    <form onSubmit={handleSubmit(console.log)}>
      <Controller
        name="select"
        control={control}
        defaultValue=""
        render={({ field }) => (
          <select {...field}>
            <option value="">Select...</option>
            <option value="option1">Option 1</option>
            <option value="option2">Option 2</option>
          </select>
        )}
      />
      
      <button type="submit">Submit</button>
    </form>
  );
}

// ==================== Formik ====================
import { Formik, Form, Field, ErrorMessage, FieldArray } from 'formik';

// 1. 基本Formik表单
function FormikBasic() {
  const initialValues = {
    username: '',
    email: '',
    password: ''
  };
  
  const validate = (values) => {
    const errors = {};
    
    if (!values.username) {
      errors.username = 'Required';
    } else if (values.username.length < 3) {
      errors.username = 'Must be at least 3 characters';
    }
    
    if (!values.email) {
      errors.email = 'Required';
    } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) {
      errors.email = 'Invalid email';
    }
    
    return errors;
  };
  
  const handleSubmit = (values, { setSubmitting }) => {
    console.log(values);
    setTimeout(() => {
      setSubmitting(false);
    }, 1000);
  };
  
  return (
    <Formik
      initialValues={initialValues}
      validate={validate}
      onSubmit={handleSubmit}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field name="username" placeholder="Username" />
          <ErrorMessage name="username" component="div" />
          
          <Field name="email" type="email" placeholder="Email" />
          <ErrorMessage name="email" component="div" />
          
          <Field name="password" type="password" placeholder="Password" />
          <ErrorMessage name="password" component="div" />
          
          <button type="submit" disabled={isSubmitting}>
            {isSubmitting ? 'Submitting...' : 'Submit'}
          </button>
        </Form>
      )}
    </Formik>
  );
}

// 2. 使用Yup验证
const formikSchema = yup.object({
  username: yup.string().min(3).required(),
  email: yup.string().email().required(),
  password: yup.string().min(8).required()
});

function FormikWithYup() {
  return (
    <Formik
      initialValues={{ username: '', email: '', password: '' }}
      validationSchema={formikSchema}
      onSubmit={(values) => console.log(values)}
    >
      <Form>
        <Field name="username" />
        <ErrorMessage name="username" />
        
        <Field name="email" />
        <ErrorMessage name="email" />
        
        <Field name="password" type="password" />
        <ErrorMessage name="password" />
        
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}

// 3. 字段数组
function FormikFieldArray() {
  return (
    <Formik
      initialValues={{ friends: [''] }}
      onSubmit={(values) => console.log(values)}
    >
      {({ values }) => (
        <Form>
          <FieldArray name="friends">
            {({ push, remove }) => (
              <div>
                {values.friends.map((friend, index) => (
                  <div key={index}>
                    <Field name={`friends.${index}`} placeholder="Friend name" />
                    <button type="button" onClick={() => remove(index)}>
                      Remove
                    </button>
                  </div>
                ))}
                <button type="button" onClick={() => push('')}>
                  Add Friend
                </button>
              </div>
            )}
          </FieldArray>
          
          <button type="submit">Submit</button>
        </Form>
      )}
    </Formik>
  );
}

// 4. 自定义Field组件
function CustomInput({ field, form, ...props }) {
  return (
    <div>
      <input {...field} {...props} />
      {form.touched[field.name] && form.errors[field.name] && (
        <div className="error">{form.errors[field.name]}</div>
      )}
    </div>
  );
}

function FormikCustomField() {
  return (
    <Formik
      initialValues={{ email: '' }}
      onSubmit={console.log}
    >
      <Form>
        <Field name="email" component={CustomInput} type="email" />
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}

(继续下一部分...)


56. React样式解决方案?

答案: React中有多种样式方案,各有优缺点。

主要方案:

  1. CSS Modules - 局部作用域CSS
  2. Styled Components - CSS-in-JS
  3. Tailwind CSS - 实用优先的CSS框架
  4. Emotion - 高性能CSS-in-JS

示例代码:

复制代码
// ==================== 1. CSS Modules ====================
// styles.module.css
/*
.container {
  padding: 20px;
  background: #f0f0f0;
}

.button {
  padding: 10px 20px;
  background: blue;
  color: white;
  border: none;
  border-radius: 4px;
}

.button:hover {
  background: darkblue;
}
*/

// Component.jsx
import styles from './styles.module.css';

function CSSModulesExample() {
  return (
    <div className={styles.container}>
      <button className={styles.button}>Click Me</button>
      <button className={`${styles.button} ${styles.primary}`}>
        Primary Button
      </button>
    </div>
  );
}

// 条件类名
import classNames from 'classnames';

function ConditionalStyles({ isPrimary, isDisabled }) {
  return (
    <button
      className={classNames(styles.button, {
        [styles.primary]: isPrimary,
        [styles.disabled]: isDisabled
      })}
    >
      Button
    </button>
  );
}

// ==================== 2. Styled Components ====================
import styled from 'styled-components';

// 基本样式
const Button = styled.button`
  padding: 10px 20px;
  background: ${props => props.primary ? 'blue' : 'gray'};
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    background: ${props => props.primary ? 'darkblue' : 'darkgray'};
  }
  
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

const Container = styled.div`
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
`;

function StyledComponentsExample() {
  return (
    <Container>
      <Button>Normal Button</Button>
      <Button primary>Primary Button</Button>
      <Button disabled>Disabled</Button>
    </Container>
  );
}

// 扩展样式
const PrimaryButton = styled(Button)`
  background: green;
  font-weight: bold;
  
  &:hover {
    background: darkgreen;
  }
`;

// 动态样式
const DynamicButton = styled.button`
  padding: ${props => props.size === 'large' ? '15px 30px' : '10px 20px'};
  font-size: ${props => props.size === 'large' ? '18px' : '14px'};
  background: ${props => props.variant === 'danger' ? 'red' : 'blue'};
`;

function DynamicExample() {
  return (
    <>
      <DynamicButton size="large" variant="danger">
        Large Danger
      </DynamicButton>
      <DynamicButton>Normal</DynamicButton>
    </>
  );
}

// 主题支持
import { ThemeProvider } from 'styled-components';

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    success: '#28a745',
    danger: '#dc3545'
  },
  spacing: {
    small: '8px',
    medium: '16px',
    large: '24px'
  }
};

const ThemedButton = styled.button`
  background: ${props => props.theme.colors.primary};
  padding: ${props => props.theme.spacing.medium};
  color: white;
  border: none;
  border-radius: 4px;
`;

function ThemedApp() {
  return (
    <ThemeProvider theme={theme}>
      <ThemedButton>Themed Button</ThemedButton>
    </ThemeProvider>
  );
}

// 全局样式
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  
  body {
    font-family: 'Arial', sans-serif;
    background: ${props => props.theme.colors.background};
  }
`;

function AppWithGlobalStyles() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <div>App Content</div>
    </ThemeProvider>
  );
}

// ==================== 3. Tailwind CSS ====================
// 需要配置 tailwind.config.js 和安装依赖

function TailwindExample() {
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold text-blue-600 mb-4">
        Tailwind Heading
      </h1>
      
      <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
        Button
      </button>
      
      <button className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded ml-2">
        Danger Button
      </button>
      
      {/* 响应式设计 */}
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
        <div className="bg-white p-4 rounded shadow">Card 1</div>
        <div className="bg-white p-4 rounded shadow">Card 2</div>
        <div className="bg-white p-4 rounded shadow">Card 3</div>
      </div>
      
      {/* 暗黑模式 */}
      <div className="bg-white dark:bg-gray-800 text-black dark:text-white p-4">
        Dark mode support
      </div>
    </div>
  );
}

// 自定义类
function TailwindCustom() {
  return (
    <button className="btn-primary">
      Custom Button
    </button>
  );
}

// tailwind.config.js 配置
/*
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {
      colors: {
        brand: '#007bff'
      }
    }
  },
  plugins: []
}
*/

// 条件类名
import clsx from 'clsx';

function ConditionalTailwind({ isPrimary, isLarge }) {
  return (
    <button
      className={clsx(
        'px-4 py-2 rounded font-semibold',
        isPrimary ? 'bg-blue-500 text-white' : 'bg-gray-200 text-black',
        isLarge ? 'text-lg' : 'text-sm'
      )}
    >
      Button
    </button>
  );
}

// ==================== 4. Emotion ====================
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import styled from '@emotion/styled';

// CSS prop
function EmotionCSS() {
  return (
    <div
      css={css`
        padding: 20px;
        background: #f0f0f0;
        border-radius: 8px;
        
        &:hover {
          background: #e0e0e0;
        }
      `}
    >
      Emotion CSS Prop
    </div>
  );
}

// Styled components
const EmotionButton = styled.button`
  padding: 10px 20px;
  background: ${props => props.primary ? 'blue' : 'gray'};
  color: white;
  border: none;
  border-radius: 4px;
`;

function EmotionStyled() {
  return (
    <>
      <EmotionButton>Normal</EmotionButton>
      <EmotionButton primary>Primary</EmotionButton>
    </>
  );
}

// 动态样式
const dynamicStyle = (color) => css`
  color: ${color};
  font-weight: bold;
`;

function EmotionDynamic() {
  return (
    <div css={dynamicStyle('red')}>
      Dynamic Color
    </div>
  );
}

// ==================== 5. 内联样式 ====================
function InlineStyles() {
  const containerStyle = {
    padding: '20px',
    background: '#f0f0f0',
    borderRadius: '8px'
  };
  
  const buttonStyle = {
    padding: '10px 20px',
    background: 'blue',
    color: 'white',
    border: 'none',
    borderRadius: '4px'
  };
  
  return (
    <div style={containerStyle}>
      <button style={buttonStyle}>Button</button>
    </div>
  );
}

// ==================== 样式方案对比 ====================
/*
CSS Modules:
优点:作用域隔离、传统CSS、SSR友好
缺点:需要导入、动态样式不方便

Styled Components:
优点:动态样式、主题支持、自动vendor前缀
缺点:运行时开销、学习曲线

Tailwind CSS:
优点:快速开发、一致性、小体积(tree-shaking)
缺点:类名冗长、学习成本

Emotion:
优点:性能好、灵活、SSR支持
缺点:配置复杂
*/

57. React测试(Jest + React Testing Library)?

答案: 测试确保代码质量和可维护性。

测试类型:

  • 单元测试:测试单个组件/函数
  • 集成测试:测试组件交互
  • E2E测试:测试完整用户流程

示例代码:

复制代码
// ==================== Jest 基础 ====================
// sum.js
export function sum(a, b) {
  return a + b;
}

// sum.test.js
import { sum } from './sum';

describe('sum function', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
  
  test('adds negative numbers', () => {
    expect(sum(-1, -2)).toBe(-3);
  });
  
  it('handles zero', () => {
    expect(sum(0, 5)).toBe(5);
  });
});

// ==================== React Testing Library ====================
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

// 1. 基本组件测试
// Button.jsx
function Button({ onClick, children, disabled }) {
  return (
    <button onClick={onClick} disabled={disabled}>
      {children}
    </button>
  );
}

// Button.test.jsx
describe('Button Component', () => {
  test('renders button with text', () => {
    render(<Button>Click Me</Button>);
    const button = screen.getByText('Click Me');
    expect(button).toBeInTheDocument();
  });
  
  test('calls onClick when clicked', () => {
    const handleClick = jest.fn();
    render(<Button onClick={handleClick}>Click Me</Button>);
    
    const button = screen.getByText('Click Me');
    fireEvent.click(button);
    
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
  
  test('is disabled when disabled prop is true', () => {
    render(<Button disabled>Click Me</Button>);
    const button = screen.getByText('Click Me');
    expect(button).toBeDisabled();
  });
});

// 2. 测试表单
// LoginForm.jsx
function LoginForm({ onSubmit }) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    if (!username || !password) {
      setError('Please fill in all fields');
      return;
    }
    onSubmit({ username, password });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
        aria-label="username"
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        aria-label="password"
      />
      {error && <div role="alert">{error}</div>}
      <button type="submit">Login</button>
    </form>
  );
}

// LoginForm.test.jsx
describe('LoginForm', () => {
  test('submits form with valid data', async () => {
    const handleSubmit = jest.fn();
    render(<LoginForm onSubmit={handleSubmit} />);
    
    const user = userEvent.setup();
    
    await user.type(screen.getByLabelText('username'), 'john');
    await user.type(screen.getByLabelText('password'), 'password123');
    await user.click(screen.getByText('Login'));
    
    expect(handleSubmit).toHaveBeenCalledWith({
      username: 'john',
      password: 'password123'
    });
  });
  
  test('shows error when fields are empty', async () => {
    render(<LoginForm onSubmit={jest.fn()} />);
    
    const user = userEvent.setup();
    await user.click(screen.getByText('Login'));
    
    expect(screen.getByRole('alert')).toHaveTextContent('Please fill in all fields');
  });
});

// 3. 异步测试
// UserProfile.jsx
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [userId]);
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

// UserProfile.test.jsx
describe('UserProfile', () => {
  test('displays user data after loading', async () => {
    // Mock fetch
    global.fetch = jest.fn(() =>
      Promise.resolve({
        json: () => Promise.resolve({
          name: 'John Doe',
          email: 'john@example.com'
        })
      })
    );
    
    render(<UserProfile userId={1} />);
    
    // 初始loading状态
    expect(screen.getByText('Loading...')).toBeInTheDocument();
    
    // 等待数据加载
    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });
    
    expect(screen.getByText('john@example.com')).toBeInTheDocument();
  });
  
  test('displays error message on fetch failure', async () => {
    global.fetch = jest.fn(() =>
      Promise.reject(new Error('Failed to fetch'))
    );
    
    render(<UserProfile userId={1} />);
    
    await waitFor(() => {
      expect(screen.getByText(/Error: Failed to fetch/)).toBeInTheDocument();
    });
  });
});

// 4. 测试Context
const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Current theme: {theme}
    </button>
  );
}

// ThemedButton.test.jsx
describe('ThemedButton', () => {
  test('toggles theme', async () => {
    render(
      <ThemeProvider>
        <ThemedButton />
      </ThemeProvider>
    );
    
    const button = screen.getByRole('button');
    expect(button).toHaveTextContent('Current theme: light');
    
    await userEvent.click(button);
    expect(button).toHaveTextContent('Current theme: dark');
    
    await userEvent.click(button);
    expect(button).toHaveTextContent('Current theme: light');
  });
});

// 5. 测试自定义Hook
import { renderHook, act } from '@testing-library/react';

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  
  const increment = () => setCount(c => c + 1);
  const decrement = () => setCount(c => c - 1);
  const reset = () => setCount(initialValue);
  
  return { count, increment, decrement, reset };
}

describe('useCounter', () => {
  test('initializes with default value', () => {
    const { result } = renderHook(() => useCounter());
    expect(result.current.count).toBe(0);
  });
  
  test('initializes with custom value', () => {
    const { result } = renderHook(() => useCounter(10));
    expect(result.current.count).toBe(10);
  });
  
  test('increments count', () => {
    const { result } = renderHook(() => useCounter());
    
    act(() => {
      result.current.increment();
    });
    
    expect(result.current.count).toBe(1);
  });
  
  test('resets count', () => {
    const { result } = renderHook(() => useCounter(5));
    
    act(() => {
      result.current.increment();
      result.current.increment();
    });
    
    expect(result.current.count).toBe(7);
    
    act(() => {
      result.current.reset();
    });
    
    expect(result.current.count).toBe(5);
  });
});

// 6. Mock模块
// api.js
export async function fetchUser(id) {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// component.test.js
import * as api from './api';

jest.mock('./api');

test('fetches user data', async () => {
  api.fetchUser.mockResolvedValue({
    id: 1,
    name: 'John'
  });
  
  // 测试使用api.fetchUser的组件
});

// 7. 测试覆盖率
// package.json
/*
{
  "scripts": {
    "test": "jest",
    "test:coverage": "jest --coverage"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx}",
      "!src/index.js"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}
*/

// 8. 快照测试
test('matches snapshot', () => {
  const { container } = render(<Button>Click Me</Button>);
  expect(container.firstChild).toMatchSnapshot();
});

58. TypeScript与React?

答案: TypeScript为React提供类型安全,提高代码质量和开发体验。

核心概念:

  • Props类型定义
  • State类型定义
  • 事件类型
  • Ref类型
  • 泛型组件

示例代码:

复制代码
// ==================== 1. 基本Props类型 ====================
interface ButtonProps {
  children: React.ReactNode;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}

function Button({ children, onClick, variant = 'primary', disabled }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled} className={variant}>
      {children}
    </button>
  );
}

// 使用type
type CardProps = {
  title: string;
  description?: string;
  footer?: React.ReactNode;
};

const Card: React.FC<CardProps> = ({ title, description, footer }) => {
  return (
    <div>
      <h2>{title}</h2>
      {description && <p>{description}</p>}
      {footer}
    </div>
  );
};

// ==================== 2. State类型 ====================
interface User {
  id: number;
  name: string;
  email: string;
}

function UserProfile() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string>('');
  
  // 数组状态
  const [users, setUsers] = useState<User[]>([]);
  
  // 对象状态
  const [formData, setFormData] = useState<{
    username: string;
    email: string;
  }>({
    username: '',
    email: ''
  });
  
  return <div>{user?.name}</div>;
}

// ==================== 3. 事件类型 ====================
function EventHandlers() {
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    console.log(e.currentTarget);
  };
  
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };
  
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
  };
  
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      console.log('Enter pressed');
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input onChange={handleChange} onKeyDown={handleKeyDown} />
      <button onClick={handleClick}>Submit</button>
    </form>
  );
}

// ==================== 4. Ref类型 ====================
function RefExample() {
  const inputRef = useRef<HTMLInputElement>(null);
  const divRef = useRef<HTMLDivElement>(null);
  const timerRef = useRef<number | null>(null);
  
  const focusInput = () => {
    inputRef.current?.focus();
  };
  
  const getHeight = () => {
    const height = divRef.current?.offsetHeight;
    console.log(height);
  };
  
  useEffect(() => {
    timerRef.current = window.setTimeout(() => {
      console.log('Timer expired');
    }, 1000);
    
    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
    };
  }, []);
  
  return (
    <>
      <input ref={inputRef} />
      <div ref={divRef}>Content</div>
      <button onClick={focusInput}>Focus</button>
    </>
  );
}

// ==================== 5. 泛型组件 ====================
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
  keyExtractor: (item: T) => string | number;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map(item => (
        <li key={keyExtractor(item)}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  );
}

// 使用泛型组件
interface User {
  id: number;
  name: string;
}

function UserList() {
  const users: User[] = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ];
  
  return (
    <List
      items={users}
      keyExtractor={(user) => user.id}
      renderItem={(user) => <span>{user.name}</span>}
    />
  );
}

// ==================== 6. Context类型 ====================
interface ThemeContextType {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

// ==================== 7. 自定义Hook类型 ====================
interface UseFetchResult<T> {
  data: T | null;
  loading: boolean;
  error: string | null;
  refetch: () => void;
}

function useFetch<T>(url: string): UseFetchResult<T> {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  
  const fetchData = async () => {
    try {
      setLoading(true);
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  };
  
  useEffect(() => {
    fetchData();
  }, [url]);
  
  return { data, loading, error, refetch: fetchData };
}

// 使用
interface Post {
  id: number;
  title: string;
  body: string;
}

function Posts() {
  const { data, loading, error } = useFetch<Post[]>('/api/posts');
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  
  return (
    <ul>
      {data?.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

// ==================== 8. 高阶组件类型 ====================
interface WithLoadingProps {
  loading: boolean;
}

function withLoading<P extends object>(
  Component: React.ComponentType<P>
): React.FC<P & WithLoadingProps> {
  return ({ loading, ...props }) => {
    if (loading) {
      return <div>Loading...</div>;
    }
    return <Component {...(props as P)} />;
  };
}

// 使用
interface UserCardProps {
  name: string;
  email: string;
}

const UserCard: React.FC<UserCardProps> = ({ name, email }) => (
  <div>
    <h3>{name}</h3>
    <p>{email}</p>
  </div>
);

const UserCardWithLoading = withLoading(UserCard);

// ==================== 9. 组件Props类型工具 ====================
// 提取组件Props类型
type ButtonElementProps = React.ComponentProps<'button'>;
type DivElementProps = React.ComponentPropsWithoutRef<'div'>;

// Omit某些props
interface ExtendedButtonProps extends Omit<ButtonElementProps, 'type'> {
  variant: 'primary' | 'secondary';
}

// Pick某些props
type OnlyOnClick = Pick<ButtonElementProps, 'onClick'>;

// ==================== 10. 实用类型 ====================
// React.ReactNode - 任何可渲染内容
type Children = React.ReactNode;

// React.ReactElement - React元素
type Element = React.ReactElement;

// React.CSSProperties - CSS样式对象
const style: React.CSSProperties = {
  color: 'red',
  fontSize: '16px'
};

// React.HTMLAttributes - HTML属性
interface DivProps extends React.HTMLAttributes<HTMLDivElement> {
  customProp: string;
}

(继续最后两题...)


59. Next.js服务端渲染?

答案: Next.js是React框架,支持SSR、SSG、ISR等多种渲染模式。

渲染模式:

  • SSR (Server-Side Rendering): 每次请求时服务器渲染
  • SSG (Static Site Generation): 构建时预渲染
  • ISR (Incremental Static Regeneration): 按需重新生成静态页面
  • CSR (Client-Side Rendering): 客户端渲染

示例代码:

复制代码
// ==================== 1. SSR (getServerSideProps) ====================
// pages/users/[id].js
interface User {
  id: number;
  name: string;
  email: string;
}

interface UserPageProps {
  user: User;
  timestamp: string;
}

// 每次请求时在服务器端执行
export async function getServerSideProps(context) {
  const { params, req, res, query } = context;
  const { id } = params;
  
  // 获取数据
  const response = await fetch(`https://api.example.com/users/${id}`);
  const user = await response.json();
  
  // 设置缓存头
  res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  );
  
  // 如果用户不存在,返回404
  if (!user) {
    return {
      notFound: true
    };
  }
  
  // 重定向
  if (user.banned) {
    return {
      redirect: {
        destination: '/banned',
        permanent: false
      }
    };
  }
  
  return {
    props: {
      user,
      timestamp: new Date().toISOString()
    }
  };
}

function UserPage({ user, timestamp }: UserPageProps) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
      <p>Rendered at: {timestamp}</p>
    </div>
  );
}

export default UserPage;

// ==================== 2. SSG (getStaticProps) ====================
// pages/blog/[slug].js
interface Post {
  slug: string;
  title: string;
  content: string;
  publishedAt: string;
}

interface BlogPostProps {
  post: Post;
}

// 构建时生成所有可能的路径
export async function getStaticPaths() {
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();
  
  const paths = posts.map((post) => ({
    params: { slug: post.slug }
  }));
  
  return {
    paths,
    fallback: 'blocking' // 或 false, true
    // false: 返回404如果路径不在paths中
    // true: 显示fallback页面,后台生成页面
    // 'blocking': 等待页面生成完成
  };
}

// 构建时为每个路径生成HTML
export async function getStaticProps({ params }) {
  const { slug } = params;
  
  const response = await fetch(`https://api.example.com/posts/${slug}`);
  const post = await response.json();
  
  if (!post) {
    return {
      notFound: true
    };
  }
  
  return {
    props: {
      post
    },
    revalidate: 60 // ISR: 60秒后重新生成
  };
}

function BlogPost({ post }: BlogPostProps) {
  const router = useRouter();
  
  // fallback: true时显示加载状态
  if (router.isFallback) {
    return <div>Loading...</div>;
  }
  
  return (
    <article>
      <h1>{post.title}</h1>
      <time>{post.publishedAt}</time>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

export default BlogPost;

// ==================== 3. ISR (Incremental Static Regeneration) ====================
// pages/products/[id].js
export async function getStaticProps({ params }) {
  const product = await fetchProduct(params.id);
  
  return {
    props: {
      product
    },
    revalidate: 10 // 10秒后重新生成
  };
}

export async function getStaticPaths() {
  // 只预生成热门产品
  const popularProducts = await fetchPopularProducts();
  
  return {
    paths: popularProducts.map(p => ({ params: { id: p.id } })),
    fallback: 'blocking' // 其他产品按需生成
  };
}

// ==================== 4. 混合渲染 ====================
// pages/dashboard.js
// 部分SSR + 客户端获取
export async function getServerSideProps() {
  // 获取初始数据
  const initialData = await fetchInitialData();
  
  return {
    props: {
      initialData
    }
  };
}

function Dashboard({ initialData }) {
  // 客户端获取用户特定数据
  const { data: userData } = useSWR('/api/user', fetcher);
  const { data: notifications } = useSWR('/api/notifications', fetcher);
  
  return (
    <div>
      <h1>Dashboard</h1>
      <InitialSection data={initialData} />
      {userData && <UserSection user={userData} />}
      {notifications && <NotificationsList items={notifications} />}
    </div>
  );
}

// ==================== 5. API Routes ====================
// pages/api/users/[id].ts
import type { NextApiRequest, NextApiResponse } from 'next';

interface User {
  id: number;
  name: string;
}

type ResponseData = {
  user?: User;
  error?: string;
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  const { method, query } = req;
  const { id } = query;
  
  switch (method) {
    case 'GET':
      try {
        const user = await fetchUser(id as string);
        res.status(200).json({ user });
      } catch (error) {
        res.status(500).json({ error: 'Failed to fetch user' });
      }
      break;
      
    case 'PUT':
      try {
        const updatedUser = await updateUser(id as string, req.body);
        res.status(200).json({ user: updatedUser });
      } catch (error) {
        res.status(500).json({ error: 'Failed to update user' });
      }
      break;
      
    case 'DELETE':
      try {
        await deleteUser(id as string);
        res.status(204).end();
      } catch (error) {
        res.status(500).json({ error: 'Failed to delete user' });
      }
      break;
      
    default:
      res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}

// ==================== 6. Middleware ====================
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 认证检查
  const token = request.cookies.get('token');
  
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  // 添加自定义header
  const response = NextResponse.next();
  response.headers.set('x-custom-header', 'value');
  
  return response;
}

// 配置哪些路径使用middleware
export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*']
};

// ==================== 7. Image优化 ====================
import Image from 'next/image';

function ImageExample() {
  return (
    <div>
      {/* 自动优化 */}
      <Image
        src="/profile.jpg"
        alt="Profile"
        width={500}
        height={500}
        priority // 优先加载
      />
      
      {/* 外部图片 */}
      <Image
        src="https://example.com/image.jpg"
        alt="External"
        width={800}
        height={600}
        placeholder="blur" // 模糊占位符
        blurDataURL="/placeholder.jpg"
      />
      
      {/* 响应式 */}
      <Image
        src="/banner.jpg"
        alt="Banner"
        fill
        style={{ objectFit: 'cover' }}
      />
    </div>
  );
}

// next.config.js配置
/*
module.exports = {
  images: {
    domains: ['example.com', 'cdn.example.com'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384]
  }
}
*/

// ==================== 8. 动态导入 ====================
import dynamic from 'next/dynamic';

// 动态导入组件(代码分割)
const DynamicComponent = dynamic(() => import('../components/Heavy'), {
  loading: () => <p>Loading...</p>,
  ssr: false // 禁用SSR
});

function PageWithDynamic() {
  return (
    <div>
      <h1>Page</h1>
      <DynamicComponent />
    </div>
  );
}

// ==================== 9. Head和SEO ====================
import Head from 'next/head';

function SEOPage() {
  return (
    <>
      <Head>
        <title>Page Title</title>
        <meta name="description" content="Page description" />
        <meta property="og:title" content="Open Graph Title" />
        <meta property="og:description" content="OG Description" />
        <meta property="og:image" content="https://example.com/image.jpg" />
        <link rel="canonical" href="https://example.com/page" />
        <script type="application/ld+json">
          {JSON.stringify({
            '@context': 'https://schema.org',
            '@type': 'Article',
            headline: 'Article Title'
          })}
        </script>
      </Head>
      <article>Content</article>
    </>
  );
}

// ==================== 10. App Router (Next.js 13+) ====================
// app/layout.tsx
export default function RootLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

// app/page.tsx
export default function HomePage() {
  return <h1>Home Page</h1>;
}

// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    next: { revalidate: 60 } // ISR
  });
  return res.json();
}

export default async function BlogPost({
  params
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// 生成静态参数
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());
  
  return posts.map((post) => ({
    slug: post.slug
  }));
}

// 生成元数据
export async function generateMetadata({ params }) {
  const post = await getPost(params.slug);
  
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage]
    }
  };
}

60. React 18新特性?

答案: React 18引入了并发特性、自动批处理、Transitions等重要更新。

核心新特性:

  1. Concurrent Rendering (并发渲染)
  2. Automatic Batching (自动批处理)
  3. Transitions (过渡)
  4. Suspense for Data Fetching
  5. useId, useDeferredValue, useTransition, useSyncExternalStore

示例代码:

复制代码
// ==================== 1. Concurrent Rendering ====================
// React 18之前
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));

// React 18 - 启用并发特性
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);

// ==================== 2. Automatic Batching ====================
import { useState } from 'react';

// React 17: 只在事件处理器中批处理
// React 18: 所有更新都自动批处理

function AutomaticBatching() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  
  // React 17: 在Promise、setTimeout中不批处理(2次渲染)
  // React 18: 自动批处理(1次渲染)
  function handleClick() {
    setTimeout(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
      // React 18中只会重新渲染一次
    }, 1000);
  }
  
  // Promise中也会批处理
  async function fetchData() {
    const data = await fetch('/api/data');
    setCount(c => c + 1);
    setFlag(f => !f);
    // React 18中只会重新渲染一次
  }
  
  console.log('Render'); // React 18中只打印一次
  
  return (
    <div>
      <p>Count: {count}</p>
      <p>Flag: {flag.toString()}</p>
      <button onClick={handleClick}>Update</button>
    </div>
  );
}

// 如果需要退出批处理
import { flushSync } from 'react-dom';

function OptOutBatching() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  
  function handleClick() {
    flushSync(() => {
      setCount(c => c + 1);
    });
    // 第一次渲染完成
    
    flushSync(() => {
      setFlag(f => !f);
    });
    // 第二次渲染完成
  }
  
  return <button onClick={handleClick}>Update</button>;
}

// ==================== 3. useTransition ====================
import { useState, useTransition } from 'react';

function SearchWithTransition() {
  const [isPending, startTransition] = useTransition();
  const [input, setInput] = useState('');
  const [list, setList] = useState([]);
  
  function handleChange(e) {
    // 紧急更新:立即更新输入框
    setInput(e.target.value);
    
    // 非紧急更新:标记为transition
    startTransition(() => {
      // 昂贵的计算或状态更新
      const filtered = heavyFilter(e.target.value);
      setList(filtered);
    });
  }
  
  return (
    <div>
      <input value={input} onChange={handleChange} />
      {isPending && <div>Searching...</div>}
      <ul>
        {list.map(item => <li key={item.id}>{item.name}</li>)}
      </ul>
    </div>
  );
}

function heavyFilter(query) {
  // 模拟昂贵的过滤操作
  const items = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`
  }));
  return items.filter(item => 
    item.name.toLowerCase().includes(query.toLowerCase())
  );
}

// 标签切换示例
function TabsWithTransition() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('home');
  
  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  
  return (
    <div>
      <button onClick={() => selectTab('home')}>Home</button>
      <button onClick={() => selectTab('posts')}>
        Posts {isPending && '(Loading...)'}
      </button>
      <button onClick={() => selectTab('contact')}>Contact</button>
      
      <div>
        {tab === 'home' && <HomePage />}
        {tab === 'posts' && <PostsPage />}
        {tab === 'contact' && <ContactPage />}
      </div>
    </div>
  );
}

// ==================== 4. useDeferredValue ====================
import { useDeferredValue, useMemo } from 'react';

function DeferredSearch() {
  const [input, setInput] = useState('');
  const deferredInput = useDeferredValue(input);
  
  // 使用延迟值进行昂贵的计算
  const filteredList = useMemo(() => {
    console.log('Filtering with:', deferredInput);
    return heavyFilter(deferredInput);
  }, [deferredInput]);
  
  return (
    <div>
      {/* 输入框立即更新 */}
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
      />
      
      {/* 列表使用延迟值,低优先级更新 */}
      <ul>
        {filteredList.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

// 显示过时内容
function SlowList({ text }) {
  const deferredText = useDeferredValue(text);
  const isStale = text !== deferredText;
  
  return (
    <div style={{ opacity: isStale ? 0.5 : 1 }}>
      {items.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

// ==================== 5. Suspense for Data Fetching ====================
import { Suspense } from 'react';

// 资源包装器
function wrapPromise(promise) {
  let status = 'pending';
  let result;
  
  const suspender = promise.then(
    (data) => {
      status = 'success';
      result = data;
    },
    (error) => {
      status = 'error';
      result = error;
    }
  );
  
  return {
    read() {
      if (status === 'pending') {
        throw suspender; // Suspense捕获
      } else if (status === 'error') {
        throw result;
      }
      return result;
    }
  };
}

const userResource = wrapPromise(fetchUser());

function User() {
  const user = userResource.read(); // 如果pending,抛出promise
  return <div>{user.name}</div>;
}

function AppWithSuspense() {
  return (
    <Suspense fallback={<div>Loading user...</div>}>
      <User />
      <Suspense fallback={<div>Loading posts...</div>}>
        <Posts />
      </Suspense>
    </Suspense>
  );
}

// 并行加载
function ProfilePage() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <User />
      <Posts />
      {/* User和Posts并行加载 */}
    </Suspense>
  );
}

// 串行加载
function ProfilePageSerial() {
  return (
    <Suspense fallback={<div>Loading user...</div>}>
      <User />
      <Suspense fallback={<div>Loading posts...</div>}>
        <Posts />
        {/* Posts在User加载完成后才开始加载 */}
      </Suspense>
    </Suspense>
  );
}

// ==================== 6. useId ====================
function LoginForm() {
  const id = useId();
  
  return (
    <div>
      <label htmlFor={`${id}-email`}>Email:</label>
      <input id={`${id}-email`} type="email" />
      
      <label htmlFor={`${id}-password`}>Password:</label>
      <input id={`${id}-password`} type="password" />
    </div>
  );
}

// 多个实例有唯一ID
function App() {
  return (
    <>
      <LoginForm /> {/* IDs: :r1:-email, :r1:-password */}
      <LoginForm /> {/* IDs: :r2:-email, :r2:-password */}
    </>
  );
}

// ==================== 7. useSyncExternalStore ====================
import { useSyncExternalStore } from 'react';

// 订阅外部store(如Redux, MobX等)
function useStore(store) {
  return useSyncExternalStore(
    store.subscribe, // 订阅
    store.getSnapshot, // 获取当前值
    store.getServerSnapshot // 服务端渲染的快照(可选)
  );
}

// 简单store实现
function createStore(initialState) {
  let state = initialState;
  const listeners = new Set();
  
  return {
    getSnapshot: () => state,
    subscribe: (listener) => {
      listeners.add(listener);
      return () => listeners.delete(listener);
    },
    setState: (newState) => {
      state = newState;
      listeners.forEach(l => l());
    }
  };
}

const store = createStore({ count: 0 });

function Counter() {
  const state = useSyncExternalStore(
    store.subscribe,
    store.getSnapshot
  );
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => store.setState({ count: state.count + 1 })}>
        Increment
      </button>
    </div>
  );
}

// 订阅浏览器API
function useOnlineStatus() {
  return useSyncExternalStore(
    (callback) => {
      window.addEventListener('online', callback);
      window.addEventListener('offline', callback);
      return () => {
        window.removeEventListener('online', callback);
        window.removeEventListener('offline', callback);
      };
    },
    () => navigator.onLine,
    () => true // 服务端默认在线
  );
}

function OnlineStatus() {
  const isOnline = useOnlineStatus();
  return <div>{isOnline ? 'Online' : 'Offline'}</div>;
}

// ==================== 8. startTransition API ====================
import { startTransition } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  
  function handleChange(e) {
    setQuery(e.target.value);
    
    // 非Hook版本的transition
    startTransition(() => {
      const filtered = search(e.target.value);
      setResults(filtered);
    });
  }
  
  return (
    <div>
      <input value={query} onChange={handleChange} />
      <Results items={results} />
    </div>
  );
}

// ==================== 9. 性能对比 ====================
// React 17 vs React 18

// React 17:
// - 同步渲染,阻塞主线程
// - 部分批处理
// - 没有优先级概念

// React 18:
// - 并发渲染,可中断
// - 完全自动批处理
// - 紧急 vs 非紧急更新
// - 更流畅的用户体验

// 实际应用场景
function RealWorldExample() {
  const [isPending, startTransition] = useTransition();
  const [searchTerm, setSearchTerm] = useState('');
  const [products, setProducts] = useState([]);
  
  function handleSearch(e) {
    const value = e.target.value;
    
    // 紧急:立即更新输入框
    setSearchTerm(value);
    
    // 非紧急:搜索产品(可被中断)
    startTransition(() => {
      const filtered = searchProducts(value);
      setProducts(filtered);
    });
  }
  
  return (
    <div>
      <input
        type="search"
        value={searchTerm}
        onChange={handleSearch}
        placeholder="Search products..."
      />
      
      {isPending ? (
        <div>Searching...</div>
      ) : (
        <ProductList products={products} />
      )}
    </div>
  );
}

// 辅助组件
const HomePage = () => <div>Home Content</div>;
const PostsPage = () => <div>Posts Content</div>;
const ContactPage = () => <div>Contact Content</div>;
const Posts = () => <div>Posts</div>;
const ProductList = ({ products }) => (
  <div>{products.map(p => <div key={p.id}>{p.name}</div>)}</div>
);

function searchProducts(query) {
  // 搜索逻辑
  return [];
}

🎓 总结和学习建议

React 18升级要点

  1. 更新到React 18

    npm install react@18 react-dom@18

  2. 使用createRoot

    // 旧
    ReactDOM.render(<App />, container);

    // 新
    const root = createRoot(container);
    root.render(<App />);

  3. 利用新特性

  • 使用useTransition优化重渲染
  • 使用Suspense优化加载体验
  • 使用useDeferredValue延迟非关键更新

面试重点

  • 理解并发渲染的概念和好处
  • 掌握Suspense的工作原理
  • 熟悉useTransition的使用场景
  • 了解自动批处理带来的性能提升

实践建议

  1. 在新项目中使用React 18
  2. 逐步将旧项目迁移到React 18
  3. 使用新特性优化用户体验
  4. 关注性能监控和优化

完整60题已全部完成! 🎉

这份完整的面试指南涵盖了:

  • ✅ Java核心知识(30题)
  • ✅ React基础到高级(30题)
  • ✅ 每题都有详细解释和代码示例
  • ✅ 实战技巧和最佳实践

建议你按照以下步骤学习:

  1. 每天5-10题,深入理解概念
  2. 动手实践每个代码示例
  3. 构建小项目巩固知识
  4. 模拟面试检验学习成果

祝你面试成功!💪🚀


🎯 面试准备建议

学习路线

  1. 基础巩固 (1-2周)
    • Java基础语法
    • React核心概念
  2. 深入学习 (2-3周)
    • 集合框架、多线程
    • Hooks、状态管理
  3. 项目实践 (2-3周)
    • 构建完整的全栈应用
    • 使用Spring Boot + React
  4. 面试刷题 (1-2周)
    • LeetCode算法题
    • 模拟面试

项目推荐

  • Todo应用(全栈)
  • 电商网站
  • 社交媒体应用
  • 博客系统

面试技巧

  1. STAR法则回答行为问题
  2. 先思考再回答,不要急于作答
  3. 举具体例子说明技术应用
  4. 主动提问展示你的兴趣
  5. 诚实面对不会的问题

额外资源

  • 官方文档:Java Docs, React Docs
  • 在线教程:freeCodeCamp, Udemy
  • 技术博客:Medium, Dev.to
  • GitHub项目:学习优秀开源项目

祝你面试成功!记住:准备充分 + 自信表达 = 成功! 💪

本文档包含60道精选面试题,涵盖Java和React的所有核心知识点。建议每天学习5-10道题,配合实际代码练习。

相关推荐
XMYX-02 小时前
从 Pod 资源到 JVM 参数:我再生产环境中踩过的 Kubernetes 资源配置那些坑——2025 年度技术总结
jvm·容器·kubernetes
秋邱2 小时前
Java匿名内部类的使用场景:从语法本质到实战优化全解析
android·java·开发语言·数据库·python
不会c嘎嘎2 小时前
QT中的常用控件(一)
开发语言·qt
悟乙己2 小时前
anthropics Skills pptx深度解读:从官方规范到实战案例(二)
java·llm·pptx·skills·anthropics
程序员阿鹏2 小时前
RabbitMQ持久化到磁盘中有个节点断掉了怎么办?
java·开发语言·分布式·后端·spring·缓存·rabbitmq
资生算法程序员_畅想家_剑魔2 小时前
Java常见技术分享-20-多线程安全-进阶模块-并发集合与线程池-ThreadPoolExecutor
java·开发语言
乐之者v2 小时前
AI生成mybatis代码
java·mybatis
lsx2024062 小时前
CSS 列表
开发语言
努力变大白2 小时前
Python多指标综合评价及预测方法实战:CRITIC法+熵权法+TOPSIS+博弈论组合赋权综合评价预测全流程解析
开发语言·python