React基本应用
一、mac的创建react项目
1、安装node版本至少11以上
node -v
2、全局安装creat-react-app
sudo npm install -g create-react-app
3、安装项目
create-react-app XXX (XXX为你想建立的项目名)
4、运行项目
cd XXX (刚才起项目名的文件夹内)
npm start //运行
5、注意
可能会出现报错node版本太低的错误,这时用cnpm淘宝镜像来启动它就可以了
cnpm start
二、JSX语法
1、 数据,vue是data,react是this.stat
this.state = {
name:'北京',
image:'https://img2.baidu.com/it/u=418352367,3766713103&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=290',
flag:true
}
2、 变量,插值,vue是用两个{{}},react用一个{}
render(){
const pElem = <p>{this.state.name}</p>
return pElem
}
3、 表达式
render(){
const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>
return exprElem
}
4、 子元素,vue里动态数据:,react直接绑定{}
render(){
const imgElem = <div>
<p>我的头像</p>
<img src = 'xxx.png' />
<img src = {this.state.image} />
</div>
return imgElem
}
5、 class,vue是写class,react是写className
render(){
const classElem = <p className="title">设置 css class</p>
return classElem
}
6、 style
render(){
const styleData = { fontSize: '30px', color: 'blue'}
const styleElem = <p style={styleData}>设置style</p>
return styleElem
//内联写法,注意是{{和}}
const styleElem = <p style={{fontSize: '30px', color: 'blue'}}></p>
return styleElem
//普通写法
const styleElem = <p style="font-size:30px;color:blue;"></p>
return styleElem
}
7、 原生html ,vue用v-html,react用dangerouslySetInnerHTM
render(){
const rawHtml = '<span>富文本内容<i>斜体</i><b>加粗</b></span>'
const rawHtmlData = {
__html: rawHtml
}
const rawHtmlElem = <div>
<p dangerouslySetInnerHTML={rawHtmlData}></p>
<p>{rawHtml}</p>
</div>
return rawHtmlElem
}
8、 加载组件
import List from './List'
render(){
const componentElem = <div>
<p>JSX 中加载一个组件</p>
<hr/>
<List/>
</div>
return componentElem
}
三、条件
1、if else
注意:vue是用v-if和v-else来进行判断,而react是直接写一个if else判断
import React from 'react'
class leftData extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return <div>left</div>
}
}
export default leftData;
2、三元表达式
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
showData: 'right'
}
}
render() {
const leftData = <button>left</button>
const rightData = <button>right</button>
return <div>
{this.state.showData == 'left' ? leftData : rightData }
</div>
}
}
export default BaseUse;
3、逻辑运算符&&||
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
showData: 'right'
}
}
render() {
const leftData = <button>left</button>
const rightData = <button>right</button>
return <div>
{this.state.showData == 'left' && leftData }
</div>
}
}
export default BaseUse;
四、渲染列表
1、map
注意:map函数是数组的一个重组,重组的规则是return里面的返回。但是原数组的数据正常保留 ,map相当于vue里的v-for
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
list: [
{
id: 'id_1',
title: '标题1'
},{
id: 'id_2',
title: '标题2'
},{
id: 'id_3',
title: '标题3'
}
]
}
}
render() {
return <ul>
{
this.state.list.map((item,index)=>{
return <li key={item.id}>
index: {index};
title: {item.title}
</li>
})
}
</ul>
}
}
export default BaseUse;
2、key
注意:这里的key 和vue的key类似,必填,但是不能是index和random
五、事件(React事件为何bind this)
1、bind this
- vue绑定事件是@加事件名称:@click,react绑定事件是on加上事件名称的首字母大写:onClick
- react的this默认是undefined
写法1:
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'李玉'
}
//修改方法的 this 指向
this.clickHandler1 = this.clickHandler1.bind(this)
}
render() {
return <p onClick={this.clickHandler1}>
{this.state.name}
</p>
}
clickHandler1(){
this.setState({
name:'简隋英'
})
}
}
export default BaseUse;
写法2:
注意:.bind(this)都是返回一个新的函数,让当前的函数绑定到当前的this上,但是写法1比写法2要好,因为写法1 只会执行一次bind,而写法2会单击一次执行一次bind,且两种的方法都是普通的方法定义的事件
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'李玉'
}
}
render() {
return <p onClick={this.clickHandler1.bind(this)}>
{this.state.name}
</p>
}
clickHandler1(){
this.setState({
name:'简隋英'
})
}
}
export default BaseUse;
方法3:
注意:此方法是用静态方法,this指向的是当前的实例
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'李玉'
}
}
render() {
return <p onClick={this.clickHandler1}>
{this.state.name}
</p>
}
clickHandler1 = () => {
this.setState({
name:'简隋英'
})
}
}
export default BaseUse;
2、关于event参数
- event是SyntheticEvent,模拟出DOM事件所有的能力
- event.nativeEvent是原生事件对象
- 所有的事件,都被挂载到document上
- 和DOM事件不一样,和Vue事件也不一样
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return <a href="https://wwww.baidu.com/" onClick={this.clickHandler3}>跳转入百度</a>
}
clickHandler3 = (event) => {
event.preventDefault() //阻止默认行为
event.stopPropagation() //阻止冒泡
console.log("targe",event.target)//指向当前元素
console.log("current target",event.currentTarget)//指向当前元素,假象
//event其实是react封装的,可以看__proto__.constructor 是 SyntheticEvent
console.log('event',event)//不是原生event,原生的是MouseEvent ,而React的是SyntheticEvent
//原生event如下,其__proto__.constructor是MouseEvent
console.log('nativeEvent',event.nativeEvent)//这个才是原生的event
//触发事件在绑定标签上,但是绑定事件在#document上
}
}
export default BaseUse;
3、传递自定义参数
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return <div onClick={this.clickHandler3.bind(this,1,2)}>跳转入百度</div>
}
clickHandler3(data1,data2,event){
console.log(data1,data2,event)
}
}
export default BaseUse;
六、表单
1、受控组件(相当于vue的v-model)
注意:通过input的value绑定state值,通过onchange事件将$event.target.value对state的绑定值进行数据绑定
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'简隋英'
}
}
render() {
return <div>
<p>{this.state.name}</p>
<label htmlFor="inputName">姓名:</label>
<input id="inputName" value={this.state.name} onChange={this.changeInput}/>
</div>
}
changeInput = (e)=>{
this.setState({
name:e.target.value
})
}
}
export default BaseUse;
2、input、textarea、select用value
注意:textarea和input的写法相同
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'简隋英'
}
}
render() {
return <div>
<p>{this.state.name}</p>
<label htmlFor="inputName">姓名:</label>
<textarea id="inputName" value={this.state.name} onChange={this.changeInput}></textarea>
</div>
}
changeInput = (e)=>{
this.setState({
name:e.target.value
})
}
}
export default BaseUse;
3、checkbox、radio用checked
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'简隋英',
flag:true
}
}
render() {
return <div>
<input type="checkbox" checked={this.state.flag} onChange={this.changeCheckbox}/>
<p>{this.state.flag.toString()}</p>
</div>
}
changeCheckbox = (e)=>{
this.setState({
flag:!this.state.flag
})
}
}
export default BaseUse;
七、组件的使用(父子组件通讯)
父组件:
import React from 'react'
import List from './component/List/index.js'
import Input from './component/Input/index.js'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
list:[
{
id: 'id_1',
title:'标题1'
},{
id: 'id_2',
title:'标题2'
},{
id:'id_3',
title:'标题3'
}
]
}
}
render() {
return <div>
<Input submitTitle={this.onSubmitTitle}/>
<List list={this.state.list}></List>
</div>
}
onSubmitTitle = (title) =>{
this.setState({
list:this.state.list.concat({
id:`id-${Date.now()}`,
title
})
})
}
}
export default BaseUse;
子组件Input:
import React from 'react'
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
title:''
}
}
render() {
return <div>
<input value={this.state.title} onChange={this.onTitleChange}/>
<button onClick={this.onSubmit}>提交</button>
</div>
}
onTitleChange =(e)=>{
this.setState({
title:e.target.value
})
}
onSubmit = () => {
const {submitTitle} = this.props
submitTitle(this.state.title)
this.setState({
title:''
})
}
}
export default Input;
子组件List:
import React from 'react'
class List extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
const {list} = this.props
return <ul>
{
list.map((item,index)=>{
return <li key={item.id}>
<span>{item.title}</span>
</li>
})
}
</ul>
}
}
export default List;
1、props传递数据
- <List list={this.state.list}></List>
- 接收:const {list} = this.props
2、props传递函数
- <Input submitTitle={this.onSubmitTitle}/>
- this.onSubmitTitle是个函数
- 接收:const {submitTitle} = this.props
- 使用:submitTitle(this.state.title)
3、props类型检查
注意:状态提升,要把共同的数据放在最高级别的组件内对他进行管理
八、setState
1、不可变值(函数式编程,纯函数)
不可变值(数组):
const list5Cpry = this.state.list5.slice()
list5Cpry.splice(2,0,'a')
this.setState({
list1: this.state.list1.concat(100),//追加
list2: [...this.state.list2,100],//同上追加,饿6写法
list3: this.state.list3.slice(0,3),//截取
list4: this.state.list4.filter(item => item > 100),//筛选
list5: list5Cpry//其他操作
})
注意:不能直接对 this.state.list 进行push pop splice 等,这样违反不可变值的规定
不可变值(对象):
this.setState({
obj1: Object.assign({},this.state.obj1,{a:100}),
obj2: {...this.state.obj2, a: 100}
})
注意:不能直接对 this.state.obj 进行修改,这样违反不可变值的规则,可以先拷贝一个出来进行修改,然后再通过setState把修改后的值赋值给原值
2、可能是异步更新
第一种异步情况:setState完没有立刻渲染页面
this.setState({
count: this.state.count+1
},()=>{
//已经渲染完成,可以拿到最新的值
//像是Vue中的 $nextTick()
console.log('count:',this.state.count);
})
//异步的拿不到最新值
console.log('count:',this.state.count);
第二种同步情况:在setTimeout中它是同步的
setTimeout(()=>{
this.setState({
count: this.state.count+1
})
//可以打印出最新的值
console.log('count:',this.state.count);
})
第三种同步情况:在自己定义的 DOM 事件,setState 是同步的
bodyClickHandler = () => {
this.setState({
count: this.state.count+1
})
console.log('count:',this.state.count);
}
componentDidMount() {
document.body.addEventListener('click',this.bodyClickHandler)
}
componentWillUnmouont(){
//销毁自定义事件
document.body.removeEventListener('click',this.bodyClickHandler)
}
3、可能会被合并
传入对象,会被合并(类似于Object.assign),执行结果只一次 +1
//this.state.count = 0
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
//执行结果:1
传入函数,不会被合并,执行结果是 +3
//this.state.count = 0
this.setState((preState,props) => {
return {
count: this.state.count + 1
}
})
this.setState((preState,props) => {
return {
count: this.state.count + 1
}
})
this.setState((preState,props) => {
return {
count: this.state.count + 1
}
})
//执行结果:3
九、组件生命周期
shouldComponentUpdata()更新生命周期
注意:父子组件生命周期和vue是一样的
React高级特性
一、函数组件
1、class组件
import React from 'react'
class List extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
const {list} = this.props
return <ul>
{
list.map((item,index)=>{
return <li key={item.id}>
<span>{item.title}</span>
</li>
})
}
</ul>
}
}
export default List;
2、函数组件
function List(props) {
const { list } = this.props
return <ul>
{list.map((item,index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}
</ul>
}
- 纯函数,输入props,输出JSX
- 没有实例,没有生命周期,没有state
- 不能扩展其他的方法
二、非受控组件
1、 ref
组件上绑定:
<input defaultValue={this.state.name} ref={this.nameInputRef}/>
constructor上注册:
this.nameInputRef = React.createRef()
绑定事件的访问:
alertName = () => {
const elem = this.nameInputRef.current;//通过 ref 获取 DOM 节点
alert(elem.value)
}
2、defaultValue、defaultChecked
<input defaultValue={this.state.name} ref={this.nameInputRef}/>
<input defaultChecked={this.state.flag} type="checkbox"/>
3、手动操作DOM原数
上传文件:
import React from 'react'
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {}
this.fileInputRef = React.createRef()
}
render() {
return <div>
<input type="file" ref={this.fileInputRef}/>
<button onClick={this.alertFileName}>alert name</button>
</div>
}
alertFileName = () => {
const elem = this.fileInputRef.current
alert(elem.files[0].name)
}
}
export default BaseUse;
非受控组件-使用场景:
- 必须手动操作DOM元素,setState实现不了
- 文件上传<input type='file'/>
- 某些富文本编辑器,需要传入DOM元素
三、Portals
- 组件默认会按照既定层次嵌套渲染
- 如何让组件渲染到父组件以外?
1、this.props.children
render() {
return <div className="modal">
{this.props.children}
</div>
}
this.props.children类似于Vue的slot
2、protals写法
普通写法:
return <div className="modal">
{this.props.childrem}
</div>
protals写法:
import ReactDom from 'react-dom'
//render()函数内
return ReactDom.createPortal(
<div className="modal">
{this.props.childrem}
</div>,
document.body
)
3、protals应用场景
- overfilow:hidden
- 父组件z-index值太小
- fixed需要放在body第一层级
四、context
- 公共信息(语言,主题)如何传递给每个组件?
- 用props太繁琐
- 用redux小题大做
context官网实例https://react.docschina.org/learn/passing-data-deeply-with-context
import React from 'react'
const ThemeContext = React.createContext('light')
function ThemedLink(props){
return <ThemeContext.Consumer>
{value => <p>link's theme is {value}</p>}
</ThemeContext.Consumer>
}
class ThemedButton extends React.Component {
render() {
const theme = this.context
return <div>
<p>button's theme is {theme}</p>
</div>
}
}
ThemedButton.contextType = ThemeContext
function Toolbar(props) {
return (
<div>
<ThemedButton/>
<ThemedLink/>
</div>
)
}
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
render() {
return <ThemeContext.Provider value={this.state.theme}>
<Toolbar/>
<ht/>
<button onClick={this.changeTheme}>change theme</button>
</ThemeContext.Provider>
}
changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light'
})
}
}
export default BaseUse;
五、异步组件
- import()
- React.lazy
- React.Suspense
import React from 'react'
const ContextDemo = React.lazy(() => import('./component/ContextDemo/index.js'))
class BaseUse extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return <div>
<p>引入一个动态组件</p>
<hr/>
<React.Suspense fallback={<div>Loading...</div>}>
<ContextDemo/>
</React.Suspense>
</div>
}
}
// 1、强制刷新,可看见Loading (看不见就限制一下 chrome的网速)
// 2、看 network 的js 加载
export default BaseUse;
六、性能优化
- 性能优化对于React更加重要
- setState中重点强调不可变值
1、SCU基本用法
shouldComponentUpdate(nextProps, nextState){
if(nextState.count !== this.state.count){
return true //可以渲染
}
return false // 不重复渲染
}
- React 默认:父组件有更新,子组件则无条件更新!!!
- 性能优化对于 React 更加重要
- SCU不是需要每次都用,根据需求去用它。
错误写法:
let ran = parseInt(Math.random()*10000)
this.state.list.push({
id:ran,
name:ran
})
this.setState({
list:this.state.list
})
正确写法:
let ran = parseInt(Math.random()*10000)
this.setState({
list:this.state.list.concat({
id:ran,
name:ran
})
})
scu深度监听:
shouldComponentUpdate(nextProps, nextState){
//_isEqual深度监听
if(_isEqual(nextProps.list, this.props.list)){
return false
}
return true
}
- SCU里对数据进行深度监听是错误写法会出现不更新的bug状态,bug出现原因为 那样的写法会导致对比会完全相同,所以不会返回true。
2、SCU使用总结
- SCU默认返回true,即React默认重新渲染所有子组件
- 必须配合“不可变值”一起使用
- 可先不用SCU,有性能问题时在考虑使用
3、PureComponent和memo
- PureComponent,SCU中实现了浅比较
- memo,函数组件中的PureComponent
- 浅比较已使用大部分情况(尽量不要做深度比较)
PureComponent
class BaseUse extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
}
}
shouldComponentUpdate(){/*浅比较*/}
}
memo
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与将
prevProps 传入 render方法的返回结果一致则返回 true
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual)
4、immutable.js
- 彻底拥抱“不可变值”
- 最快的方式用不可变值的方式就是深拷贝,但是深拷贝有缺点,缺点会导致性能非常烂
- immutable基于共享数据(不是深拷贝),速度好
- 有一定的学习和迁移成本,按需使用
const map1 = Immutable.Map({a:1, b:2 c:3})
const map2 = map1.set('b', 50)
map1.get('b')//2
map2.get('b')//50
七、组件的公共逻辑抽离
- mixin,已被React弃用了
- 高阶组件HOC
- Render Props
1、高阶组件HOC
一张图说明
高阶组件 基本用法
//高阶组件不是一种功能,而是一种模式
const HOCFactory = (Component) => {
class HOC extends React.Component {
//在此定义多个组件公共逻辑
render(){
return <Component {...this.props} /> //返回拼装的结果
}
}
return HOC
}
const EnHancedComponent1 = HOCFactory(WrappedComponent1)
const EnHancedComponent2 = HOCFactory(WrappedComponent2)
- 接收一个组件,返回一个组件(返回的组件用了我们传入的组件)
- 它类似一个组装厂
例子:
import React, { Component } from 'react'
//高阶组件
const withMouse = (Component) => {
class withMouseComponent extends React.Component{
constructor(props) {
super(props)
this.state = {x:1 ,y:0 }
}
handleMouseMove = (event) =>{
this.setState({
x: event.clientX,
y: event.clientY
})
}
render(){
return (
<div style={{height:'500px'}} onMouseMove={this.handleMouseMove}>
{/*1、透传所有 props ,2、增加 mouse 属性*/}
<Component {...this.props} mouse={this.state}></Component>
</div>
)
}
}
return withMouseComponent
}
const APP = (props) => {
const {x, y} = props.mouse //接收 mouse 属性
return (
<div style={{ height: '500px' }}>
<h1>The mouse position is ({x}, {y})</h1>
</div>
)
}
export default withMouse(APP)//返回高阶函数
- HOC通过mouse的属性传递值给子组件
<Component {...this.props} mouse={this.state}></Component>
- 子组件接收传递的值直接用props接收
const {x, y} = props.mouse //接收 mouse 属性
- 透传所有props (避免上一个父组件有传值,且高阶组件内包含的子组件有对传值的数据处理,所以需要将所有的props传入子组件内)
{...this.props}
<Component {...this.props} mouse={this.state}></Component>
- vue的透传所有props通过:$props 和v-bind
2、Render Props
基本用法:
const App () => (
<Factory render={
/* render 是一个函数组件*/
(props))=> <p>{props.a} {props.b} ...</p>
}/>
)
//Render Props 的核心思想
//通过一个函数将 class 组件的 state 做为 props 传递给函数组件
class Factory extends React.Component{
constructor(){
this.state = {
/*state 即多个组件的公共逻辑的数据*/
}
}
/*修改 state*/
render(){
return <div>{this.props.render(this.state)}</div>
}
}
例子:
import React, { Component } from 'react'
class Mouse extends React.Component{
constructor(props) {
super(props)
this.state = {x:1 ,y:0 }
}
handleMouseMove = (event) =>{
this.setState({
x: event.clientX,
y: event.clientY
})
}
render(){
return (
<div style={{height:'500px'}} onMouseMove={this.handleMouseMove}>
{/*将当前 state 作为 props ,传递给 render (render是一个函数组件)*/}
{this.props.render(this.state)}
</div>
)
}
}
const APP = () => (
<div style={{ height: '500px' }}>
<Mouse render={
/* render 函数 是个函数组件*/
({x, y}) => (<h1>The mouse position is ({x} , {y})</h1>)
}/>
</div>
)
export default APP
3、 高阶组件HOC和Render Props的区别
- HOC:模式简单,但是会增加组件层级(可能会出现透传少值的情况)
- Render Props:代码简洁,学习成本较高
Redux
- 和Vuex作用相同,但比Vuex学习成本高
- 不可变值,纯函数
- 面试常考
一、安装使用
- 安装
npm install --save redux
- 使用react时安装
npm install --save react-redux
npm install --save-dev redux-devtools
二、三大原则
1、单一数据源
整个应用的state被存储在一棵object tree 中,并且这个 object tree 只存在于唯一一个store中。
2、使用纯函数来修改执行
如何改变 state tree 需要编写reducers 。它接收先前的 state 和 action ,并且返回新的state。如果这个 action 超出 reducers 的权限, 要返回老的state 。(注意:里面不能用异步,如定时器网络请求)
3、state是只读的
唯一改变 state 的方法就是触发 action , action是一个用于描述已发生事件的普通对象。执行上面纯函数
三、核心
1、Action
function gaiTel(){
return {
type:'GAITL',//要办理业务类型
data:1234567//新数据
}
}
2、Reducer
function todoAPP(state = initialState,action) {
switch (action.type){
case SET_VISIBILITY_FILTER:
return Object.assign({},state,{
visibiliFill:action.filter
})
default:
return state
}
}
3、Store
//调用业务流程,执行
store.dispatch(Action)
//创建出来唯一的仓库
let store = createStore(Reducer)
四、语法
1、创建仓库
import { createStore } from 'redux'
const store = createStore(reducer,initState,middleware)
- 返回值是一个store对象,这个对象上有3个常用的方法
- dispatch:store对象提供给用户,告诉管理员有哪些操作
- getState:获取store中存储的数据
- subscribe:是一个监听方法,当store中的数据发生改变时就会触发
2、添加管理员 reducer
function admi(){
switch(action.type){
case A:
return state;
case B:
return state;
default:
return state
}
}
3、整体代码
首页引入:
import './component/redux1.js'
redux1.js:
//引入createStore,创建store方法
import {createStore} from 'redux'
const init = {
color:'red',
height:'80cm'
}
const store = createStore(admin,init)
//管理员
function admin(state,action){
switch(action.type){
case 'ChangeColor':
state.color = action.data
return {...state}
case 'ChangeHeight':
state.height = action.data
return {...state}
default:
return state
}
}
//getState()方法:获取storre中存储的方法
console.log(store.getState())
//dispatch()方法,让用户提交需求
store.dispatch({
type:'ChangeColor',
data:'黄色'
})
//subscribe()方法是监听store中的数据变化,变化后会自动触发函数
store.subscribe(function(){
console.log(store.getState())
})
五、react-redux关联
1、<Provider>
2、connect
react-redux关联https://blog.csdn.net/weixin_49866029/article/details/122900437?spm=1001.2014.3001.5506
3、mapStateToProps、mapDispatchToProps
react-redux关联https://www.jianshu.com/p/77bd43527a0e
六、异步Action
1、同步Action
//同步Action
export const addTodo = text =>{
//返回action对象
return {
type:'ChangeColor',
id:Idoe++,
text
}
}
//异步Action
export const addTodoAsync = text =>{
return (dispatch) => {
//ajax 异步获取数据
fetch(url).then(res=>{
//执行异步action
dispatch(addTodo(res.text))
})
}
}
- 异步的情况下,要return一个函数
- 函数里面会接收一个dispatch
- 异步请求完成后再去调用同步action函数
- 异步action就是在异步里面再去做了一次同步的action
- 直接像上面是那么写不行
2、引入thunk完成异步action的完整
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from '../rootReducer'
//创建store时,作为中间件引入redux-thunk
const store = createStore(rootReducer,applyMiddleware(thunk))
七、redux中间件
- 上面的redux-thunk就是中间件
- 图一是一般的操作redux的图
- action是一个对象,我们的中间件只能在dispatch函数处理action里面去加入中间件
八、redux单项数据流
react-router
- hash模式(默认),如http://abc.com/#/user/10
- H5 history模式,如http://abc.com/user/10
注意:后者需要server端支持,因此无特殊要求可以选择前者 toC用后者,toB用前者
一、react-router路由模式
二、react-router动态路由的配置
三、跳转路由
四、懒加载
React原理
一、函数式编程
- 一种编程范式,概念比较多
- 纯函数
- 不可变值
二、vdom和diff
注意:第一个提出虚拟dom和diff是React
vdom
- h函数
- vnode数据结构
- patch函数
diff
- 只比较同一层级,不跨级比较
- tag不相同,则直接删除重建,不再深度比较
- tag和key,两者都相同,则认为是同一节点,不再深度比较
补充
- Vue2.0、vue3.0、React三者实现vdom细节口不同
- 核心概念和实现思路,都一样
三、JSX本质
- JSX等同于Vue模板
- Vue模板不是html
- JSX也不是JS
JSX实例
//JSX写法
const imgRlem = <div>
<p>some text</p>
<img src={imgUrl}/>
</div>
//编译后写法
var imgRlem = React.createElement(
"div",
null,
React.createElement(
"p",
null,
"some text"
),
React.createElement("img", { src: imgUrl })
);
h函数编译本质:
React.createElement('tag',null,child1,child2,child3)
- 第一个参数为标签
- 第二个参数为属性
- 第三个参数为子元素,且第三个参数可以为数组也可以不是数组
返回vnode
//JSX基本用法
const styleData = {
fontSize:'30px',
color:'blue'
}
const styleElem = <p style={styleData}>设置style</p>
//h函数转换
var styleData = {
fontSize: '30px',
color: 'blue'
};
var styleElem = React.createElement(
'p',
{ style: styleData },
'\u8BBE\u7F6Estyle'
);
//JSX
const app = <div>
<Input submitTitle={onSubmitTitle}/>
<List list={list}/>
</div>
//h函数
var app = React.createElement(
"div",
null,
React.createElement(Input, { submitTitle: onSubmitTitle }),
React.createElement(List, { list: list })
);
注意:React.createElement第一个参数不一定是元素的名称,还有可能是组件的变量名。
//JSX
const listElem = <ul>
{this.state.list.map((item,index)=>{
return <li key={item.id}>index{index},title{item.title}</li>
})}
</ul>
//h函数
var listElem = React.createElement(
"ul",
null,
undefined.state.list.map(function (item, index) {
return React.createElement(
"li",
{ key: item.id },
"index",
index,
",title",
item.title
);
})
);
总结
- React.createElement即h函数,返回vnode
- 第一个参数可能是组件,也可能是html tag
- 组件名,首字母必须大写(React规定)
四、合成事件
- 所有事件挂载到document上
- event不是原生的,是SyntheicEvent合成事件对象
- 和Vue事件不同,和Dom事件不同
为何要合成事件机制?
- 更好的兼容性和跨平台
- 挂载到docoment,较少内存消耗,避免频繁解绑
- 方便事件的同一管理(如事务机制)
五、setState、 batchUpdata
- 有时是异步(普通使用),有时是同步的(setTimeout、DOM事件)
- 有时合并(对象形式),有时不合并(函数形式)
- 后者比较好理解(像Object.assign),主要是前者
setState主流程
注意:普通异步就会处于batch updata之中,同步不会处于batch updata之中
setState是异步还是同步
- setState无所谓是异步还是同步
- 看是否能够命中batchUpdata机制
- 判断isBatchingUpdates
能够命中batchUpdata机制
- 生命周期(和它调用的函数)
- React中注册的事件(和它调用的函数)
- React可以“管理”的入口
不能命中batchUpdata机制
- setTimeout、setInterval等(和它调用的函数)
- 自定义的DOM事件(和它调用的函数)
- React “管不到”的入口
transaction(事务)机制
实例:
六、总结(组件渲染和更新的过程)
更新的两个阶段
- 上述的patch被拆分成两个阶段
- reconciliation阶段-执行diff算法,纯JS阶段
- commit阶段-将diff结果渲染DOM
如果不分可能会有性能问题
- JS是单线程,且和DOM渲染共用一个线程
- 当组件足够复杂,组件更新时计算和渲染都压力过大
- 同时再有DOM操作需求(动画、拖拽等)、将会卡顿
解决方案fiber
- 将reconciliation阶段进行任务拆分(commit无法拆分)
- DOM需要渲染时暂停,空闲时恢复
- 如何知道DOM需要渲染,window。requestIdleCallback