React基础学习笔记

JSX的语法规则

1.定义虚拟DOM时,不要写引号;
2.标签中混入JS表达式时要用{};
3.样式的类名指定不要用class,要使用className;
4.内联样式,要用style={{key:value}}的形式去写;
5.只有一个跟标签;
6.标签必须闭合;
7.标签首字母
	(1).若小写字母开头,则将标签转为html中同名元素,若html中没有对应的同名元素,则报错;
	(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义则报错;

1.类中的构造器(constructor)不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写;
2.如果A类继承了B类且A类中写了构造器,那么A类构造器中super()必须要调用;
3.类中定义的方法,都是放在了类的原型对象上供实例去使用;

组件

函数式组件:
    function MyComponent(){
     return <h1>我是简单组件</h1>
    }
    ReactDOM.render(<MyComponent/>,document.getElementById('text'))
类式组件
	class MyComponent extends React.Component{
        render(){
			return{
				<h1>我是复杂组件</h1>
			}
        }
	}
	ReactDOM.render(<MyComponent/>,document.getElementById('text'))

组件实例的三大核心属性(state)

1.state 是组件的重要属性,值为对象(可以包含多个 key-value的组合)
2.组件被称为'状态机',通过更新组件的State来更新对应的页面显示(更新渲染组件)
注意:
	组件中的render方法中的this为组件实例对象;
	组件自定义的方法中this为undefined,如何解决
		(1).强制指定this,通过函数对象的bind() this.weater=this.weater.bind(this);
		(2).通过赋值语句+箭头函数实现; weater=()=>{};
	状态数据,不能直接修改或更新;需要修改时通过:this.setState({key:value})

组件实例的三大核心属性(props)

1.每个组件对象都有props属性;
2.组件标签的所有属性都保存在props中
3.通过标签属性从组件外部向组件内部传递变化的数据;
4.注意:
	组件内部不要修改props数据
操作:
	(1).读取某个值:this.props.name;
	(2).对props中的参数进行限制
		引入prop-type.js组件库
		参数限制:
            Person.propTypes={
                name:PropTypes.string.isRequired,
                age:PropTypes.number,
                speak:PropTypes.func,
            }
		设置默认值:
			Person.defaultProps={
				name:'常鑫',
				age:18,
			}
		简写(组件内部):
			限制(设置默认同理)
                static propTypes={
                    name:PropTypes.string.isRequired,
                    age:PropTypes.number,
                    speak:PropTypes.func,
                }

组件实例的三大核心属性(refs)

1.字符串ref
	(1).影响性能
		Tips:<input type="text" ref="input1">
		used: const { input1 } = this.refs;
2.回调ref 
	(1).内联回调ref 更新DOM时会执行两次,并没有太大影响;
		Tips:<input type="text" ref={cur => this.input1 = curr }>
		used:const { input1 } = this;
	(2).类的函数绑定ref 首次DOM渲染,执行一次
		Tips:
			<input type="text" ref={this.saveInput}>
			saveInput = (curr) => {
                this.input1 = curr;
            }
        used:const { input1 } = this;
3.createRef 
	(1).存放ref节点,专人专用
        Tips: myRef=React.createRef()
        used: this.myRef.current;

React中的事件绑定

1.通过onXXX属性指定事件处理函数,注意大小写
	a.React使用自定义(合成事件),而不是使用原生DOM事件     -----为了更好的兼容性
	b.React中的事件绑定通过事件委托方式处理的(委托给组件最外层的元素)     -----为了高效
2.通过event.target得到发生事件的DOM元素对象;     -----不要过度的使用ref

React中收集表单数据

1.非受控组件 现用现取(触发事件时,才去获取数据)
2.受控组件 随着输入将输入的内容维护到状态中   (vue的双向绑定) 减少ref的使用;

高阶函数&&函数的柯里化

高阶函数:如果一个函数符合下面2个规则中的任何一个,那么该函数为高阶函数.
	1.若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数;
	2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数;
	常见的高阶函数:Promise setTimeout arr.map()等等;
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受函数最后统一处理的函数的编码形式.

tips:
	<input type="text" onChange={this.saveFormDate('username')} name="username"/>
	saveFormDate=(dataType)=>{
		return (event)=>{
			this.setState({
				[dataType]:event.target.value
			})
		}
	}
	
tips:
    function sum(a){
        return(b)=>{
            return (c)=>{
                return a+b+c
            }
        }
    }
    sum(1)(2)(3)

React的生命周期(旧)

生命周期钩子函数:

	1.初始化阶段  ReactDOM.render(<Component/>,document.getElementById('text'))触发 初次渲染
	
        (1).组件将要挂载:
            componentWillMount(){}
        (2).初始化渲染,状态更新之后(重复调用):
            render(){}
        (3).组件挂载完毕:		 *** 类似于Vue的created 小程序的onload 一般用于:开启定时器,发送网络请求,订阅消息 
            componentDidMount(){}   
        
	2.更新阶段 由this.setState()或者父组件render()触发
	
        (4).控制组件是否更新 return true||false;	tips:setState()触发
            shouldComponentUpdate(){}
        (5).组件将要更新;	tips:forceUpdate()触发
            componentWillUpdate(){} 
        (2).初始化渲染,状态更新之后(重复调用):		*** 渲染
            render(){}   
        (6).组件更新完毕
            componentDidUpdate(){}
        (7).组件将要接受props;	tips:首次传递不触发
        	componentWillReceiveProps(props){}
    3.卸载 ReactDom.unmountComponentAtNode(document.getElementById('text'))触发
    
    	(8).组件将要卸载:			*** 一般做一些收尾的事:关闭定时器,取消订阅消息
            componentWillUnmount(){}   
生命周期钩子函数执行流程
	1.加载:(1),(2),(3),
	2.this.setState():(4),(5),(2),(6) 
	3.父组件render():(7),(4),(5),(2),(6)
	

React的生命周期(新)

废弃或者即将废弃
componentWillMount(){}  => UNSAFE_componentWillMount(){},
componentWillUpdate(){}  => UNSAFE_componentWillUpdate(){},
componentWillReceiveProps(){}  => UNSAFE_componentWillReceiveProps(){},
新增:
getDerivedStateFormProps(){}
getSnapshotBeforeUpdate(){}

1.初始化阶段:由ReactDOM.render()触发
	(1).constructor()
	(2).getDerivedStateFormProps
	(3).render()
	(4).componentDidMount()
2.更新阶段:由组件内部的this.setState()或父组件更新render触发
	(1).getDerivedStateFromProps
	(2).shouldComponentUpdate()
	(3).render()
	(4).getSnapshotBeforeUpdate
	(5).componentDidMount()
3.卸载组件:由ReactDOM.unmountConponentAtNode()触发
        componentWillUnmount();
	

vue或者React中的key有什么作用

1.简单来说就是虚拟DOM的标识,
	让数据状态发生改变时,React会根据[新的数据]生成[新的虚拟DOM],随后React进行[新虚拟DOM]与[旧的虚拟DOM]进行diff比较
规则如下:
	a.旧的虚拟DOM中找到了新的虚拟DOM相同的key
		(1).若虚拟DOM内容没有变化,直接使用之前的真实DOM
		(2).若虚拟DOM中内容发生了变化,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
	b.旧的虚拟DOM中未找到与新的虚拟DOM相同的Key
		根据数据创建新的真实DOM,随后渲染到页面上
用index作为key可能引发的问题:
	1.对数据进行:逆序添加,逆序删除等破坏顺序操作:会产生没有必要的真实DOM渲染==>界面效果没问题,但效率低
	2.如果结构中还包含输入类的DOM:会产生错误的DOM更新  ==>界面渲染错误
	3.注意:如果不存在对数据的逆序添加,逆序删除等操作,仅用渲染列表用于展示,使用index作为key是没有问题的
开发中如何选择key?
	1.最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号,学号等
	2.如果确定只是简单的展示数据,用index也是可以的

React类式组件模板

class Person extends React.Component{
	1.限制props中的数据类型
	static propTypes={
		name1:PropTypes.string.isRequired,
		age1:PropTypes.number,
		sex1:PropTypes.string,
	}
	2.设置props的数据默认值
	static defaultProps={
		name1:'常鑫',
		age1:18,
		sex1:'男'
	}
	3.初始化数据
	state={
		name:'常鑫',
		age:18,
	}
	4.渲染
	render(){
		const { name, age }=this.state;
		const { name1, age1, sex1 } =this.props;
		return (
			<ul className="Demo" onClick={this.speak} style={{fontSize:'22px',color:'red'}}>
				<li>{name}</li>
				<li>{age}</li>
				<li>-----</li>
				<li>{name1}</li>
				<li>{age1}</li>
				<li>{sex1}</li>
			</ul>
		)
	}
	5.事件
	speak=()=>{
		const { name,age }=this.state;
		this.setState({
			age:age+1-
		})
	}
}

渲染组件const p={name1:'jack',age1:20,}
ReactDOM.render(<Person {...p}/>, document.getElementById('text'))

React脚手架安装

1.全局安装: npm install -g create-react-app
2.创建项目: create-react-app 项目名称
3.切换项目目录:cd 项目名称
4.启动项目:npm start

React脚手架

public---静态资源文件夹
	favicon.icon ----- 网站页签图标
	index.html ----- 主页面
	logo.192.png ----- logo图
	logo.512.png ----- logo图
	manifest.json ----- 应用加壳的配置文件
	robots.txt ----- 爬虫协议文件
src---源码文件夹
	App.css ----- App组件样式
	App.js ----- App组件
	app.test.js ----- 用于给App测试
	index.css ----- 公共样式
	index.js ----- 入口文件
	logo.svg ----- logo图
	reportWebVitals.js ----- 页面性能分析文件(需要web-vitals库的支持)
	setupTest.js ----- 组件单元测试的文件(需要jest-dom库的支持)

React脚手架初始化配置

目录结构:
    -public
        -index.html 主页面
        -favicon.icon
    -src
        -App.js		App外壳组件
        -index.js   入口文件
        -Component  组件文件夹
            -各类组件文件夹
                --index.jsx
                --index.css
        -pages 路由组件
        	-各类路由组件文件夹
        		--index.jsx
        		--index.css

样式的模块化

xxx.module.css
引入: import xxx form '模块换样式路径'
used: <div className={xxx.样式名}></div>

React常用开发快捷键

rcc =>快速构建类式组件模板
rfc =>快速构建函数式组件模板
cdm => componentDidMount
cwun =>componentWillUnmount

脚手架配置代理

方法一:
	在package.json中追加如下配置 "proxy":"http://localhost:5000"
	tips:
		(1). 优点:配置简单,前端请求资源时可以不加任何前缀。
        (2). 缺点:不能配置多个代理。
        (3). 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
方法二:
	1.第一步:创建代理配置文件 在src下创建配置文件:src/setupProxy.js
	2.编写setupProxy.js配置具体代理规则:
const proxy = require('http-proxy-middleware')

module.exports = function(app) {
  app.use(
    proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
      target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      /*
      	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
      	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
      	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    proxy('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}
说明:
    1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
    2. 缺点:配置繁琐,前端请求资源时必须加前缀。
消息订阅发布
借助工具库:PubSubjs
下载:npm install pubsub-js --save
used:
	import PubSub from 'pubsub-js'
	PubSub.subscribe('delete',function(data){}) //订阅消息
	PubSub.publish('delete',data) //发布消息
Fetch的使用
search= async ()=>{
    // 发送网络请求Fetch(未优化)
        //#region 
        // fetch(`/search/users?q=${data}`).then(
        //     res=>{
        //         // 将连接成功之后的数据通过 原型链上的json() 传递出去; 为promise()类型;
        //         return res.json()
        //     },
        //     err=>{
        //         console.log(err)
        //         // 中断promise链
        //         return new Promise(()=>{})
        //     }
        // ).then(
        //     res=>{
        //         console.log(res)
        //     },
        //     err=>{
        //         console.log(err)
        //     }
        // )
        //#endregion
    // 发送网络请求Fetch(优化后)
        try {
            const res = await fetch(`/search/users?q=${data}`)
            const reault = await res.json()
            PubSub.publish('updata', { users: reault.items, isLoading: false })
        } catch (err) {
            PubSub.publish('updata', { err: '请求超时,请重试!!!', isLoading: false })
        }
}
tips:
	1.Fetch原生函数,不借助xhr发送ajax请求
	2.老版浏览器可能不支持使用Fetch
路由的基本使用
npm i react-router-dom 安装 react-router-dom
1.明确好界面的导航区、内容区
2.导航区的a标签改为Link标签 to='地址栏中展示的路径'
	<Link to="/xxxxx">Demo</Link>   类似于vue中的<router-link to="/xxx"></router-link>  不含选中样式
3.展示区写Route标签进行路径匹配   path='地址栏中注册路径' component={路由组件}
	<Route path="/XXX" component={Demo}>   类似于vue中的<router-view></router-view>
4.<App>的最外侧包裹一个 <BrowserRouter> 或者 <HashRouter>

一般组件与路由组件的区别

1.写法不同
	一般组件:<Demo>
	路由组件:<Route path="/demo" component={Demo}>
2.存放位置不同
	一般组件:components
	路由组件:pages
3.接收到的props不同:
	一般组件:写组件标签时,传递了什么就能收到什么
	路由组件:接收到三个固定的属性
		(1).history:
            go: ƒ go(n)
            goBack: ƒ goBack()
            goForward: ƒ goForward()
            push: ƒ push(path, state)
            replace: ƒ replace(path, state)
        (2).location:
            pathname: "/about"
            search: ""
            state: undefined
        (3).match:
            params: {}
            path: "/about"
            url: "/about"

NavLink与navLink 封装

<NavLink activeClassName='active' to="/xxxxx">Demo</NavLink>   含选中样式
		NavLink的封装:将NavLink组件进行二次封装,减少代码量;
			 封装:<NavLink activeClassName="actives" className="list-group-item" {...this.props} />
			 使用:<MyNavLink to="/about">About</MyNavLink>

switch的使用

path和component是一一对应的;
switch可以提高路由匹配效率(单一匹配)
路由样式丢失
当路由为多级路由时;路由组件加载完毕再次刷新时会出现样式错乱
解决方法:
	(1).public/index中引入样式文件时不写 ./ 写 /
	(2).public/index中引入样式文件时不写 ./ 写 %PUBLIC_URL%
	(3).修改路由模式:将index.js中的BrowserRouter改为HashRouter 

路由的模糊匹配与精准匹配

<Router exact={true} path="/about" component="about">  exact={true}开启精准匹配  默认为模糊匹配
模糊匹配规则:"输入路径"的路径必须包含"匹配的路径"只多不少,且顺序一致
	<NavLink to="/about/a/c"> <Route path="/about" component={About}> ==>成功匹配
	<NavLink to="a/about"> <Route path="/about" component={About}> ==>失败匹配
严格匹配: 
	<NavLink>中的to=""的路径与<Router>中的path=""完全一致才匹配
tips:严格匹配不能随便开启,需要开启时在开启,有的时候开启会导致无法匹配到二级路由

redireact的使用

一般写在路由注册的最下方,当所有路由都没有匹配时,跳转到redireacr指定的路由
<Switch>
	<Route path="/about" component={About}>About</Route>
	<Route path="/home" component={Home}>Home</Route>
	<Redireact to="/about">
</Switch>

嵌套路由的使用

1.注册的子路由要写上父路由的path值
	路由链接:<NavLink to="home/news"> 
	路由注册:<Route path="home/news" component={News}>
2.路由的匹配按照注册路由的顺序进行

路由传参

1.params参数:需要在路由注册时提前声明接受
	注册路由时:<Route path="/home/message/messageinfo/:id/:title" component={MessageInfo}/>
	跳转链接时:<Link to={`/home/message/messageinfo/${item.id}/${item.title}`}>{item.title}/</Link>
	参数接受时: const { id, title } = this.props.match.params;
2.search参数:无需提前声明接受
	注册路由时:<Route path="/home/message/messageinfo" component={MessageInfo}/>
	路由链接时:<Link to={`/home/message/messageinfo/?id=${item.id}&title=${item.title}`}>{item.title}</Link>
	参数接受时:const search = this.props.location.search;
        	  const { id, title } = qs.parse(search.slice(1))  //获取到的search时urlencoded编码,需要qs转换
3.state参数:无需提前声明接受,不会再地址栏中显示参数
	注册路由时:<Route path="/home/message/messageinfo" component={MessageInfo}/>
	路由链接时:<Link to={{pathname:'/home/message/messageinfo',state:{id:item.id,title:item.title}}}>{item.title}</Link>
	接受参数时:const { id, title } = this.props.location.state;
	

qs常用api

stringify:
	let obj={name:'常鑫',age='12'}
	console.log(qs.stringIfy(obj)) // name=常鑫&&age=12
parse:
	let str='name=常鑫&&age=12'
	console.log(qs.parse(str)) //{name:'常鑫',age='12'}

路由跳转的两种模式

push(默认): 压栈处理,会保留浏览记录
replace: 替换处理,会替换当前浏览记录
	<Link replace to={{pathname:'/home/message/messageinfo',state:{id:item.id,title:item.title}}}> //replace 开启replace模式

导航式路由跳转和编程时路由跳转

导航式路由跳转:需要借助<Link>,<NavLink>等组件进行路由组件切换 ,类似于<a>标签;
编程时路由跳转:
	路由组件api:
        this.props.history.push('path',state)  //push跳转
        this.props.history.replace('path',state)  //replace跳转
        this.props.history.goBack()  //路由回退
        this.props.history.goForward()  //路由前进
        this.props.history.go(num)  //路由前进  1前进1步  -1后退一步
        
    	withRouter(components) //接受一个组件,给一般组件添加路由组件特有的三个属性; hitory ,loaction ,match  返回值是一个新组件

BrowserRouter与HashRouter的区别

1.底层原理不同:
	BrowserRouter使用的式H5的history API 不兼容ie9及一下把呢吧
	HashRouter 使用的是URL的哈希值
2.url的表现形式不一样
	BrowserRouter的路径中没有#
	HashRouter的路径中含有#
3.刷新后对路由state参数的影响
	BrowserRouter没有任何影响
	HashRouter刷新后会导致路由的state参数丢失
4.备注:HashRouter可以用于解决一些路径错误相关的问题

antdUI库的使用 3.0x

//安装ant-design UI库
npm i antd

//按需引入所需要的antd组件
import { Button } from 'antd'

1.安装antd组件“按需引入”插件
	npm i react-app-rewired customize-cra
	
2.修改package.json
    "scripts": {
        -   "start": "react-scripts start",
        +   "start": "react-app-rewired start",
        -   "build": "react-scripts build",
        +   "build": "react-app-rewired build",
        -   "test": "react-scripts test",
        +   "test": "react-app-rewired test",
    }
    
3.安装babel-plugin-import 插件
	npm i babel-plugin-import
	
4.在根目录下创建config-overrides.js
	const { override, fixBabelImports } = require('customize-cra');
    module.exports = override(
        fixBabelImports('import', {
            libraryName: 'antd',
            libraryDirectory: 'es',
            style: 'css',
        }),
    );

antd自定义主题 3.0x

1.安装less-loade
	npm i less less-loader
2.修改config-overrides.js
	- const { override, fixBabelImports } = require('customize-cra');
    + const { override, fixBabelImports, addLessLoader } = require('customize-cra');

    module.exports = override(
      fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
    -   style: 'css',
    +   style: true,
      }),
    + addLessLoader({
    +   javascriptEnabled: true,
    +   modifyVars: { '@primary-color': '#1DA57A' },
    + }),
    );

ant的使用及自定义主题4.0

1.安装ant-design UI库
	npm i antd
2.修改 src/App.js引入组件,引入样式
    import { Button } from 'antd'
    import './App.less';
3.新建App.less于app.jsx同级,并引入antd.less
	/* App.less */
	@import '~antd/dist/antd.less'; 
4.安装@craco/craco和craco-less插件
    npm i @craco/craco
    npm i craco-less
 5.修改package.json文件
 	/* package.json */
 	"scripts": {
        -   "start": "react-scripts start",
        -   "build": "react-scripts build",
        -   "test": "react-scripts test",
        +   "start": "craco start",
        +   "build": "craco build",
        +   "test": "craco test",
    }
6.新建文件craco.config.js
	/* craco.config.js */
	const CracoLessPlugin = require('craco-less');
    module.exports = {
      plugins: [
        {
          plugin: CracoLessPlugin,
          options: {
            lessLoaderOptions: {
              lessOptions: {
                modifyVars: { '@primary-color': '#1DA57A' }, //变量所代表的颜色
                javascriptEnabled: true,
              },
            },
          },
        },
      ],
    };
详细地址:https://ant.design/docs/react/use-with-create-react-app-cn

redux的使用

redux:用于状态管理,类似于与vuex,存放一些公共的共享数据
redux的三个核心属性:
	action
		动作对象
		包含两个属性{type:'标识属性,字符串,唯一,必要属性',data:'数据属性,值类型任意,可选属性'}
		tips:{type:'ADD_STUDENT',data:{name:'chuancey',age:18}}
	reducer
		用于初始化状态,加工状态
		加工时,根据旧的state和action,产生新的state的纯函数
	store
		将state,action,reducer联系在一起的对象
		如何得到此对象?
			1).import {createStore} from 'redux'
			2).import reducer from './reducers'
			3).const store =createStore
		此对象的功能?
			1).getState();得到state,
			2).dispatch(action):分发action,触发reducer调用,产生新的state
			3).subscrible(listener):注册监听,产生新的state时,自动调用

