React的初体验

377 阅读4分钟

一、背景

在最近一段时间,经常被人问起React,脑子里面只是存在一个JSX的概念,何况在毕业一年半的时间里,工作日常中只接触到Vue,对Vue再熟悉不过。但是React框架具体怎么玩?真是一知半解。
于是便下定决心来把React学一学,看看这葫芦里到底装的什么药。

二、学习路径

官方文档

react.docschina.org/docs/thinki…

楼主花了一个多小时,阅读了 React 的文档,主要阅读的是 React 的核心概念

学习笔记

  • 组件声明
// ES5 函数组件
function Welcome (props) {
    return <div>Hello, {props.name}</div>
}
// ES6 继承写法
class Welcome extends React.Component {
    render () {
        return <div>Hello, {this.props.name}</div>
    }
}
  • 组件名称必须以 大写字母 开头
  • Props
    Props 具有只读性,React 组件都必须像纯函数一样保护它们的 props 不被更改
  • State
    正确地使用 State
  1. 不要直接修改 State
  2. State 的更新可能是异步的
  3. State 的更新会被合并

数据是向下流动的 叫做“自上而下”或是“单向”的数据流 任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件

  • 生命周期
  1. componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
  2. componentWillUnmount() 方法会在组件即将从 DOM 中移除前 运行
  • 事件处理
    React 事件的命名采用小驼峰式(camelCase),而不是纯小写。 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串
  • 条件渲染
    在组件的 render 方法中返回 null 并不会影响组件的生命周期
  • 列表 & key
    key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值 元素的 key 只有放在就近的数组上下文中才有意义
  • 受控组件
    渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”
  • 状态提升(组件化)
    自上而下的数据溜 在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。 通常,state 都是首先添加到需要渲染数据的组件中去。 然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中
  • 组合 vs 继承
    react 中没有 slot 插槽的概念,通过 props.xxx 或者 props.children 来实现。而继承基本没用
  • React 哲学
    React 单向数据流(也叫单向绑定)的思想使得组件模块化

三、实践

自己实现一个小项目 需要事先花半个小时,阅读 create-react-app 的官方文档

create-react-app.dev/docs/docume…

初始化工程

直接使用官网推荐的脚手架搭建工程

$ npx create-react-app 'better-react'
$ cd 'better-react'
$ yarn install
$ yarn start

运用 Sass 编写样式

文档:create-react-app.dev/docs/adding…

$ npm install node-sass --save
$ # or
$ yarn add node-sass

安装完毕后,我们就可以快乐的使用Sass编写样式了

@import 'styles/_colors.scss';

四、项目结构

大体结构:

graph LR
src-->common
src-->components
        components-->AddBtn
        components-->list
        components-->ListItem
src-->App.js

(掘金居然不支持流程图。。)

image.png

五、具体代码

  • AddBtn.js
/*
 * @Author: cunhang_wwei
 * @Date: 2021-03-16 20:04:36
 */

import React from 'react'
import './btn.scss'

class AddBtn extends React.Component {
    constructor (props) {
        super(props)

        this.handleClick  = this.handleClick.bind(this)
    }

    handleClick (event) {
        event.preventDefault()
        console.log('add')
    }

    render () {
        return (
            <button onClick={this.handleClick} className='good-btn'>添加</button>
        )
    }
}

export default AddBtn
  • list.js
/*
 * @Author: cunhang_wei
 * @Date: 2021-03-13 12:38:01
 */
// import React from 'react'
import React from 'react'
import './list.scss'
import ListItem from '../ListItem/list-item'
import AddBtn from '../AddBtn/btn'

class List extends React.Component {
    constructor (props) {
        // 拿到组件实例的 props
        super(props)
        // state 创建组件自身的数据,类似于 Vue 组件里的 data
        this.state = {
            listData: [],
            count: 0
        }

        for (let i = 0; i < 10; i++) {
            this.state.listData.push({
                title: '尝试一下React',
                content: 'React真不错,真香!'
            })
        }
        // 构造要渲染的 JSX 实例数组
        this.lists = this.state.listData.map((item, index) =>
            <ListItem photoUrl="" title={item.title} content={item.content} key={index}></ListItem>
        )
        // 修改方法的指针,指向当前组件实例
        this.plus = this.plus.bind(this)
    }

    plus () {
        // 单向数据流,需要手动 setState 更新数据
        this.setState(state => ({
            count: state.count + 1
        }))
    }

    componentDidMount () {
        // 组件挂载后 开始跑计时器
        this.interval = setInterval(() => this.plus(), 1000)
    }

    componentWillUnmount () {
        clearInterval(this.interval)
    }

    render () {
        return (
            <div className='list'>
                <p>你在当前页面大概停留了{this.state.count}秒</p>
                {this.lists}
                <AddBtn></AddBtn>
            </div>
        )
    }
}

export default List
  • ListItem.js
/*
 * @Author: cunhang_wei
 * @Date: 2021-03-16 19:17:31
 */

import React from 'react'
import './list-item.scss'

class ListItem extends React.Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
    }

    handleClick () {
        console.log('Is Me!')
    }

    render () {
        return (
            <div onClick={this.handleClick} className='list-item'>
                <p className='title'>{this.props.title}</p>
                <span className='content'>{this.props.content}</span>
            </div>
        )
    }
}

export default ListItem

六、最终的效果

GIF.gif

代码github地址:github.com/AFine970/my…

最后,好好学习不会差!我是爱你们的航少,看完记得点个赞