1. 什么是React? #

2. 组件化的概念 #

3.搭建React开发环境 #

cnpm i create-react-app -g
create-react-app zhufeng2019react --typescript
cd zhufeng2019react
npm start
包名 用途
react React is a JavaScript library for creating user interfaces
@types/react This package contains type definitions for React (http://facebook.github.io/react/)
react-dom This package serves as the entry point to the DOM and server renderers for React. It is intended to be paired with the generic React package, which is shipped as react to npm
@types/react-dom This package contains type definitions for React (react-dom) (http://facebook.github.io/react/)
react-scripts This package includes scripts and configuration used by Create React App
typescript TypeScript is a language for application-scale JavaScript
@types/jest This package contains type definitions for Jest (https://jestjs.io/)
@types/node This package contains type definitions for Node.js (http://nodejs.org/)

4.JSX #

4.1 什么是JSX #

ReactDOM.render(
  <h1>Hello</h1>,
  document.getElementById('root')
);

4.2 什么是元素 #

<h1 className="title" style={{color:'red'}}>hello</h1>
React.createElement("h1", {
  className: "title",
  style: {
    color: 'red'
  }
}, "hello");

createElement的结果

{
  type:'h1',
  props:{
    className: "title",
    style: {
      color: 'red'
    }
  },
  children:"hello"
}

4.3 JSX表达式 #

import React from 'react';
import ReactDOM from 'react-dom';
let title: string = 'hello';
let root: HTMLElement | null = document.getElementById('root');
ReactDOM.render<HTMLElement>(
    <h1>{title}</h1>,
    root
);

4.4 JSX属性 #

import React from 'react';
import ReactDOM from 'react-dom';
let title: string = 'hello';
let root: HTMLElement | null = document.getElementById('root');

ReactDOM.render<HTMLElement>(
    <h1 className="title" style={{ color: 'red' }}>Hello</h1>,
    document.getElementById('root')
);
 */

4.5 JSX也是对象 #

if中使用

import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
function greeting(name: string): React.ReactElement {
    if (name) {
        return <h1>Hello, {name}!</h1>;
    }
    return <h1>Hello, Stranger.</h1>;
}

const element: React.ReactElement = greeting('zhufeng');

ReactDOM.render(
    element,
    root
);

for中使用

import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
let names: Array<string> = ['张三', '李四', '王五'];
let elements: Array<React.ReactElement> = [];
for (let i = 0; i < names.length; i++) {
    elements.push(<li>{names[i]}</li>);
}
ReactDOM.render(
    <ul>
        {elements}
    </ul>,
    root
);

4.6 更新元素渲染 #

import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
function tick(): void {
    const element: React.ReactElement = (
        <div>
            {new Date().toLocaleTimeString()}
        </div>
    );
    ReactDOM.render(element, root);
}
setInterval(tick, 1000);

4.7 React只会更新必要的部分 #

5. 组件 & Props #

5.1 函数(定义的)组件 #

function Welcome(props: Props): React.ReactElement {
    return <h1>Hello, {props.name}</h1>;
}

5.2 类(定义的)组件 #

class Welcome extends React.Component<Props> {
    render(): React.ReactElement {
        return <h1>Hello, {this.props.name}</h1>;
    }
}

5.3 组件渲染 #

import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
interface Props {
    name: string;
}
function Welcome(props: Props): React.ReactElement {
    return <h1>Hello, {props.name}</h1>;
}
class Welcome2 extends React.Component<Props> {
    render(): React.ReactElement {
        return <h1>Hello, {this.props.name}</h1>;
    }
}

const element1: React.ReactElement<Props, React.JSXElementConstructor<Props>> = <Welcome name="zhufeng" />;
console.log(element1.props.name);
const element2: React.ReactElement<Props, React.JSXElementConstructor<Props>> = <Welcome2 name="zhufeng" />;
console.log(element1.props.name);

ReactDOM.render(
    <div>{element1}{element2}</div>,
    root
);

5.4 复合组件 & 提取组件 #

import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');

interface PanelProps {
    header: string;
    body: string;
}
interface HeaderProps {
    header: string;
}
interface BodyProps {
    body: string;
}
class Panel extends React.Component<PanelProps> {
    render(): React.ReactElement {
        let { header, body } = this.props;
        return (
            <div style={{ border: '1px solid red', padding: 5 }}>
                <div className="panel-default panel">
                    <Header header={header}></Header>
                    <Body body={body} />
                </div>
            </div>
        )
    }
}
class Header extends React.Component<HeaderProps> {
    render(): React.ReactElement {
        return (
            <div style={{ border: '1px solid green' }}>
                {this.props.header}
            </div>
        )
    }
}
class Body extends React.Component<BodyProps> {
    render(): React.ReactElement {
        return (
            <div style={{ border: '1px solid blue' }}>
                {this.props.body}
            </div>
        )
    }
}

let data: PanelProps = { header: '头部', body: '身体' };
ReactDOM.render(<Panel {...data} />, root);

5.5 Props的只读性 #

//纯函数
function sum(a, b) {
  return a + b;
}
//非纯函数
function withdraw(account, amount) {
  account.total -= amount;
}

5.6 类型检查 #

用法 含义
PropTypes.array 数组
PropTypes.bool 布尔类型
PropTypes.func 函数
PropTypes.number 数字
PropTypes.object 对象
PropTypes.string 字符串
PropTypes.symbol Symbol
PropTypes.node 任何可被渲染的元素(包括数字、字符串、元素或数组)
PropTypes.element 一个 React 元素
PropTypes.instanceOf(Message) Message类的实例
PropTypes.oneOf(['News', 'Photos']) 枚举类型
PropTypes.oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)]) 几种类型中的任意一个类型
PropTypes.arrayOf(PropTypes.number) 一个数组由某一类型的元素组成
PropTypes.objectOf(PropTypes.number) 可以指定一个对象由某一类型的值组成
PropTypes.shape({color: PropTypes.string,fontSize: PropTypes.number}) 可以指定一个对象由特定的类型值组成
PropTypes.func.isRequired 你可以在任何 PropTypes 属性后面加上 isRequired ,确保这个 prop 没有被提供时,会打印警告信息
PropTypes.any.isRequired 任意类型的数据
customProp: function(props, propName, componentName){} 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
interface PersonProps {
    name?: string;
    age?: number,
    gender?: 'male' | 'female',
    hobby?: Array<string>,
    position?: { x: number, y: number },
    friends?: Array<{ name: string, age: number }>,
    [prop: string]: any
}
let root: HTMLElement | null = document.getElementById('root');
class Person extends React.Component<PersonProps> {
    static defaultProps: PersonProps = {
        name: 'Stranger'
    }
    static propTypes = {
        name: PropTypes.string.isRequired,
        gender: PropTypes.oneOf(['male', 'female']),
        hobby: PropTypes.arrayOf(PropTypes.string),
        position: PropTypes.shape({
            x: PropTypes.number,
            y: PropTypes.number
        }),
        age(props: PersonProps, propName: string, componentName: string) {
            let age = props[propName];
            if (age < 0 || age > 120) {
                return new Error(`Invalid Prop ${propName} supplied to ${componentName}`)
            }
        },
        friends: PropTypes.arrayOf((propValue: any, key: string, componentName: string, location: string, propFullName: string): Error | null => {
            console.log('propValue=' + JSON.stringify(propValue, null, 2), "key=" + key, "componentName=" + componentName, "location=" + location, "propFullName=" + propFullName);
            /**
             * propValue=[{ name: 'zhangsan', age: 10 }, { name: 'lisi', age: -20 }] key=0 componentName=Person location=prop propFullName=friends[0]
             * propValue=[{ name: 'zhangsan', age: 10 }, { name: 'lisi', age: -20 }] key=1 componentName=Person location=prop propFullName=friends[1]
             */
            let age = propValue[key].age;
            if (age < 0 || age > 120) {
                return new Error(
                    'Invalid prop `' + propFullName + '.age` supplied to' +
                    ' `' + componentName + '`. Validation failed.'
                );
            }
            return null;
        })
    }
    render() {
        let { name, age, gender, hobby, position } = this.props;
        return (
            <table>
                <thead>
                    <tr>
                        <td>姓名</td>
                        <td>年龄</td>
                        <td>性别</td>
                        <td>爱好</td>
                        <td>位置</td>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>{name}</td>
                        <td>{age}</td>
                        <td>{gender}</td>
                        <td>{hobby!.join(',')}</td>
                        <td>{position!.x + ' ' + position!.y}</td>
                    </tr>
                </tbody>
            </table>
        )
    }
}
let personProps: PersonProps = {
    age: 80,
    gender: 'male',
    hobby: ['basketball', 'football'],
    position: { x: 10, y: 10 },
    friends: [{ name: 'zhangsan', age: 10 }, { name: 'lisi', age: -20 }]
}
ReactDOM.render(<Person {...personProps} />, root);

6. 虚拟DOM #

6.1 src\index.tsx #

src\index.tsx

import React from './react';
import ReactDOM from './react-dom';
import { FunctionComponentElement, ComponentElement } from './typings';
//let element = <h1 className="title" style={{color:'red',fontSize:'24px'}}></h1>
//let element = React.createElement('h1', { className: 'title', style: { color: 'red', fontSize: '50px' } }, 'hello');
//console.log(JSON.stringify(element));
interface Props {
    title: string
}
/* 
function Welcome(props: Props) {
    return React.createElement('h1', { className: 'title' }, props.title);
} 
let element: FunctionComponentElement<Props> = React.createElement<Props>(Welcome, { title: '标题' }) as FunctionComponentElement<Props>;
*/
class Welcome extends React.Component {
    render() {
        return React.createElement('h1', { className: 'title' }, this.props.title);
    }
}
let element: ComponentElement<Props> = React.createElement<Props>(Welcome, { title: '标题' }) as ComponentElement<Props>;
ReactDOM.render<Props>(element, document.getElementById('root') as HTMLElement);

6.2 src\react.tsx #

src\react.tsx

import { FunctionComponent, ComponentClass, ReactElement, PropsWithChildren, Component } from './typings';

export function createElement<P>(type: string | FunctionComponent<P> | ComponentClass<P>, config: P, ...children: Array<ReactElement | string>): ReactElement<P> {
    let props = { ...config, children };
    const element: ReactElement<PropsWithChildren<P>, FunctionComponent<PropsWithChildren<P>> | ComponentClass<PropsWithChildren<P>> | string> = {
        type: type, props,
    };
    return element;
}

export default {
    createElement, Component
}

6.3 src\typings.tsx #

src\typings.tsx

export interface Component<P = any> {
    render(): ReactElement
}
export class Component<P = any> {
    static isReactComponent: boolean = true;
    constructor(public props: P) {
        this.props = props;
    }
}

export type JSXElementConstructor<P> = ((props: P) => ReactElement) | (new (props: P) => Component<P>);

export interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
}
export type PropsWithChildren<P> = P & { children?: Array<ReactElement | string> };
export interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>): ReactElement;
}