react-redux的使用

所有UI组件都应该包裹一个容器组件,他们是父子关系
容器组件是真正与redux打交道的,里边可以随便使用redux的api store.dispath(action),store.getState();
UI组件不能使用任何redux的api
容器组件会传给UI组件: (1).redux中保存的所有状态 (2).用于操作的方法
容器组件给UI组件传递:状态,操作的方法,均通过Props传递 容器组件版存放在src目录下的container文件夹中

纯函数和高阶函数

是一类特殊的纯函数:只要是同样的输入(实参),必定得到同样的输出(返回)
必须遵守一下的约束
	不得改写参数数据
	不会产生副作用,例如网络请求,输入和输出设备
	不能调用Date.now()或者Math.random()等不纯的方法
redux的reducer函数必须是一个纯函数

redux开发者工具的使用

npm i  redux-devtools-extension 
在store中引入 { composeWithDevTools } from 'redux-devtools-extension'
createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))

使用umi 创建react项目

1.安装tyarn
	$ npm i yarn tyarn -g
2.使用tyarn创建 umi
	tyarn cerate umi 文件项目

案例总结

todoList案例总结
    1.拆分组件,实现静态组件,注意className/style写法
    2.动态初始化列表,如何确定将数据放在那个组件中的state中?
        (1).某个组件使用,放在自身的state中
        (2).某些组件使用,放在他们公用的父组件的state中
    3.父子组件通信
         父与子传递参数:使用props传递
         子与父传递参数:通过props传递,要求父提前传递给子组件一个函数
    4.注意defaultChecked 和check的区别 defaultChecked第一次渲染 checked渲染完之后不能修改需要绑定onChange
    5.状态在哪里,操作状态的方法放哪里

