Skip to content

安装

CDN 引入

可以通过 CDN 获得 React 和 ReactDOM 的 UMD 版本:

html
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

上述版本仅用于开发环境,不适合用于生产环境。压缩优化后可用于生产的 React 版本可通过如下方式引用:

html
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

如果需要加载指定版本的 react 和 react-dom,可以把 18 替换成所需加载的版本号。

create-react-app

Create React App 是一个用于学习 React 的舒适环境,也是用 React 创建新的单页应用的最佳方式。

它会自动配置开发环境,以便能够使用最新的 JavaScript 特性,提供良好的开发体验,并为生产环境优化你的应用程序。需要在机器上安装 Node >= 14.0.0npm >= 5.6。要创建项目,执行:

bash
npx create-react-app my-app
cd my-app
npm start

基础

JSX

JSX,是 JavaScript 的语法扩展。在 React 中建议配合使用 JSX,可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。

基本写法:

jsx
//  dom 类型标签
const element = <h1>123</h1>;

//  react 组件类型标签
const element = <HelloWorld />;

//  混用 dom 和 react
const element = (
    <div>
        <HelloWorld />
    </div>
);

//  使用表达式计算
const element = <div>{1+2}</div>;

//  表达式中使用列表渲染
const element = (
    <ul>
        {
            todos.map((item, index), function () {
                return <li key="index">item<li>
            })
        }
    </ul>
);

注意,使用表达式的方法时,不能使用 js 多行语句,遇到需要使用多行语句时,可以使用三目运算符或者逻辑与替代:

jsx
// 写法一
let complete;
const element = (
    <div>
        {
            complete ? <CompletedList /> : null
        }
    </div>
);

// 写法二
let complete;
const element = (
    <div>
        {
            complete && <CompletedList />
        }
    </div>
);

React DOM 在渲染所有输入内容之前,默认会进行转义,它可以确保在应用中永远不会注入那些并非自己明确编写的内容:

jsx
const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;

在 JSX 中注释的格式为 {/* 注释 */},比如:

jsx
consr element = (
    <div>
        {/* 这里是一个注释 */}
        <span>React</span>
    </div>
)

生命周期

界面渲染

事件处理

写法

在 JSX 中,事件使用小驼峰法,即 onclick 要写成 onClick

事件绑定

在 React16 版下时,事件会自动绑定到当前组件上,但是在 React16 以上,我们需要手动将事件绑定到当前组件上。

js
class TestComponnet extends React.Component{
    constructor() {
        this.handleClick = this.handleClick.bind(this);
    }
}

注意:必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined

如果觉得使用 bind 很麻烦,这里有两种方式可以解决。你可以使用 public class fields 语法

jsx
/**
 * 方案一:CRA 默认启用此语法
 */
class LoggingButton extends React.Component {
    // This syntax ensures `this` is bound within handleClick.
    handleClick = () => {
        console.log('this is:', this);
    };
    render() {
        return (
            <button onClick={this.handleClick}>
                Click me
            </button>
        );
    }
}
jsx
/**
 * 方案二:在回调中使用箭头函数
 * 注意:此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,
 * 这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。
 * 我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题
 */
class LoggingButton extends React.Component {
    handleClick() {
        console.log('this is:', this);
    }

    render() {
        // 此语法确保 `handleClick` 内的 `this` 已被绑定。
        return (
            <button onClick={() => this.handleClick()}>
                Click me
            </button>
        );
    }
}

传参

默认的参数 event

jsx
<button onClick={this.handleClick}>Click me</button>
handleClick(e) {
    console.log(e.target);
}

event 的基础上再加其他参数,e 为默认参数:

jsx
<button onClick={(e) => this.handleClick(id, e)}>Delete Row</button>
<button onClick={this.handleClick.bind(this, id)}>Delete Row</button>
handleClick(param1, param2, e) {
    console.log(param1, param2, e);
    e.preventDefault(); // 阻止默认事件运行
}

上述两种方式是等价的,分别通过箭头函数Function.prototype.bind 来实现。在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

原生事件

使用事件监听器的时候,要在 componentDidMount 中增加,在 componentWillUnmount 中移除:

jsx
// 监听按钮的点击事件
<button ref="myBtn">点击我</button>

componentDidMount() {
     this.refs.myBtn.addEventListener('click', function (e) {
        console.log('触发事件')
    })
},
componentWillUnmount() {
    this.refs.myBtn.removeEventListener('click', function (e) {
        console.log('移除事件');
    });
}

// 监听敲击键盘事件
componentDidMount() {
     window.addEventListener('keypress', function (e) {
        code = e.which;
        console.log(code);
    })
},
componentWillUnmount() {
    window.removeEventListener('keypress', function (e) {
        console.log('移除事件');
    });
}

状态提升

在 React 应用中,任何可变数据应当只有一个相对应的唯一「数据源」。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。应当首先依靠自上而下的数据流,而不是尝试在不同组件间同步 state

虽然提升 state 方式比双向绑定方式需要编写更多的「样板」代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于「存在」于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。

组合 vs 继承

进阶

高阶组件

操作 DOM

非受控组件

性能优化

HOOK

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hook描述
useState设置和改变 state,代替原来的 statesetState
useEffect代替原来的生命周期,componentDidMountcomponentDidUpdatecomponentWillUnmount 的合并版
useLayoutEffectuseEffect 作用相同,但它会同步调用 effect
useMemo控制组件更新条件,可根据状态变化控制方法执行、优化传值
useCallbackuseMemo 优化传值,useCallback 优化传的方法,是否更新
useRef跟以前的 ref 一样,只是更简洁了
useContext上下文爷孙及更深组件传值
useReducer代替原来 redux 里的 reducer,配合 useContext 一起使用
useDebugValue在 React 开发者工具中显示自定义 hook 的标签,调试使用。
useImperativeHandle可以让你在使用 ref 时自定义暴露给父组件的实例值。

原理

工具链

生态系统

路由

数据

请求

渲染

最佳实践

参考资料