export interface ComponentClass<P = {}> {
    new(props: P, context?: any): Component<P>;
}
export interface FunctionComponentElement<P> extends ReactElement<P, FunctionComponent<P>> {

}
export interface ComponentElement<P> extends ReactElement<P, ComponentClass<P>> {

}
export interface CSSProperties extends Record<string, any> {
    [key: string]: any
}
export interface HTMLAttributes {
    className?: string;
    style?: CSSProperties;
}

6.4 src\react-dom.tsx #

src\react-dom.tsx


import { FunctionComponent, ComponentClass, ReactElement, PropsWithChildren, HTMLAttributes } from './typings';
function render<P>(element: ReactElement<PropsWithChildren<P>, FunctionComponent<PropsWithChildren<P>> | ComponentClass<PropsWithChildren<P>> | string> | string, container: HTMLElement) {
    if (typeof element == 'string') {
        return container!.appendChild(document.createTextNode(element))
    }
    let type, props: HTMLAttributes & P & Record<string, any>;
    type = element.type;
    props = element.props;
    if ((type as any).isReactComponent) {//如果为true说明它是一个类组件
        element = new (type as ComponentClass<PropsWithChildren<P>>)(props as PropsWithChildren<P>).render();
        type = element.type;
        props = element.props;
    } else if (typeof type == 'function') {
        element = (type as FunctionComponent<P>)(props);
        type = element.type;
        props = element.props;
    }
    let domElement = document.createElement(type as string);
    for (let propName in props) {
        if (propName === 'children') {
            let children: Array<ReactElement | string> | undefined = props.children;
            if (children) {
                children.forEach((child: ReactElement | string) => render(child, domElement));
            }
        } else if (propName === 'className') {
            if (props.className)
                domElement.className = props.className;
        } else if (propName === 'style') {
            let styleObj = props.style;
            /**
            for(let attr in styleObj){
                domElement.style[attr] =  styleObj[attr];
            }
             */
            if (styleObj) {
                let cssText = Object.keys(styleObj).map(attr => {
                    return `${attr.replace(/([A-Z])/g, function () { return "-" + arguments[1] })}:${styleObj![attr]}`;
                }).join(';');
                domElement.style.cssText = cssText;
            }

        } else {
            domElement.setAttribute(propName, props[propName]);
        }
    }
    container!.appendChild(domElement);
}
export default { render };