github搜索案例总结
	1.设计状态时要考虑全面 例如带有网络请求的组件 要考虑请求失败怎么办
	2.es6小知识点:结构+重命名
		let obj = {a:{b:1}}
		const {a} = obj //传统解构赋值
		const {a:{b}} = obj //联系解构赋值
		const {a:{b:c}} = obj //二次解构赋值并改名
	3.消息订阅与发布消息
		先订阅,在发布
		适用于任意组件间通信
		要在组件的componentWillUnMount中取消订阅
	4.Fetch发送请求(关注分离思想)
        try {
            const res = await fetch(`/search/users?q=${data}`)
            const reault = await res.json()
            PubSub.publish('updata', { users: reault.items, isLoading: false })
        } catch (err) {
            //错误统一处理
            PubSub.publish('updata', { err: '请求超时,请重试!!!', isLoading: false })
        }

redux
	(1).去除Count自身的状态
	(2).src下建立redux
			-redux
				-store.js          //创建store,
				-count_reducer.js //创建reducer, 定义为组件服务的reducer
				/*完整版新增*/
				-count_acton.js  //创建action, 定义Action对象
				-constant.js   //便于管理的同时,防止单词写错
	(3).store.js:
		1).引入reduce中的createStore函数,创建一个store,
		2).createState调用时传入一个为其服务的reducer
		3).暴露store对象
	(4).count_reducer.js
		1).reducer的本质上是一个函数,接受preState,action,返回加工后的状态
		2).reducer有两种作用:初始化状态,加工状态
		3).reducer第一次调用时,是store自动触发的,传递的是:
			preState:undefined
			action:{type:'@@INIT@@',data:''}
	(5).在index.js中检测store中状态的改变,一但发生改变就重新渲染<App/>
	 tips:redux只负责管理状态,至于状态的改变驱动页面的展示,要靠自己
	 
	 
