React基础

REACT(面向组件开发)
1.react高效原因
    1)使用虚拟DOM技术,不总是直接操作页面真实DOM
    2)DOM Diffing算法,最小化界面重绘
	
2.react使用jsx语法
    js创建虚拟DOM太繁琐,所以使用jsx创建虚拟DOM
	
3.关于虚拟DOM
    1.本质是Object类型的对象
    2.虚拟DOM比较轻,真实DOM比较重,因为虚拟DOM是React内部在用,无需真实DOM上那么多属性
    3.虚拟DOM最终会被react转换为真实DOM,呈现在界面上
	DOM:文档对象模型。DOM 为文档提供了结构化表示,并定义了如何通过脚本来访问文档结构。目的其实就是为了能让js操作html元素而制定的一个规范。
	
4.react引用的js文件
	①react.development.js react核心库
	②react-dom.development.js 支持react操作DOM
	③babel.min.js 将jsx转换为jS
	④prop-types.js 用于对组件标签属性进行限制
	
5.jsx语法规则
	①定义虚拟DOM时不要写引号
	②标签中混入JS表达式要用{}
	③样式的类名不要用class要用className
	④内联样式要用style={{key:value}}的形式去写,font-size等要用fontSize小驼峰样式
	⑤虚拟标签必须只有一个根标签
	⑥标签必须闭合
	⑦标签首字母
		(1)若小写字母开头,则将标签转换为html中同名元素,若html中无该标签对应的同名元素.则报错
		(2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错
		
6.区分jS代码(语句)和jS表达式
	①表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
		下面这些都是表达式
			(1)a 
			(2)a+b 
			(3)demo(1) 
			(4)arr.map() 
			(5)function test(){}
	②语句(代码):
		下面这些都是语句(代码):
			(1)if(){}
			(2)for(){}
			(3)switch(){}
			
7.模块和组件,模块化和组件化
	①模块 js文件 作用:复用js,简化js,提高js运行效率		
	②组件 实现局部功能效果的代码和资源的合集(html/css/js/image等等)
	③模块化 当应用的js都以模块来编写的,就是一个模块化的应用
	④组件化 当应用以多组件的方式实现,这个应用就是一个组件化的应用
	
8.函数组件(适用于简单组件[无state的组件]的定义)
	//1.创建函数式组件
	function MyComponent(){
		return <h2>函数组件</h2>
	}
	//2.渲染组件到界面 (组件首字母大写,标签闭合)
	ReactDOM.render(<MyComponent/>,document.getElementById('test'));
	
	react工作顺序:1.解析组件标签,之后寻找MyComponent组件
				  2.发现组件是使用函数定义的,随后调用该函数,将虚拟DOM转换为真实DOM,随后呈现在界面中.
				  
9.类式组件(适用于复杂组件[有state的组件]的定义)
	//1.创建类式组件 必须继承react中的一个类(React.Component)
	class MyComponent extends React.Component(){
		//必须有render 
		render(){
			//render中的this代表MyComponent的实例对象<=>MyComponent的组件实例对象
			//有返回值
			return <h2>类式组件</h2>
		}
	}
	//2.渲染组件到界面 (组件首字母大写,标签闭合)
	ReactDOM.render(<MyComponent/>,document.getElementById('test'));
	
	react工作顺序:1.解析组件标签,之后寻找MyComponent组件
				  2.发现组件是使用类定义的,随后new出该类实例,并通过该实例调用原型上的render方法.
				  3.将render返回的虚拟DOM转换为真实DOM,随后呈现在界面中.
				  

10.	组件实例的三大核心属性
	前端解构赋值
		const [a,b,c] = [1,2,3]
		const {name,age} = {name:'小明',age:18}
	10.1 state
		理解:1)state是组件对象的重要属性,值必须是对象(可以包含多个key-value的组合)
			2)组件被称为'状态机',通过更新组件的state来更新对应界面显示(重新渲染组件)
		注意:1)组件render中的this是组件实例对象
			2)组件自定义的方法中的this为undefined,如何解决
				a.强制绑定this:通过函数对象bind();
				b.箭头函数(开发中使用箭头函数)
			3)状态数据不能直接修改要使用setState方法	
			
		如下方代码
			标准写法:
				class Weather extrends React.Component{
					//构造器 调用1次
					constructor(props){
						//放到最前方
						super(props)
						//初始化状态
						this.state = {isHot:false,wind:'微风'}
						//解决changeWeather中的this指向,并挂在在实例对象上
						this.changeWeather = this.changeWeather.bind(this);
					}
					
					//调用1+n次,1是初始化的那次,n是状态更新的次数
					render(){
						//this为组件实例对象(即:Weather)
						retrun <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热':'凉爽'}</h1>
					}
					
					//点几次调用几次
					changeWeather(){
						//changeWeather放到了Weather的原型对象上,供实例使用
						//由于changeWeather作为onClick的回调,不是通过实例调用,是直接调用
						//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
						
						//获取原来isHot的值
						const isHot = this.state.isHot
						
						//严重注意:状态通过setState进行更新,且更新是一种合并不是替换
						this.setState({isHot:!isHot})
						
						
						//严重注意:状态(isHot)不能直接更改
						//错误写法this.state.isHot = !isHot;
					}
				}
				ReactDOM.render(<Weather/>,document.getElementById('test'));
			
			精简写法:
				//1.创建组件
				class Weather extrends React.Component{
					//初始化状态
					state = {isHot:false,wind:'微风'}
					
					render(){
						retrun <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热':'凉爽'}</h1>
					}
					// 自定义方法----要用赋值语句的形式+箭头函数
					// 箭头函数没有自身的this会自己找外层的this
					changeWeather = ()=>{
						const isHot = this.state.isHot
						this.setState({isHot:!isHot})
					}
				}
				//2.渲染组件到页面
				ReactDOM.render(<Weather/>,document.getElementById('test'));
	10.2 props
		理解:1)每个组件都会有props属性
			2)组件标签的所有属性都保存在props中
		做用:1)通过标签属性从组件外向组件内传递变化的数据
			2)注意:组件内部不能修改props数据
		
		如下方代码:
			类式组件使用props:
			复杂写法:
			//1.创建组件
			class Person extrends React.Component{
				render(){
					const {name,age,sex} = this.props
					//props是只读的
					retrun (
						<ul>
							<li>姓名:{name}</li>
							<li>性别:{sex}</li>
							<li>年级:{age+1}</li>
						</ul>
					)
				}
			}
			//对标签属性进行类型,必要性的限制
			Person.proTypes = {
				name:ProTypes.string.isRequired, //限制name必传,且为字符窜
				sex:ProTypes.string, //限制sex为字符串
				age:ProTypes.number, //限制age为数值
				speak:ProTypes.func, //限制speak为函数
			}
			
			//指定默认标签属性值
			Person.defaultProps = {
				sex:'男', //性别默认值为男
				age:18,//性别默认值为18
			}
			//2.渲染组件到页面
			ReactDOM.render(<Person name="lzq" speak={speak}/>,document.getElementById('test'));	
			
			精简写法:
			//1.创建组件
			class Person extrends React.Component{
				//对标签属性进行类型,必要性的限制
				static proTypes = {
					name:ProTypes.string.isRequired, //限制name必传,且为字符窜
					sex:ProTypes.string, //限制sex为字符串
					age:ProTypes.number, //限制age为数值
					speak:ProTypes.func, //限制speak为函数
				}
				
				//指定默认标签属性值
				static defaultProps = {
					sex:'男', //性别默认值为男
					age:18,//性别默认值为18
				}
				render(){
					const {name,age,sex} = this.props
					//props是只读的
					retrun (
						<ul>
							<li>姓名:{name}</li>
							<li>性别:{sex}</li>
							<li>年级:{age+1}</li>
						</ul>
					)
				}
			}
			//2.渲染组件到页面
			ReactDOM.render(<Person name="lzq" speak={speak}/>,document.getElementById('test'));	
			
			函数式组件使用props: //函数式组件没有state和refs,proTypes只能写在外侧
			//1.创建组件
			function Person(props){
				const {name,sex,age} = props
				retrun (
					<ul>
						<li>姓名:{name}</li>
						<li>性别:{sex}</li>
						<li>年级:{age}</li>
					</ul>
				)
			}
			//对标签属性进行类型,必要性的限制
			Person.proTypes = {
				name:ProTypes.string.isRequired, //限制name必传,且为字符窜
				sex:ProTypes.string, //限制sex为字符串
				age:ProTypes.number, //限制age为数值
				speak:ProTypes.func, //限制speak为函数
			}
			
			//指定默认标签属性值
			Person.defaultProps = {
				sex:'男', //性别默认值为男
				age:18,//性别默认值为18
			}
			//2.渲染组件到页面
			ReactDOM.render(<Person name="lzq" sex='男' age={18}/>,document.getElementById('test'));	
		
	10.3 refs(请勿过度使用refs)
		理解:组件内的标签可以定义ref属性来标记自己
		
		10.3.1 字符串形式的ref(不建议使用)
			//1.创建组件
			class Demo extrends React.Component{
				render(){
					retrun (
						<div>
							<input ref="input1" type="text" placeholder="点击按钮提示数据">
							<button onClick={this.showData1}>点击我提示左侧的数据</button>
							<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据">
						</div>
					)
				}
				showData1 = ()=>{
					const {input1} = this.refs
					alter(input1.value)
				}
				showData2 = ()=>{
					const {input2} = this.refs
					alter(input2.value)
				}
			}
			//2.渲染组件到页面
			ReactDOM.render(<Demo/>,document.getElementById('test'));	
			
		10.3.2 回调函数形式的ref 
			回调函数(1)你定义的函数 (2)你没调用 (3)最后别人调用了
			render的return中写js代码{}
			//1.创建组件
			class Demo extrends React.Component{
				showData1 = ()=>{
					const {input1} = this
					alter(input1.value)
				}
				showData2 = ()=>{
					const {input2} = this
					alter(input2.value)
				}
				saveInput = (c) => {
					this.input1 = c
				}
				render(){
					retrun (
						<div>
							{//注释后面代码:ref回调函数是内联式,在界面更新时执行两次,第一次传null,第二次传DOM元素}
							<input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据">
							{//使用内联式不影响使用,可以使用class类绑定函数的方式避免,以后还是使用内联的比较多}
							<input ref={this.saveInput} type="text" placeholder="点击按钮提示数据">
							<button onClick={this.showData1}>点击我提示左侧的数据</button>
							<input ref={c => this.input2 = c } onBlur={this.showData2} type="text" placeholder="失去焦点提示数据">
						</div>
					)
				}
			}
			//2.渲染组件到页面
			ReactDOM.render(<Demo/>,document.getElementById('test'));
			
		10.3.3 createRef (目前react最推荐的形式)
			//1.创建组件
			class Demo extrends React.Component{
				/*
					React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点.该容器是'专人专用'的
				*/
				//创建ref容器				
				myRef = React.createRef()
				myRef2 = React.createRef()
				//展示左侧输入框的数据		
				showData1 = ()=>{
					alter(this.myRef.current.value)
				}
				//展示右侧输入框的数据	
				showData2 = ()=>{
					alter(this.myRef2.current.value)
				}
				render(){
					retrun (
						<div>
							<input ref={this.myRef} type="text" placeholder="点击按钮提示数据">
							<button onClick={this.showData1}>点击我提示左侧的数据</button>
							<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据">
						</div>
					)
				}
			}
			//2.渲染组件到页面
			ReactDOM.render(<Demo/>,document.getElementById('test'));
	
12.	react中的事件处理
		(1)通过onXxx属性(如:onClick,onBlur等)指定事件处理函数(注意大小写)
			a.React使用的是自定义(合成)事件,而不是原生DOM事件(如:onClick是onclick重新封装的事件)---为了更好的兼容性
			b.React中的事件是通过事件委托方式处理的(委托给组件最外层元素) ---为了高效
		(2)通过event.target得到发生事件DOM元素对象	
		
		当发生事件的DOM元素和操作的DOM元素是一个时候可以省略ref(如下面onBlur事件)
		//1.创建组件
		class Demo extrends React.Component{
			/*
				React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点.该容器是'专人专用'的
			*/
			//创建ref容器				
			myRef = React.createRef()
			//展示左侧输入框的数据		
			showData1 = ()=>{
				alter(this.myRef.current.value)
			}
			//展示右侧输入框的数据	
			showData2 = (event)=>{
				alter(event.target.value)
			}
			render(){
				retrun (
					<div>
						<input ref={this.myRef} type="text" placeholder="点击按钮提示数据">
						<button onClick={this.showData1}>点击我提示左侧的数据</button>
						<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据">
					</div>
				)
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Demo/>,document.getElementById('test'));

13.收集表单数据
	13.1 非受控组件(页面内所有输入类DOM现用现取)
		//1.创建组件
		class Login extrends React.Component{
			handleSubmit = (event) => {
				event.preventDefault() //阻止表单提交
				const {username,password} = this
				alter(`你输入的用户名是:$(username.value),你输入的密码是:$(password.value)`)
			}
			render(){
				retrun (
					<form onSubmit={this.handleSubmit}>
						用户名:<input ref={c => this.username = c} type="text" name="username">
						密码:<input ref={c => this.password = c} type="password" name="password">
						<button>登录</button>
					</form>
				)
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Login/>,document.getElementById('test'));
	13.2 受控组件(页面内所有输入类的DOM,随着输入将状态维护在状态里面,需要用的时候从state中取出来)
		//1.创建组件
		class Login extrends React.Component{
			//初始化状态
			state = {
				username:'',//用户名
				password:'',//密码
			}
			
			//保存用户名到状态中
			saveUsername = (event)=>{
				this.setState({username:event.target.value})
			}
			
			//保存密码到状态中
			savePassword = (event)=>{
				this.setState({password:event.target.value})
			}
			
			//表单提交回调
			handleSubmit = (event) => {
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state
				alter(`你输入的用户名是:$(username),你输入的密码是:$(password)`)
			}
			render(){
				retrun (
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={this.saveUsername} type="text" name="username">
						密码:<input onChange={this.savePassword} type="password" name="password">
						<button>登录</button>
					</form>
				)
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Login/>,document.getElementById('test'));
		
14.高阶函数和函数的柯里化
		/*高阶函数:如果一个函数符合下面规范中的任何一个,那该函数就是高阶函数
			1.若A函数,接收的参数是一个函数,那该函数就可以称为高阶函数
			2.若A函数调用的返回值依然是一个函数,,那该函数就可以称为高阶函数
			常见高阶函数:Promise、setTimeout、arr.map()等等
		  函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式		
		*/

		使用高阶函数和函数柯里化的写法:
		//1.创建组件
		class Login extrends React.Component{
			//初始化状态
			state = {
				username:'',//用户名
				password:'',//密码
			}
			
			//保存表单数据
			saveFormData = (dataType)=>{
				return (event) => {
					this.setState({[dataType]:event.target.value})
				}
			}
			
			//表单提交回调
			handleSubmit = (event) => {
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state
				alter(`你输入的用户名是:$(username),你输入的密码是:$(password)`)
			}
			render(){
				retrun (
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={this.saveFormData('username')} type="text" name="username">
						密码:<input onChange={this.saveFormData('password')} type="password" name="password">
						<button>登录</button>
					</form>
				)
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Login/>,document.getElementById('test'));
		
		不使用函数柯里化的写法:
		//1.创建组件
		class Login extrends React.Component{
			//初始化状态
			state = {
				username:'',//用户名
				password:'',//密码
			}
			
			//保存表单数据
			saveFormData = (dataType,event)=>{
				this.setState({[dataType]:event.target.value})
			}
			
			//表单提交回调
			handleSubmit = (event) => {
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state
				alter(`你输入的用户名是:$(username),你输入的密码是:$(password)`)
			}
			render(){
				retrun (
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={ event => this.saveFormData('username',event)} type="text" name="username">
						密码:<input onChange={ event => this.saveFormData('password',event)} type="password" name="password">
						<button>登录</button>
					</form>
				)
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Login/>,document.getElementById('test'));
		
14.react的生命周期
		生命周期回调函数 <=>  生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
		
		14.1 生命周期(旧)
			14.1.1 初始化阶段(挂载时)
				①constructor() //构造器
				②componentWillMount() //组件将要挂载
				③render() //初始化渲染
				④componentDidMount() //组件挂载完毕
				
			14.1.2 更新阶段
				setState() 更新
				①shouldComponentUpdate() //控制组件更新的阀门
				②componentWillUpdate() //组件将要更新
				③render() //状态更新之后执行
				④componentDidUpdate() //组件更新完毕
				
				forceUpdate() 强制更新
				①componentWillUpdate() //组件将要更新
				②render() //状态更新之后执行
				③componentDidUpdate() //组件更新完毕
				
				父组件重新render触发
				①componentWillReceiveProps() //组件将要接受新的props的钩子
				②shouldComponentUpdate() //控制组件更新的阀门
				③componentWillUpdate() //组件将要更新
				④render() //状态更新之后执行
				⑤componentDidUpdate() //组件更新完毕
			
			
			14.1.3 卸载组件 由ReactDOM.unmountComponentAtNode()触发
				componentWillUnmount() //组件将要卸载
		
		14.2 生命周期(新)
			新的和旧的相比
			①新的可能弃用componentWillMount、componentWillUpdate、componentWillReceiveProps
			不要使用,如果使用新版本需要在这三个前面加UNSAFE_ 如:UNSAFE_componentWillMount 不是不安全而是乱用容易出现bug
			②新的新提出两个getDerivedStateFromProps和getSnapshotBeforeUpdate
			getDerivedStateFromProps 当state的值在任何时候都取决于props,使用场景罕见
			14.2.1 初始化阶段(挂载时)
				①constructor() //构造器
				②getDerivedStateFromProps()
				③render() //初始化渲染
				④componentDidMount() //组件挂载完毕 ----常用
					一般在这个钩子中做一些初始化的事情,例如:开启定时器、发送网路请求、订阅消息
				
			14.2.2 更新阶段
				setState()和父组件重新render触发
				①getDerivedStateFromProps() //衍生
				①shouldComponentUpdate() //控制组件更新的阀门
				③render() //状态更新之后执行
				④getSnapshotBeforeUpdate() //在更新之前获取快照
				④componentDidUpdate() //组件更新完毕
				
				forceUpdate() 强制更新
				①getDerivedStateFromProps() //衍生
				②render() //状态更新之后执行
				③getSnapshotBeforeUpdate() //在更新之前获取快照
				④componentDidUpdate() //组件更新完毕
			
			
			14.2.3 卸载组件 由ReactDOM.unmountComponentAtNode()触发
				componentWillUnmount() //组件将要卸载 ----常用
					一般在这个钩子中做一些收尾的事情,例如:关闭定时器、取消订阅消息
			
			getSnapshotBeforeUpdate使用场景:
				//1.创建组件
				class NewsList extrends React.Component{
					//初始化状态
					state = {newsArr:[]}
					
					componentDidMount(){
						setInterval(()=>{
							//获取原状态
							const {newArr} = this.state
							//模拟一条新闻
							const news = '新闻' + (newArr.length+1)
						},1000)
					}
					
					getSnapshotBeforeUpdate(){
						return this.refs.list.scrollHeight
					}
					
					④componentDidUpdate(preProps,preState,height){
						this.refs.list.scrollTop += this.refs.list.scrollHeight - height
					}
					
					render(){
						retrun (
							<div className="list" ref="list">
								{
									this.state.newArr.map((n,index)=>{
										return <div key={index} className="news">{n}</div>
									})
								}
							</div>
						)
					}
				}
				//2.渲染组件到页面
				ReactDOM.render(<NewsList/>,document.getElementById('test'));

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

16.父子组件之间的通信:
父组件给子组件传递数据:通过props
子组件给父组件传递数据:父组件给子组件传一个函数,当子组件要给父组件传东西的时候,通过props调用一下该函数

17.defaultChecked和checked的区别,defaultChecked只有在第一次指定的时候才起作用,以后不起作用

18.状态在哪里,操作状态的方法就在哪里

  

posted @ 2023-05-30 20:41  Zhuang_Z  阅读(7)  评论(0编辑  收藏  举报