6. 状态 #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props {

}
interface State {
    date: any
}
class Clock extends React.Component<Props, State>{
    timerID
    constructor(props) {
        super(props);
        this.state = { date: new Date() };
    }

    componentDidMount() {
        this.timerID = setInterval(
            () => this.tick(),
            1000
        );
    }

    componentWillUnmount() {
        clearInterval(this.timerID);
    }

    tick() {
        this.setState({
            date: new Date()
        });
    }

    render() {
        return (
            <div>
                <h1>Hello, world!</h1>
                <h2>It is {this.state.date.toLocaleTimeString()}</h2>
            </div>
        );
    }
}

ReactDOM.render(
    <Clock />,
    document.getElementById('root')
);

6.1 不要直接修改 State #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props {
}
interface State {
    number: number
}
class Counter extends React.Component<Props, State> {
    timerID
    constructor(props) {
        super(props);
        this.state = {
            number: 0
        };
    }

    componentDidMount() {
        this.timerID = setInterval(
            () => {
                this.setState({ number: this.state.number + 1 });
                //this.state.number = this.state.number + 1;
            },
            1000
        );
    }

    componentWillUnmount() {
        clearInterval(this.timerID);
    }

    render() {
        return (
            <div >
                <p> {this.state.number} </p>
            </div>
        );
    }
}