redux(异步action)
	明确:延迟的动作不想交给组件,想交给action
	何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回.
	具体编码:
		npm i redux-thunk,并配置到store.js中 
			import thunk from "redux-thunk";
			import { createStore,applyMiddleware } from "redux";
			export default createStore(CountReducer,applyMiddleware(thunk))
		创建action的函数不在返回一个对象而是一个函数,该函数中写异步任务
		异步任务完成之后,分发一个同步的action去真正操作数据
	tips:异步action中不是必须要写的,完全可以自己等待异步任务的结果,再去分发同步action
	
react-redux
	明确两个概念:
        UI组件:不能使用redux的任何Api ,只负责页面的展示,交互等
        容器组件:负责与Redux进行通信,将结果交给UI组件
    如何创建容器组件 ---靠react-redux的connect函数
    	connect(mapStateToProps,mapDispatchToProps)(UI组件)
    		mapStateToProps:映射状态,返回一个对象
    		mapDispatchToProps:映射操作状态的方法,返回一个对象
    tips1:容器组件中的store是靠在组件中用props传进去的,而不是在容器组件中直接引入的
    tips2:mapDispatchToProps 传递两种参数 1.function 2.对象 传递action对象,react-redux会自动调用dispatch 来执行此action
        
react-redux(优化)
	(1).容器组件和UI组件整合为一个组件
	(2).无需自己给容器组件添加state,给<App/> 包裹一个<Provider store={store}>即可.
	(3).使用了react_redux后不需要自己在检测redux中状态的改变,容器组件可以自动完成
	(4).mapStateToProps可以简单的写出一个对象啊
	(5).一个组件和redux"打交道"要经过的几个步骤
		1).定义好UI组件 --不暴露
		2).引入connect生成一个容器组件,并暴露,写法如下:
			connect(
			state=>{},
			{key:value(action)}
			)(UI组件)
		3).在UI组件中通过this.props.xxx读取或操作状态

