一、背景
在最近一段时间,经常被人问起React,脑子里面只是存在一个JSX的概念,何况在毕业一年半的时间里,工作日常中只接触到Vue,对Vue再熟悉不过。但是React框架具体怎么玩?真是一知半解。
于是便下定决心来把React学一学,看看这葫芦里到底装的什么药。
二、学习路径
官方文档
楼主花了一个多小时,阅读了 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
- 不要直接修改 State
- State 的更新可能是异步的
- State 的更新会被合并
数据是向下流动的 叫做“自上而下”或是“单向”的数据流 任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件
-
生命周期
- componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
- 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 的官方文档
初始化工程
直接使用官网推荐的脚手架搭建工程
$ 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
(掘金居然不支持流程图。。)
五、具体代码
- 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
六、最终的效果
代码github地址:github.com/AFine970/my…