ReactDOM.render(<
    Counter />,
    document.getElementById('root')
);

6.2 State 的更新可能是异步的 #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props {
}
interface State {
    number: number
}
class Counter extends React.Component<Props, State> {
    constructor(props) {
        super(props);
        this.state = {
            number: 0
        };
    }
    handleClick = () => {
        //this.setState({number:this.state.number+1});
        //console.log(this.state.number);
        //this.setState({number:this.state.number+1});

        this.setState((state) => (
            { number: state.number + 1 }
        ));
        this.setState((state) => (
            { number: state.number + 1 }
        ));
    }
    render() {
        return (
            <div >
                <p> {this.state.number} </p>
                <button onClick={this.handleClick}>+</button>
            </div>
        );
    }
}

ReactDOM.render(<
    Counter />,
    document.getElementById('root')
);

6.3 State 的更新会被合并 #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props {

}
interface State {
    name: string;
    number: number
}
class Counter extends React.Component<Props, State> {
    constructor(props) {
        super(props);
        this.state = {
            name: 'zhufeng',
            number: 0
        };
    }
    handleClick = () => {
        //this.setState({number:this.state.number+1});
        //console.log(this.state.number);
        //this.setState({number:this.state.number+1});

        this.setState((state) => (
            { number: state.number + 1 }
        ));
        this.setState((state) => (
            { number: state.number + 1 }
        ));
    }
    render() {
        return (
            <div >
                <p>{this.state.name}: {this.state.number} </p>
                <button onClick={this.handleClick}>+</button>
            </div>
        );
    }
}

