为什么要学习React?
起源于Fackbook内部项目,2013年5月开源
声明式编程 what vs how(命令式编程)
高效(通过虚拟DOM,最大程度减少与DOM的交互)
灵活(与已知的库或框架可以很好地配合)
JSX(JavaScript + XML)JavaScript语法的扩展
组件化(通过React构建组件,增加代码复用性)
单向数据流(顶层组件控制数据的变化,父组件流向子组件,降低副作用)
React内容:
React基础
React Hooks
React-router
Redux
组件库
Immutable
Mobx
React + TS
单元测试
dva + umi
虚拟DOM
传统DOM的更新:真实页面对应一个DOM树。传统开发模式中,每次更新页面时,都要手动操作DOM进行更新。100条DATA -> 99条DATA 【销毁100条再创建99条】
虚拟DOM思想:DOM的操作非常昂贵。在前端开发中,性能消耗最大的就是DOM操作,而且这部分代码会让整体项目的代码变得难以维护。React把真实DOM树转换为JavaScript对象树,即VirtualDOM。类似于{ type:'div', text: 'I am div' },数据变化会经过新旧VDOM对象经过diff算法进行对比后,以最小的更新代价,再进行一次真实DOM的更新。
create-react-app
1. 全局安装create-react-app
npm install -g create-react-app
2. 创建一个项目
create-react-app 项目名称
3. 如果不想全局安装,也可以使用临时安装npx
npx create-react-app 项目名称
这个过程需要一段时间,主要会安装这三个东西
- react: react的顶级库
- react-dom: 因为react有很多运行环境,如app端的react-native,web上就是react-dom
- react-scripts: 包含运行和打包react应用程序的所有脚本和配置
JSX语法与组件
JSX语法:JSX将HTML语法直接加入到JavaScript代码中,再通过翻译器转换到纯JavaScript后再由浏览器执行。在实际开发中,JSX在产品打包阶段都已经编译成纯Javascript,不会带来任何副作用,反而会让代码变得更加直观并易于维护。编程过程由Babel的JSX编译器实现。
Class组件
ES6加入的让JavaScript直接支持使用class定义一个类,react创建组件的方式就是使用类的继承,ES6class是目前官方推荐的使用方式,它使用ES6标准语法来构建。
注意点:
1. 类名使用大驼峰
2. return 的内容换行就要加小括号()
3. JSX要有一个根节点,可以使用幽灵节点<></>
import React from "react"
class App extends React.Component {
render() {
return (
<>
<h2>App</h2>
</>
)
}
}
export default App;
函数组件
function App () {
return (
<>
<h2>App</h2>
</>
)
}
export default App
注意:函数式组件v16.8版本之前被认为是无状态组件,
v16.8之后引入了React hooks,函数组件就有了状态
组件的嵌套
import React, { Component } from 'react'
class NavBar extends Component {
render () {
return <div>NavBar -- <NavBarChild></NavBarChild></div>
}
}
function NavBarChild() {
return <div>NavBarChild</div>
}
function Swiper () {
return <div>Swiper</div>
}
const TabBar = () => {
return <div>TabBar</div>
}
export default class App extends Component {
render () {
return (
<div>
<NavBar></NavBar>
<Swiper></Swiper>
<TabBar></TabBar>
</div>
)
} 1
}
组件的样式
行内样式写法
import React, { Component } from 'react'
export default class App extends Component {
render () {
const textStyle = { color: 'green', fontSize: 24, backgroundColor: 'orange' }
return (
<>
<div style={{ color: 'red', fontSize: 24 }}>I am Text1</div>
<div style={textStyle}>I am Text2</div>
</>
)
}
}
外部css导入写法
先定义外部index.css样式文件
.text {
color: blue;
font-size: 26px;
font-weight: 600;
}
引入并使用
import React, { Component } from 'react'
import './index.css' // 引入样式文件
export default class App extends Component {
render () {
const textStyle = { color: 'green', fontSize: 24, backgroundColor: 'orange' }
return (
<>
{/* style行内写法 */}
<div style={{ color: 'red', fontSize: 24 }}>I am Text1</div>
<div style={textStyle}>I am Text2</div>
{/* className写法 */}
<div className='text'>I am made by className</div>
<label htmlFor="username">请输入用户名:</label>
<input type="text" id='username' />
</>
)
}
}
注意:
React推荐我们使用行内样式,因为React认为每一个组件都是一个独立的个体。
其实我们大多数情况下还是大量在为元素添加类名。但是需要注意的是在JSX中,class属性要写为className,for属性要写成htmlFor。(因为jsx/js中class和for为关键字)。
事件绑定
import React, { Component } from 'react'
export default class App extends Component {
a = 666
// 推荐函数写法(不存在this指向问题)
handleAdd = () => {
console.log('add')
}
handleChange = (e, param1, param2) => {
console.log(e.target.value)
console.log(`params: ${param1} ${param2}`)
}
handleSend (param1, param2, param3) {
console.log(param1, param2, param3)
}
render () {
return (
<div>
{/* 传递e和参数的写法 */}
<input type="text" onChange={(e) => this.handleChange(e, 'a', 'b')} />
<button onClick={this.handleAdd}>Add</button>
{/* 传递参数写法 */}
<button onClick={() => this.handleSend(1, 2, 3)}>send</button>
</div>
)
}
}
React中的事件绑定:
React并不会真正地绑定事件到每一个具体元素上,而是采用事件代理的模式。
React中的event对象:
和普通浏览器一样,事件handler会被自动传入一个event对象,这个对象和普通浏览器的event对象所包含的方法和属性都基本一致。不同的是React中的event对象不是浏览器提供的,而是·它自己内部构建的。它同样具有event.stopPropagation、event.preventDefault这种常用的方法。
ref的使用
1. 给标签或组件设置ref="myText",通过这个获取this.$refs.myText,ref可以拿到该真实DOM或组件对象.(该方法即将启用,在严格模式下会报错)
import React, { Component } from 'react'
export default class App extends Component {
handleAdd = () => {
console.log(this.refs.myText.value)
}
render() {
return (
<div>
<input type="text" ref='myText'/>
<button onClick={() => this.handleAdd()}>add</button>
</div>
)
}
}
2. 另一种写法(推荐)
import React, { Component } from 'react'
export default class App extends Component {
myText = React.createRef()
handleAdd = () => {
console.log(this.myText.current)
console.log(this.myText.current.value)
}
render() {
return (
<div>
<input type="text" ref={this.myText}/>
<button onClick={() => this.handleAdd()}>add</button>
</div>
)
}
}
组件数据挂载方式
状态(state)是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同状态下使用组件的显示不同(自己管理)。
state初体验demo
import React, { Component } from 'react'
export default class App extends Component {
// 状态
state = {
isSave: 'false'
}
// button状态切换
handleClick = () => {
this.setState({
isSave: !this.state.isSave
})
}
// render
render () {
return (
<div>
<button onClick={this.handleClick}>{this.state.isSave ? '保存' : '取消'}</button>
</div>
)
}
}
注意:this.state是纯js对象,在Vue中,data属性是利用了Object.defineProperty或Proxy处理过的,更改data数据的时候会触发数据的getter和setter,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以需要使用特殊的更改状态的方法setState。
补充另一种写法:也可以把state写在类组件的构造器constructor中
import React, { Component } from 'react'
export default class App extends Component {
// constructor
constructor() {
super()
// 状态
this.state = {
isSave: 'false'
}
}
// button状态切换
handleClick = () => {
this.setState({
isSave: !this.state.isSave
})
}
// render
render () {
return (
<div>
<button onClick={this.handleClick}>{this.state.isSave ? '保存' : '取消'}</button>
</div>
)
}
}
列表渲染
import React, { Component } from 'react'
export default class App extends Component {
state = {
// 列表数据
cityList: [
{ id: 1, name: 'ShangHai' },
{ id: 2, name: 'BeiJing' },
{ id: 3, name: 'London' },
]
}
render () {
return (
<div>
<ul>
{this.state.cityList.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
}
注意:key的问题
<li>1</li><li>2</li><li>3</li> ->>> <li>1</li><li>3</li>
key值起到标识作用,使新的VDOM尽可能的复用旧的VDOM
TodoList小案例
import React, { Component } from 'react'
import { v4 as uuid } from 'uuid'
export default class App extends Component {
state = {
inputValue: '',
showList: [
{ id: 1, text: 'aaa' },
{ id: 2, text: 'bbb' },
{ id: 3, text: 'ccc' },
]
}
handleInputChange = (e) => {
this.setState({
inputValue: e.target.value
})
}
handleAdd = () => {
this.setState({
showList: [...this.state.showList, { id: uuid(), text: this.state.inputValue }],
inputValue: ''
})
}
handleDelete = (curItem) => {
this.setState({
showList: this.state.showList.filter(item => item.id !== curItem.id)
})
}
render () {
return (
<div>
<label htmlFor="addInput">请输入:</label><input type="text" id='addInput' value={this.state.inputValue} onChange={(e) => this.handleInputChange(e)} />
<button onClick={this.handleAdd}>添加</button>
<ul>
{this.state.showList.map((item, index) => <li key={item.id}>
{/* dangerouslySetInnerHTML富文本编辑器=>解析出代码片段*/}
<span dangerouslySetInnerHTML={{
__html: item.text
}}></span>
<button onClick={() => this.handleDelete(item)}>删除</button></li>)}
</ul>
{/* 条件渲染 */}
{this.state.showList.length === 0 ? <h2>暂无待办事项</h2> : null}
{this.state.showList.length === 0 && <h2>暂无待办事项哦~</h2>}
</div>
)
}
}