react-redux(数据共享)
	(1).定义一个Person类,和Count组件通过redux共享数据
	(2).为Person组件编写:reducer,action,配置constant常量
	(3).Person的reducer和Count的Reducer要使用combineReducers进行合并,合并之后为一个对象
	(4).交给store的是总的Reducer,最后注意在组件中取出状态的时候,记得"取到位"
	
react-redux(最终版)
	变量命名时,一定要尽量避免简写形式
	在reducer中创建index.js用于汇总所有的reducer,并暴露出去

react扩展

1. setState
setState更新状态的2种写法
	(1). setState(stateChange, [callback])------对象式的setState
            1.stateChange为状态改变对象(该对象可以体现出状态的更改)
            2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
					
	(2). setState(updater, [callback])------函数式的setState
            1.updater为返回stateChange对象的函数。
            2.updater可以接收到state和props。
            4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
		1.对象式的setState是函数式的setState的简写方式(语法糖)
		2.使用原则:
				(1).如果新状态不依赖于原状态 ===> 使用对象方式
				(2).如果新状态依赖于原状态 ===> 使用函数方式
				(3).如果需要在setState()执行后获取最新的状态数据, 
					要在第二个callback函数中读取
2. lazyLoad
路由组件的lazyLoad
	//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
	const Login = lazy(()=>import('@/pages/Login'))
	
	//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
	<Suspense fallback={<h1>loading.....</h1>}>
        <Switch>
            <Route path="/xxx" component={Xxxx}/>
            <Redirect to="/login"/>
        </Switch>
    </Suspense>