ReactDOM.render(<
    Counter />,
    document.getElementById('root')
);

6.4 数据是向下流动的 #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props {

}
interface State {
    name: string;
    number: number
}
class Counter extends React.Component<Props, State> {
    constructor(props) {
        super(props);
        this.state = {
            name: 'zhufeng',
            number: 0
        };
    }
    handleClick = () => {
        this.setState((state) => (
            { number: state.number + 1 }
        ));
    }
    render() {
        return (
            <div style={{ border: '1px solid red' }}>
                <p>{this.state.name}: {this.state.number} </p>
                <button onClick={this.handleClick}>+</button>
                <SubCounter number={this.state.number} />
            </div>
        );
    }
}
class SubCounter extends React.Component<{ number: number }> {
    render() {
        return <div style={{ border: '1px solid blue' }}>子计数器:{this.props.number}</div>;
    }
}
ReactDOM.render(
    <Counter />,
    document.getElementById('root')
);

7. 事件处理 #

import React from 'react';
import ReactDOM from 'react-dom';
class Link extends React.Component {
    handleClick(e: React.MouseEvent) {
        e.preventDefault();
        alert('The link was clicked.');
    }

    render() {
        return (
            <a href="http://www.baidu.com" onClick={this.handleClick}>
                Click me
          </a>
        );
    }
}

ReactDOM.render(
    <Link />,
    document.getElementById('root')
);

7.2 this #

class LoggingButton extends React.Component {
    handleClick() {
        console.log('this is:', this);
    }
    handleClick1 = () => {
        console.log('this is:', this);
    }
    render() {
        //onClick={this.handleClick.bind(this)
        return (
            <button onClick={(event) => this.handleClick(event)}>
                Click me
        </button>
        );
    }
}

7.3 向事件处理程序传递参数 #

import React from 'react';
import ReactDOM from 'react-dom';
class LoggingButton extends React.Component {
    handleClick = (id, event: React.MouseEvent) => {
        console.log('id:', id);
    }
    render() {
        return (
            <>
                <button onClick={(event) => this.handleClick('1', event)}>
                    Click me
            </button>
                <button onClick={this.handleClick.bind(this, '1')}>
                    Click me
            </button>
            </>
        );
    }
}
ReactDOM.render(
    <LoggingButton />,
    document.getElementById('root')
);

7.4 Ref #

7.4.1 ref的值是一个字符串 #

import React from 'react';
import ReactDOM from 'react-dom';
class Sum extends React.Component {
    handleAdd = (event: React.MouseEvent) => {
        let a = (this.refs.a as HTMLInputElement).value;
        let b = (this.refs.b as HTMLInputElement).value;
        (this.refs.c as HTMLInputElement).value = a + b;
    }

    render() {
        return (
            <>
                <input ref="a" />+<input ref="b" /><button onClick={this.handleAdd}>=</button><input ref="c" />
            </>
        );
    }
}
ReactDOM.render(
    <Sum />,
    document.getElementById('root')
);

7.4.2 ref的值是一个函数 #

import React from 'react';
import ReactDOM from 'react-dom';
class Sum extends React.Component {
    a
    b
    result
    handleAdd = (event) => {
        let a = this.a.value;
        let b = this.b.value;
        this.result.value = a + b;
    }
    render() {
        return (
            <>
                <input ref={ref => this.a = ref} />+<input ref={ref => this.b = ref} /><button onClick={this.handleAdd}>=</button><input ref={ref => this.result = ref} />
            </>
        );
    }
}
ReactDOM.render(
    <Sum />,
    document.getElementById('root')
);

