模块与组件的理解

  1. 模块
    • 向外提供特定功能的js 程序, 一般就是一个js 文件
    • 复用js, 简化js 的编写, 提高js 运行效率
  2. 组件
    • 用来实现特定(局部)功能效果的代码集合(html/css/js)
    • 复用编码, 简化项目编码, 提高运行效率

组件的基本使用

创建组件有两种方式

  1. 工厂函数组件

    简单组件,通过函数返回值即可。函数名就是组件(标签)名。

    function MyComponent() {
        return <h2>工厂函数组件(简单组件)</h2>
    }
    ReactDOM.render(<MyComponent />, document.getElementById('test'))
  2. ES6类组件

    复杂组件,通过ES6类中的render方法。

    class MyComponent2 extends React.Component {
        render() {
            return <h2>ES6类组件(复杂组件)</h2>
        }
    }
    ReactDOM.render(<MyComponent2 />, document.getElementById('test2'))

image-20201114091744893

组件三大属性

state

state用于记录属性,其声明在constructor中,值是一个对象。(简单组件中没有状态)

constructor(props) {
    // 固定写法
    super(props)
    // 设置属性
    this.state = {
        isLikeMe: false
    }
}

render中可以通过this.state.xxx获取相关属性的值。此时的this指向当前对象。

render() {
    // 读取状态
    const { isLikeMe } = this.state
    return <h2 onClick={this.handleClick}>{isLikeMe ? '你喜欢我' : '我喜欢你'}</h2>
}

h2标签绑定事件只需要在类中新增方法即可,但其默认this并不是指向当前对象,而是undefiend因此,需要在constructor将其指向当前对象。

class Like extends React.Component {
    // 接受一个参数
    constructor(props) {
        // 固定写法
        super(props)
        // 设置属性
        this.state = {
            isLikeMe: false
        }
        // 将新增的方法中的this强制绑定为组件对象
        this.handleClick = this.handleClick.bind(this)
    }
    // 新添加方法:内部的this默认不是组件对象,而是undefined
    handleClick() {
        // 得到状态
        const isLikeMe = !this.state.isLikeMe
        // 更新状态
        this.setState({ isLikeMe })
    }
    render() {
        // 读取状态
        const { isLikeMe } = this.state
        return <h2 onClick={this.handleClick}>{isLikeMe ? '你喜欢我' : '我喜欢你'}</h2>
    }
}

ccdeec99-3625-40e9-a51d-ccb01e8db4d2

props

基本用法

function Person(props) {
    return (
        <ul>
            <li>姓名:{props.name}</li>
            <li>年龄:{props.age}</li>
            <li>性别:{props.sex}</li>
        </ul>
    )
}
const p1 = {
    name: 'Tom',
    age: 18,
    sex: '女'
}
ReactDOM.render(<Person name={p1.name} age={p1.age} sex={p1.sex} />, document.getElementById('test'))

属性传入时也可以使用扩展运算符

ReactDOM.render(<Person {...p1} />, document.getElementById('test'))

通过ES6类的继承实现语法

class Test extends React.Component {
    // 接受一个参数
    constructor(props) {
        // 固定写法
        super(props)
    }
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>年龄:{this.props.age}</li>
                <li>性别:{this.props.sex}</li>
            </ul>
        )
    }
}
  1. 默认值

    Person.defaultProps = {
        sex: '男',
        age: 18
    }
  2. 设置属性的类型

    需要引入react-type文件

    // 指定属性类型和必要性
    Person.propTypes = {
        name: PropTypes.string.isRequired,
        age: PropTypes.number
    }

refs和事件处理

  1. 为元素绑定ref

    <input type="text" ref='content' />
  2. 事件中通过this.refs.content即可拿到当前DOM元素

    showInput() {
        const input = this.refs.content
        alert(input.value)
    }

官方推荐的写法并不是使用字符串,而是使用一个函数,将其挂载到React对象中。

<input type="text" ref={input => this.input = input}

此时通过this.input.value即可拿到值。

组件组合使用

f85a8f7c-ca9c-4fb4-850d-3abf22469003

