React
是Facebook
在2013年5月
推出的开源的函数式编程的前端框架,用于构建用户界面的。拥有较高的性能,声明式设计(采用声明范式)、高效、灵活、JSX语法、组件、单向响应的数据流等特点,已经成为Web开发的主流框架。JSX是JavaScript中的一种语法扩展,类似于XML格式的语言。
React简介
Facebook在
2013年5月
推出的开源的函数式编程的前端框架
React开发环境的搭建
npm i create-react-app -g
全局安装Create-react-app
脚手架create-react-app my-app
使用脚手架初始化项目cd my-app
进入工程目录npm start
启动项目npm run build
打包项目npm run eject
将webpack等配置显示出来便于自定义修改
组件化理解
- 组件的封装:封装视图、数据、变化逻辑
- 组件的复用:、props传递、复用
JSX本质是什么
JSX是语法糖, 需要被解析成JS才能运行, 核心方法是 React.createElement()函数
React基础
- react 双向绑定 和 插值用 {}
{this.state.inputValue}
react 实现类似双向绑定是 state或props发生改变, render函数就会重新执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38// 受控组件 form表单元素 value受 state 控制 只能通过 setState 修改
import React, { Component } from 'react'
class Todo extends Component {
constructor(props){
super(props)
this.state = { inputValue: '' }
}
render(){
return (
<div>
<input value={this.state.inputValue} onChange={this.changeInput.bind(this)} />
<div>{this.state.inputValue}</div>
</div>
)
}
changeInput(e){
this.setState({ inputValue: e.target.value })
}
}
// 非受控组件 通过 ref 控制 表单元素值不受state控制
import React, { Component } from 'react'
class Todo extends Component {
//需要状态时,需要构造函数
constructor(props){
super(props)
}
render(){
return (
<div>
<input value={this.state.inputValue}
ref={(textInput) => {this.textInput = textInput}}
/>
<div>{this.textInput}</div>
</div>
)
}
}
export default Todo事件绑定 onClick 事件名大写 JSX使用变量 {变量}
1
<button onClick={this.btnHandle.bind(this)} >提交</button>
props 属性 组将像一个函数,接受特定得输出,但不能修改
state 状态 组件内部得数据,可以动态改变 this.setState()更新state得唯一路径
注释
1
2
3
4{
//todolist deomo 注释方法一 单行注释必须换行写
}
{/* todolistdemo 注释方法二 */}Fragment 占位符不会在页面中渲染出来
1
2
3
4import React, {Fragment} from 'react'
<Fragment>
<div className="box" ref = 'box'></div>
</Fragment>不能使用class定义类名 class必须写成className
1
<input className = "input" />
style样式
1
<p style="{{fontSize:'40px', color:'blue'}}">
展示html标签
1
2
3<li
dangerouslySetInnerHTML = {{__html: item}}
></li>使用htmlFor代替label中的for
1
2<label htmlFor="ipt">关联input</label>
<input id = "ipt" className = "input" />组件使用及父子组件传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/* 父组件 */
// 引入
import TodoItemList from './TodoItemList'
// 使用 传值
<TodoItemList list = {this.state.list} deleteItem = {this.handleItem.bind(this)}/>
/* 子组件 */
// props 接收父组件属性传入的值
render () {
return (
<ul>
{
this.props.list.map((item, index) => {
return <li onClick = {this.handleItem.bind(this, index)}>{item}</li>
})
}
</ul>
)
}
handleItem (index) {
// 调用父组件方法
this.props.deleteItem(index)
}context 提供了在组件中共享值得方法 不必通过props单向传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39// theme-context.js 创建context对象
import React from 'react'
const ThemeContext = React.createContext()
export default ThemeContext
// app.js
import ThemeContext from './theme-context'
import ThemeBar from './component/ThemeBar'
const themes = {
light: { className: 'btn btn-primary', bgColor: '#333333'},
dark: { className: 'btn btn-light', bgColor: '#eeeeee'}
}
// 使用 ThemeContext.Provider 包裹
<ThemeContext.Provider value={themes[this.state.theme]}>
<button className="btn btn-light"
onClick={() => { this.setState({theme: 'light'})}}
>浅色主题</button>
<button className="btn btn-secondary"
onClick={() => { this.setState({theme: 'dark'})}}
>深色主题</button>
<ThemeBar />
</ThemeContext.Provider>
// ThemeBar.js
import React from 'react'
import ThemeContext from '../theme-context';
const ThemeBar = () => {
return (
<ThemeContext.Consumer>
{ theme => {
return (
<div className="alert mt-5" style={{backgroundColor:theme.bgColor,color:theme.color}}>
<p>样式区域</p>
<button className={theme.className}>样式按钮</button>
</div>
)
}}
</ThemeContext.Consumer>
)
}
export default ThemeBar无状态组件(函数组件):只根据props渲染页面,没有内部的state,只有render函数 它就是一个函数 无状态组件性能高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import React from 'react'
import { Input, Button, List } from 'antd';
const TodoListUI = (props) => {
return (
<div className="todo">
<div className="input-box">
<Input placeholder="Basic usage" value={ props.inputValue } onChange={ props.InpuChange }/>
<Button type="primary" onClick={ props.buttonClick }>todo</Button>
</div>
<List size="small" bordered dataSource={ props.list }
renderItem={ (item,index) =>
<List.Item onClick={() => { props.itemDeleteHandle(index)}}>{ item }</List.Item>
}
/>
</div>
)
}
export default TodoListUIprops类型检查与默认值
Typechecking With PropTypes – React1
2
3
4
5
6
7
8
9
10
11import PropTypes from 'prop-types'
// 类型校验
MyComponent.propTypes = {
list: PropTypes.oneofType([Proptypes.array, Proptypes.string]),
deleteItem: PropTypes.func,
test: PropTypes.string.isRequired
}
// 默认值
MyComponent.defaultProps = {
test: 'test'
}props,state与render函数的关系
1
2// 1. 当组件的state或者props发生改变时, render函数就会重新执行
// 2. 当父组件的render函数被执行时, 它的子组件的render都将被重新执行
虚拟DOM
虚拟DOM就是一个JS对象, 用它来描述真实的DOM
优点:
1. 大大的提升了性能(减少了真实DOM的创建与对比,虚拟DOM创建的是js对象)
2. 它使得跨端应用得以实现. React Native
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// JSX --> createElement --> 虚拟DOM(JS对象) --> 真实DOM
// 虚拟DOM中的Diff算法 虚拟DOM同层比对 DOM key值相同比对
真实DOM
/*
1. state数据
2. JSX 模板
3. 数据 + 模板 结合, 生成真实的DOM 来显示
4. state 发生改变
5. 数据 + 模板 结合生成真实的DOM, 替换原始的DOM
缺陷: 第一次生成完整DOM 第二次生成完整DOM 用第二次DOM替换第一次DOM, 非常耗性能
*/
虚拟DOM
/*
1. state 数据
2. JSX 模板
3. 数据 + 模板生成虚拟DOM
['div', {id: 'test'}, ['span', {} ,'hello world']]
4. 用虚拟DOM的结构生成真实的DOM,来显示
<div id="test"><span>hello world</span></div>
5. state 发生变化
6. 数据 + 模板生成新的虚拟DOM
['div', {id: 'test'}, ['span', {} ,'hi react']]
7. 比较原始虚拟DOM和新的虚拟DOM区别, 找到区别是span中的内容变化
8. 直接操作DOM, 改变span中的内容
*/
ref的使用 获取DOM元素
1
2
3
4
5
6// ref 是箭头函数 this.input 指向 input DOM对象
// setState是异步函数 在获取DOM对象会有问题
<input ref = {(input) => {this.input = input}} />
this.setState({
inputValue: this.input.value
})setState是异步函数 第二个参数解决 异步导致DOM获取问题已经数值更新后的操作
1
2
3
4
5
6this.setState(prevState => ({
list: [...this.state.list,this.state.inputValue],
inputValue: ''
}), () => {
console.log(this.ul.querySelectorAll('div').length)
})setState为何需要异步以及过程
1
2
3// 1. 可能会一次执行多次setState操作
// 2. 没必要每次setState都重新渲染,考虑性能
// 3. 无法规定 限制用户如何使用setState
React 生命周期函数
生命周期函数是指在某一时刻组件会自动调用执行的函数
Mountting阶段componentWillMount
: 组件即将被挂载(render)到页面的时刻自动执行(第一次挂载执行)componentDidMount
: 组件在被挂载(render)到页面之后自动执行render之后(第一次挂载执行)
Updation阶段更新的三种情形: New props setState() fourceUpdate
shouldComponentUpdate
: 组件被更新之前自动执行,是否需要更新,必须返回布尔值componentWillUpdate
: 组件在被更新之前自动执行,但它在shouldComponentUpdate之后执行,如果shouldComponentUpdate返回true它才会执行,如果返回false,它不会执行componentDidUpdate
: 组件更新完成之后自动执行 (React更新DOM和refs会触发)componentWillReceiveProps
: 组件接收参数之前自动执行,需要满足两个条件,子组件要从父组件接收参数,且子组件以前已经存在于父组件中,才会执行,不能是第一次存在
UnmounttingcomponentWillUnmount
: 当这个组件即将被从页面中剔除(卸载)的时候,自动执行1
2
3
4
5
6
7
8
9
10// 当父组件的render函数被执行时, 它的子组件的render都将被重新执行
// shouldComponentUpdate 避免子组件无谓的render渲染
shouldComponentUpdate (nextProps, nextState) {
console.log(nextProps)
if (nextProps.list !== this.props.list) {
return true
} else {
return false
}
}
- PureComponent 定制了shouldComponentUpdate后的Component(浅比较)
react 性能优化
1
2
3
4
5
6
7
8
9// 1. this的绑定放在constructor里面
constructor (props) {
super(props)
this.handleButton = this.handleButton.bind(this)
}
// 2. setState异步函数将多次数据改变结合成一次执行
// 3. 虚拟DOM同层匹配 diff算法
// 4. 使用shouldComponentUpdate避免子组件无谓的render渲染
// 5. ajax最好放在componentDidMount中安装React Developer Tools chrome调试工具 调试react应用
React 动画
- 使用react-transition-group实现动画
npm i react-transition-group -S
使用CSSTransition实现单个元素过渡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import { CSSTransition } from 'react-transition-group'
// in 控制变量 timeout 时长 classNames 自定义class前缀 test-enter
// unmountOnExit 是否隐藏dom appear 是否首次也应用过渡
// onEntered 过渡完成事件
<CSSTransition
in={this.state.status}
timeout={1000}
classNames="test"
unmountOnExit
appear={true}
onEntered={(el)=>{ el.style.color = "blue"}}
>
<p>过渡{this.state.status?'显示':'隐藏'}</p>
</CSSTransition>
// transition.css
.test-enter, .test-appear{ opacity: 0; }
.test-enter-active,.test-appear-active{ opacity: 1;transition: opacity 1s ease-in}
.test-enter-done{ opacity: 1;}
.test-exit{ opacity: 1; }
.test-exit-active{ opacity: 0;transition: opacity 1s ease-in }
.test-exit-done{ opacity: 0; }使用TransitionGroup + CSSTransition 实现一组或多个元素过渡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import { CSSTransition, TransitionGroup } from 'react-transition-group'
<TransitionGroup>
{
this.state.list.map((item, index) => {
return (
<CSSTransition
timeout={1000}
classNames="test"
unmountOnExit
appear={true}
key={index}
onEntered={(el)=>{ el.style.color = "blue"}}
>
<li>{item}</li>
</CSSTransition>
)
})
}
</TransitionGroup>
React和Vue有何异同
- 本质区别 Vue是MVVM框架 React是前端组件化框架
- 模板的区别 Vue使用模板 React使用JSX
- 共同点 都支持组件化 都是以数据驱动视图
react 路由
- 安装
npm i react-router-dom
1
2
3
4
5
6
7
8
9// 引入
import { BrowserRouter, Route } from 'react-router-dom'
// 使用 exact path必须相等 Route必须用元素包裹
<BrowserRouter>
<div>
<Route path="/" exact component={ Home }></Route>
<Route path="/detail" exact component={ Detail }></Route>
</div>
</BrowserRouter>