7.4.3 为 DOM 元素添加 ref #

import React from 'react';
import ReactDOM from 'react-dom';
class Sum extends React.Component {
    a
    b
    result
    constructor(props) {
        super(props);
        this.a = React.createRef();
        this.b = React.createRef();
        this.result = React.createRef();
    }
    handleAdd = () => {
        let a = this.a.current.value;
        let b = this.b.current.value;
        this.result.current.value = a + b;
    }
    render() {
        return (
            <>
                <input ref={this.a} />+<input ref={this.b} /><button onClick={this.handleAdd}>=</button><input ref={this.result} />
            </>
        );
    }
}
ReactDOM.render(
    <Sum />,
    document.getElementById('root')
);

7.4.4 为 class 组件添加 Ref #

import React from 'react';
import ReactDOM from 'react-dom';
class Form extends React.Component {
    input
    constructor(props) {
        super(props);
        this.input = React.createRef();
    }
    getFocus = () => {
        this.input.current.getFocus();
    }
    render() {
        return (
            <>
                <TextInput ref={this.input} />
                <button onClick={this.getFocus}>获得焦点</button>
            </>
        );
    }
}
class TextInput extends React.Component {
    input
    constructor(props) {
        super(props);
        this.input = React.createRef();
    }
    getFocus = () => {
        this.input.current.focus();
    }
    render() {
        return <input ref={this.input} />
    }
}
ReactDOM.render(
    <Form />,
    document.getElementById('root')
);

7.4.5 Ref转发 #

使用forwardRef

import React from 'react';
import ReactDOM from 'react-dom';
interface InputProps { }
const TextInput = React.forwardRef((props: InputProps, ref: React.Ref<HTMLInputElement>) => (
    <input ref={ref} />
));
class Form extends React.Component {
    input
    constructor(props) {
        super(props);
        this.input = React.createRef();
    }
    getFocus = () => {
        console.log(this.input.current);

        this.input.current.focus();
    }
    render() {
        return (
            <>
                <TextInput ref={this.input} />
                <button onClick={this.getFocus}>获得焦点</button>
            </>
        );
    }
}

ReactDOM.render(
    <Form />,
    document.getElementById('root')
);

8.实现 #

8.1 src\index.js #

src\index.js

import React from './react';
import ReactDOM from './react-dom';
class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { number: 0 };
    }
    handleClick = () => {
        this.setState({ number: this.state.number + 1 });
        console.log(this.state);

    }
    render() {
        return (
            <div>
                <p>number:{this.state.number}</p>
                <button onClick={this.handleClick}>+</button>
            </div>
        )
    }
}
ReactDOM.render(<Counter title="计数器" />, document.getElementById('root'));

8.2 react\index.js #

src\react\index.js

import { _render } from '../react-dom';
import { useDebugValue } from 'react';
function createElement(type, config, ...children) {
    let props = { ...config, children };
    let element = { type, props };
    return element;
}
function renderComponent(componentInstance) {
    const renderElement = componentInstance.render();
    const newDOM = _render(renderElement.type, renderElement.props, componentInstance);
    componentInstance.dom.parentNode.replaceChild(newDOM, componentInstance.dom);
    componentInstance.dom = newDOM;
}
class Component {
    static isReactComponent = true
    constructor(props) {
        this.props = props;
        this.updateQueue = [];
        this.isBatchUpdate = false;
    }
    setState = (partialState) => {
        this.updateQueue.push(partialState);
        if (!this.isBatchUpdate)
            this.flushState();
    }
    flushState = () => {
        this.state = this.updateQueue.reduce((acc, cur) => {
            acc = { ...acc, ...cur };
            return acc;
        }, this.state);
        renderComponent(this);
    }
}

export default {
    createElement,
    Component
}

8.3 react-dom\index.js #

src\react-dom\index.js