DEMO涉及到三个组件与组件之间的传值。其原则如下

  1. 都需要使用到的数据则放在父组件中
  2. 状态定义在哪个组件,则哪个组件修改状态
  3. 父组件将修改状态的函数作为props属性传递给子组件
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            todos: ['吃饭', '睡觉', '打代码']
        }
        // 绑定this
        this.addItem = this.addItem.bind(this)
    }

    // 添加值的具体方法
    addItem(val) {
        // 将值放到数组最前方
        this.state.todos.unshift(val)
        // 设置状态
        this.setState(this.state)
    }

    render() {
        const {todos} = this.state
        // 将addItem方法作为属性传入到子组件
        return (
            <div>
                <h1>Simple TODO List</h1>
                <Add count={todos.length} addItem={this.addItem}/>
                <List todos={todos}/>
            </div>
        )

    }
}
class Add extends React.Component {
    constructor(props) {
        super(props);
        this.addItem = this.addItem.bind(this)
    }

    addItem() {
        // 获取input的值
        const todo = this.todoInput.value.trim()
        if (!todo) return
        // 调用父组件传来的方法
        this.props.addItem(todo)
        this.todoInput.value = ''
    }

    render() {
        // 为input标签挂载到当前对象的属性
        return (
            <div>
                <input type="text" ref={input => this.todoInput = input}/>
                <button onClick={this.addItem}>add #{this.props.count + 1}</button>
            </div>
        )

    }
}
Add.propsTypes = {
    count: PropTypes.number.isRequired,
    addItem: PropTypes.func
}

List组件渲染

class List extends React.Component {
    render() {
        return (
            <ul>
                {
                    this.props.todos.map((todo, index) => <li key={index}>{todo}</li>)
                                         }
            </ul>
        )

    }
}

List.propsTypes = {
    todos: PropTypes.array.isRequired
}

表单

收集表单数组有两种方式

  1. 受控组件

    表单项输入数据能自动收集成状态

    class LoginForm extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                pwd: ''
            }
            this.handleSubmit = this.handleSubmit.bind(this)
            this.handleChange = this.handleChange.bind(this)
        }
        handleSubmit(e) {
            const { pwd } = this.state
            e.preventDefault()
        }
        handleChange(e) {
            const pwd = e.target.value
            this.setState({ pwd })
        }
        render() {
            return (
                <form action='/test' onSubmit={this.handleSubmit}>
                    密码:<input type="password" value={this.state.pwd} onChange={this.handleChange} />
                    <input type="submit" value="登录" />
                </form>
            )
        }
    }
  2. 非受控组件

    需要时才手动读取表单输入框中的数据

    class LoginForm extends React.Component {
        constructor(props) {
            super(props)
            this.handleSubmit = this.handleSubmit.bind(this)
        }
        handleSubmit(e) {
            const name = this.NameInput.value
            e.preventDefault()
        }
        render() {
            return (
                <form action='/test' onSubmit={this.handleSubmit}>
                    用户名:<input type="text" ref={input => this.NameInput = input} />
                    <input type="submit" value="登录" />
                </form>
            )
        }
    }

生命周期

image-20201115142211647

0a98c3d6-bf58-4be6-ad13-c75e6430e582

class Life extends React.Component {
    constructor(props) {
        super(props)
        // 初始化状态
        this.state = {
            opactity: 1
        }
        this.distroy = this.distroy.bind(this)
    }
    // 挂载后的生命周期
    componentDidMount() {
        // 启动循环定时器
        this.intervalId = setInterval(() => {
            let { opactity } = this.state
            opactity -= 0.1
            if (opactity <= 0) {
                opactity = 1
            }
            this.setState({ opactity })
        }, 100)
    }

    distroy() {
        ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }
    // 销毁时的生命周期
    componentWillUnmount() {
        clearInterval(this.intervalId)
    }
    render() {
        const { opactity } = this.state
        return (
            <div>
                <h2 style={{ opacity: opactity }}>{this.props.msg}</h2>
                <button onClick={this.distroy}>不活了</button>
            </div>
        )
    }
}
  1. 组件的三个生命周期状态

    • Mount插入真实DOM
    • Update被重新渲染
    • Unmount被移出真实DOM
  2. 生命周期流程

    React生命周期流程

  3. 重要的钩子

    • render()

      初始化渲染或更新渲染调用

    • componentDidMount()

      开启监听, 发送ajax 请求

    • componentWillUnmount()

      做一些收尾工作, 如: 清理定时器

    • componentWillReceiveProps()

      后面需要时讲

虚拟DOM和DOM diff算法