3. Hooks
1). React Hook/Hooks是什么?
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
2). 三个常用的Hook
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()
3). State Hook
(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)  
(3). useState()说明:
        参数: 第一次初始化指定的值在内部作缓存
        返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
        setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
        setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
4). Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM
(3). 语法和说明: 
        useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
(4). 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
    	componentWillUnmount() 
5). Ref Hook
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
4. Fragment
使用
<Fragment><Fragment>
<></>
作用

可以不用必须有一个真实的DOM根标签了


5. Context
理解

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

使用
1) 创建Context容器对象:	const XxxContext = React.createContext()  	2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:	<xxxContext.Provider value={数据}>		子组件    </xxxContext.Provider>    3) 后代组件读取数据:	//第一种方式:仅适用于类组件 	  static contextType = xxxContext  // 声明接收context	  this.context // 读取context中的value数据	  	//第二种方式: 函数组件与类组件都可以	  <xxxContext.Consumer>	    {	      value => ( // value就是context中的value数据	        要显示的内容	      )	    }	  </xxxContext.Consumer>
注意
在应用开发中一般不用context, 一般都用它的封装react插件

6. 组件优化
Component的2个问题
  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低

  2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

效率高的做法

只有当组件的state或props数据发生改变时才重新render()

原因

Component中的shouldComponentUpdate()总是返回true