/**
ReactDOM.render(element, document.getElementById('root'));
element是要渲染的元素 parent是真实的DOM元素
*/
function render(element, parent, componentInstance) {
    if (typeof element === 'string' || typeof element === 'number') {
        return parent.appendChild(document.createTextNode(element));
    }
    let type = element.type, props = element.props;
    let isReactComponent = type.isReactComponent;
    if (isReactComponent) {
        componentInstance = new type(props);
        element = componentInstance.render();
        type = element.type;
        props = element.props;
    } else if (typeof type === 'function') {
        element = type(props);
        type = element.type;
        props = element.props;
    }
    let dom = _render(type, props, componentInstance);
    if (componentInstance && isReactComponent) {
        componentInstance.dom = dom;
    }
    parent.appendChild(dom);
}
class SyntheticEvent {
    constructor() {
        this.events = {};
    }
    on = (type, dom, handler, componentInstance) => {
        if (this.events[type]) {
            this.events[type].push({ dom, handler, componentInstance });
        } else {
            this.events[type] = [{ dom, handler, componentInstance }];
        }
    }
    emit = (type, event) => {
        let listeners = this.events[type];
        listeners && listeners.forEach(item => {
            if (event.target === item.dom) {
                if (item.componentInstance) {
                    item.componentInstance.isBatchUpdate = true;
                }
                item.handler(event);
                if (item.componentInstance) {
                    item.componentInstance.isBatchUpdate = false;
                    item.componentInstance.flushState();
                }
            }
        });

    }
}
let syntheticEvent = new SyntheticEvent();
document.onclick = syntheticEvent.emit.bind(null, 'onclick');
export function _render(type, props, componentInstance) {
    let dom = document.createElement(type);
    for (let propName in props) {
        if (propName === 'children') {
            let children = props.children;
            children.forEach(child => render(child, dom, componentInstance));
        } else if (propName === 'style') {
            let styleObj = props.style;
            for (let attr in styleObj)
                dom.style[attr] = styleObj[attr];
        } else if (propName === 'className') {
            dom.className = props.className
        } else if (propName.startsWith('on')) {
            //currentDOM[propName.toLowerCase()] = props[propName];
            syntheticEvent.on(propName.toLowerCase(), dom, props[propName], componentInstance);
        } else {
            dom.setAttribute(propName, props[propName]);
        }
    }
    return dom;
}
export default {
    render
}

8.生命周期 #

8.1 旧版生命周期 #

react15

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
interface Props { }
interface State { number: number }
class Counter extends React.Component<Props, State> { // 他会比较两个状态相等就不会刷新视图 PureComponent是浅比较
    static defaultProps = {
        name: '珠峰架构'
    };
    constructor(props) {
        super(props);
        this.state = { number: 0 }
        console.log('1.constructor构造函数')
    }
    componentWillMount() { // 取本地的数据 同步的方式:采用渲染之前获取数据,只渲染一次
        console.log('2.组件将要加载 componentWillMount');
    }
    componentDidMount() {
        console.log('4.组件挂载完成 componentDidMount');
    }
    handleClick = () => {
        this.setState({ number: this.state.number + 1 });
    };
    // react可以shouldComponentUpdate方法中优化 PureComponent 可以帮我们做这件事
    shouldComponentUpdate(nextProps, nextState): boolean { // 代表的是下一次的属性 和 下一次的状态
        console.log('5.组件是否更新 shouldComponentUpdate');
        return nextState.number % 2 == 0;
        // return nextState.number!==this.state.number; //如果此函数种返回了false 就不会调用render方法了
    } //不要随便用setState 可能会死循环
    componentWillUpdate() {
        console.log('6.组件将要更新 componentWillUpdate');
    }
    componentDidUpdate() {
        console.log('7.组件完成更新 componentDidUpdate');
    }
    render() {
        console.log('3.render');
        return (
            <div>
                <p>{this.state.number}</p>
                {this.state.number > 3 ? null : <ChildCounter n={this.state.number} />}
                <button onClick={this.handleClick}>+</button>
            </div>
        )
    }
}
class ChildCounter extends Component<{ n: number }> {
    componentWillUnmount() {
        console.log('组件将要卸载componentWillUnmount')
    }
    componentWillMount() {
        console.log('child componentWillMount')
    }
    render() {
        console.log('child-render')
        return (<div>
            {this.props.n}
        </div>)
    }
    componentDidMount() {
        console.log('child componentDidMount')
    }
    componentWillReceiveProps(newProps) { // 第一次不会执行,之后属性更新时才会执行
        console.log('child componentWillReceiveProps')
    }
    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.n % 3 == 0; //子组件判断接收的属性 是否满足更新条件 为true则更新
    }
}
ReactDOM.render(<Counter />, document.getElementById('root'));
// defaultProps
// constructor
// componentWillMount
// render
// componentDidMount
// 状态更新会触发的
// shouldComponentUpdate nextProps,nextState=>boolean
// componentWillUpdate
// componentDidUpdate
// 属性更新
// componentWillReceiveProps newProps
// 卸载
// componentWillUnmount

8.2 新版生命周期 #

react16

8.2.1 getDerivedStateFromProps #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props { }
interface State { number: number }
class Counter extends React.Component<Props, State> {
    static defaultProps = {
        name: '珠峰架构'
    };
    constructor(props) {
        super(props);
        this.state = { number: 0 }
    }

    handleClick = () => {
        this.setState({ number: this.state.number + 1 });
    };

    render() {
        console.log('3.render');
        return (
            <div>
                <p>{this.state.number}</p>
                <ChildCounter number={this.state.number} />
                <button onClick={this.handleClick}>+</button>
            </div>
        )
    }
}
class ChildCounter extends React.Component<{ number: number }, { number: number }> {
    constructor(props) {
        super(props);
        this.state = { number: 0 };
    }
    static getDerivedStateFromProps(nextProps, prevState) {
        const { number } = nextProps;
        // 当传入的type发生变化的时候,更新state
        if (number % 2 == 0) {
            return { number: number * 2 };
        } else {
            return { number: number * 3 };
        }
        // 否则,对于state不进行任何操作
        return null;
    }
    render() {
        console.log('child-render', this.state)
        return (<div>
            {this.state.number}
        </div>)
    }

}

ReactDOM.render(
    <Counter />,
    document.getElementById('root')
);

8.2.2 getSnapshotBeforeUpdate #

import React from 'react';
import ReactDOM from 'react-dom';
interface Props { }
interface State { messages: Array<string> }
class ScrollingList extends React.Component<Props, State> {
    wrapper
    timeID
    constructor(props) {
        super(props);
        this.state = { messages: [] }
        this.wrapper = React.createRef();
    }

    addMessage() {
        this.setState(state => ({
            messages: [`${state.messages.length}`, ...state.messages],
        }))
    }
    componentDidMount() {
        this.timeID = window.setInterval(() => {//设置定时器
            this.addMessage();
        }, 1000)
    }
    componentWillUnmount() {//清除定时器
        window.clearInterval(this.timeID);
    }
    getSnapshotBeforeUpdate() {//很关键的,我们获取当前rootNode的scrollHeight,传到componentDidUpdate 的参数perScrollHeight
        return this.wrapper.current.scrollHeight;
    }
    componentDidUpdate(pervProps, pervState, prevScrollHeight) {
        const curScrollTop = this.wrapper.current.scrollTop;//当前向上卷去的高度
        //当前向上卷去的高度加上增加的内容高度
        this.wrapper.current.scrollTop = curScrollTop + (this.wrapper.current.scrollHeight - prevScrollHeight);
    }
    render() {
        let style = {
            height: '100px',
            width: '200px',
            border: '1px solid red',
            overflow: 'auto'
        }
        return (
            <div style={style} ref={this.wrapper} >
                {this.state.messages.map((message, index) => (
                    <div key={index}>{message} </div>
                ))}
            </div>
        );
    }
}

ReactDOM.render(
    <ScrollingList />,
    document.getElementById('root')
);

参考 #