解决
办法1: 	重写shouldComponentUpdate()方法	比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false办法2:  	使用PureComponent	PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true	注意: 		只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  		不要直接修改state数据, 而是要产生新数据项目中一般使用PureComponent来优化

7. render props
如何向组件内部动态传入带内容的结构(标签)?
Vue中: 	使用slot技术, 也就是通过组件标签体传入结构  <A><B/></A>React中:	使用children props: 通过组件标签体传入结构	使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
<A>  <B>xxxx</B></A>{this.props.children}问题: 如果B组件需要A组件内的数据, ==> 做不到 
render props
<A render={(data) => <C data={data}></C>}></A>A组件: {this.props.render(内部state数据)}C组件: 读取A组件传入的数据显示 {this.props.data} 

8. 错误边界
理解:

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

特点:

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

使用方式:

getDerivedStateFromError配合componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发static getDerivedStateFromError(error) {    console.log(error);    // 在render之前触发    // 返回新的state    return {        hasError: true,    };}componentDidCatch(error, info) {    // 统计页面的错误。发送请求发送到后台去    console.log(error, info);}
9. 组件通信方式总结
组件间的关系:
  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)
几种通信方式:
	1.props:		(1).children props		(2).render props	2.消息订阅-发布:		pubs-sub、event等等	3.集中式管理:		redux、dva等等	4.conText:		生产者-消费者模式
比较好的搭配方式:
	父子组件:props	兄弟组件:消息订阅-发布、集中式管理	祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

签体传入结构 使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

children props
<A>  <B>xxxx</B></A>{this.props.children}问题: 如果B组件需要A组件内的数据, ==> 做不到 
render props
<A render={(data) => <C data={data}></C>}></A>A组件: {this.props.render(内部state数据)}C组件: 读取A组件传入的数据显示 {this.props.data} 

8. 错误边界
理解:

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

特点:

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

使用方式:

getDerivedStateFromError配合componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发static getDerivedStateFromError(error) {    console.log(error);    // 在render之前触发    // 返回新的state    return {        hasError: true,    };}componentDidCatch(error, info) {    // 统计页面的错误。发送请求发送到后台去    console.log(error, info);}
9. 组件通信方式总结
组件间的关系:
  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)
几种通信方式:
	1.props:		(1).children props		(2).render props	2.消息订阅-发布:		pubs-sub、event等等	3.集中式管理:		redux、dva等等	4.conText:		生产者-消费者模式
比较好的搭配方式:
	父子组件:props	兄弟组件:消息订阅-发布、集中式管理	祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值