文章目录
React
3.19React全家桶
1.React所涉及到的技术有哪些?
- React基础
- React-Router:专门做路由的
- PubSub:消息管理库
- Redux:集中式状态管理库
- AntDesign:UI组件库
2.React是什么?
- 用于构建用户界面的javascript库(操作DOM呈现页面)
- React是一个将数据渲染为HTML视图的开源js库
3.组件化与模块化?
- 组件化:html、css、js放到同一个文件中,整个文件就是一个独立的组件
- 模块化:将函数挑出来封装到js文件中,只在需要的时候调用这个js文件即可,这就是模块化
4.React的特点?
- 采用组件化,声明式编码(自动化:(命令式)电风扇跟(声明式)空调)
- 在React Native中可以使用React语法进行移动端开发(用js编写安卓和IOS)
- 使用虚拟DOM+优秀的Diffing算法,减少真实DOM的交互
5.react需要的三个js文件?
- react.development.js(必须在第二个之前)=>生成React对象
- react.dom.development.js=>生成ReactDOM对象(render方法在这上面)
- babel.min.js
- react使用的是jsx语法
6.jsx的写法?
jsx的写法
<script type="text/babel">//一定要写babel
//此处写的不是js语句,而是react语句了
//1.创建虚拟DOM
const VDOM=<h1>HELLO,React</h1>
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById("xxx"))
</script>
-
jsx里面的标签能够跟js语句混着写
-
将jsx中的标签渲染到页面中,需要用react内置的方法React.render(VDOM,原生js方法获取元素)
注意点:
- text/babel区域内的标签代码,千万不要写引号
3.20
3.20虚拟DOM两种创建方式
1.使用jsx:const VDOM=hello
2.原生js:React.createElement(标签名,标签属性,标签内容)
例子:React.createElement(“h1”,{name=“haha”,age=22},“hello”)
JSX
如果想在第一个标签内部创建一个子标签,则直接以标签的形式实现:
const vdom=<h1><span>hello world</span></h1>
原生js(一般不用)
如果想在第一个标签内部创建一个子标签,则需要嵌套的方式实现:
React.createElement("h1",{name="haha",age=22},React.createElement(...))
总结:jsx就是原生js创建虚拟DOM的一种语法糖
- 关于虚拟DOM,本质是Object类型的对象(一般对象)
- 虚拟DOM比较轻,真实DOM比较重,因为虚拟DOM是React内部再用,无需太多属性
- 虚拟DOM会被React转化为真实DOM,呈现在页面上
3.20 jsx特点
- jsx的全称:javascript+ XML
- React定义的一种js扩展语法
- 本质上是创建
React.createElement(component,props,...children)
的一种语法糖
jsx中调用变量
//jsx中调用变量,需要加{}调用
//1.创建变量
const mid=1;
const name="sjh"
//2.渲染虚拟DOM到页面
ReactDOM.render(
<div id={mid}>{name}</div>
)
- 标签内想混入js表达式,需要用花括号的形式
- 定义虚拟DOM时,不要写引号
jsx中给标签定义类名
在js中定义类名用class
在jsx中定义类名用className
ReactDOM.render(
<div id={mid} className="mytest ">{name}</div>
)
jsx中的style内联样式
- js中的内联样式:
<h1 style="color:red"></h1>
- jsx中的内联样式:
<h1 style={{color:red}}></h1>
- 最外层{}表示要写js表达式了,内层{}表示写的是对象
- 内联样式中的键值对,
jsx中虚拟DOM不能有多个根节点
jsx内虚拟DOM的标签必须闭合
jsx中默认标签为小写,组件标签开头应大写
- 标签首字母
- 若为小写字母,则将标签改为html同名元素.若HTML中无该标签对应的同名元素,则报错。
- 若为大写字母,则去找对应组件
3.20jsx中数组的遍历
-
jsx中的虚拟DOM标签中,可以通过{}花括号引入表达式,但绝对不能直接引入语句
-
const data=["angular","vue"] //引入语句(错误) //创建虚拟DOM const VDOM=( <h1>前端框架</h1> <ul> { for(let i=0;i<data.length;i++){ <li>data[i]</li> } } </ul> ) //引入语句(正确) const VDOM2=( <ul> { data.map((item,index)=>{ return <li key={index}>{item}</li> }) } </ul> )
何为语句和表达式
- 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
- let a=null;a+b;arr.map()都是表达式
- 语句:代码,控制代码走向,没有返回值
- if(){}
- for(){}
- switch(){case:xxxx}
3.20模块与组件、模块化与组件化
-
模块:一个js文件相当于一个模块
- 复用js简化js的编写,提高js运行效率
-
组件:将一份js/css/html组合在一起,形成一个全功能性组件,叫做组件
-
模块化:当应用的js都以模块来编写,这个应用就是一个模块化的应用
-
组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用
3.20函数式组件和类式组件
执行ReactDom.render(,document.getElementById())之后发生了什么?
- 第一步:React解析组件标签,找到了MyComponent组件
- 第二步:发现组件是用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,呈现在页面中
函数式组件
-
函数式组件适用于简单组件的定义
-
//定义函数式组件 function Demo(){ return <h2>我是函数式组件</h2> } //使用函数式组件 ReactDom.render(<Demo/>,document.getElementById('test'))
-
以上为函数式组件
-
注意点:
- 因为babel自动开启了严格模式,所以函数式组件中的this是undefined
类式组件
类相关知识:
- 类的constructor构造器不是必须要写的,只有给实例添加一些私有的属性时才需要写
- 如果A类继承了B类,且A类写了构造器,那么A类的构造器中必须调用super()
类式组件:
-
想要创建类式组件,就必须要先继承另外一个类React.Component
-
类式组件中必须要写render()方法
-
类式组件中的render必须有返回值,且返回内容
-
创建组件时的render与渲染组件的render没有任何关系
-
render中的this是谁?->MyComponent中的实例对象,因为render是在new构造出实例后,通过实例调用的
-
除render函数之外,其他函数中的this都需要看情况而定,因为其他函数不是通过实例调用的
-
组件实例对象中的三大属性(state,refs,props)都是继承自React.Component类的构造器创建的
-
类中所有定义的方法,都局部开启了严格模式
-
//1.创建类式组件:继承React的组件类 class MyCompnent extends React.Component{ render(){ return <h1>react</h1> } } //2.渲染组件到页面上 ReactDOM.render(<MyComponent/>,document.getElementById("test"))
-
执行上面一段代码之后会经历什么?
- 第一步:React解析组件标签,找到MyComponent组件
- 第二步:发现组件是类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
- 第三步:将render返回的虚拟DOM转为真实DOM,随后呈现在页面上
如何修改state中的值,驱动页面更新内容?
- 如果我们想通过某个行为修改state中值,不能直接通过赋值修改state中的值,这样是错误的
3.20组件的三大属性
函数式组件中只能玩:props
,如果想规定props的属性类型,只能通过原始写法来定义propTypes和defaultProps
类式组件中三大组件都能玩:state、props、refs
state(状态)
组件实例的三大属性之一
- 人: 状态 影响 行为
- 组件:状态 驱动 页面
state注意点:
-
state在constructor中定义,如果没有构造器,则可以直接在类中定义state
-
state应该是一个对象,可以定义多个属性({key:value})
-
class MyComponent extends React.Component{ //方式一 constructor(props){ super(props) //初始化状态 this.state={ ishot:true } } //方式二 state={isHot=true} } render(){ //读取状态 let isHot=this.state }
-
组件被称为状态机,通过更新组件的state来更新对应的页面显示(重新渲染组件)
-
不能直接修改state中的值:需要用Api
this.setState({ishot:false})
重点、重点、重点:
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,解决方法:
- 1、强制绑定:bind
- 2、箭头函数
- 状态数据,不能直接修改或更新
state的简写方式
-
state应该是放在实例上的属性,所以在构造器中就是this.state,作为实例的状态值
-
但是在类中,实例的属性不仅可以在构造器中用this构造,直接在类中构造的属性,也会称为实例的私有属性
-
注意:类中不用定义属性,可以直接写赋值语句
-
//给Car的实例添加一个a和一个state私有属性 class Weather extends React.Component{ //初始化状态 state={name:11} //获取最终需要渲染的信息 render(){ return <h1></h1> } //自定义的方法:这样写能够保证方法中的this是实例本身 chageWeather=()=>{ this.setState({isHot:!isHot}) } }
-
自定义要用赋值语句+箭头函数的方式定义
-
props(属性)
-
给组件传的标签属性就能在类中的props属性中找到
-
props中的内容是只读的
-
class Person extends React.Component{ render(){ return ( <ul> //这里就能够通过实例的props属性找到实例标签中的name,age,sex <li>{this.props.name}</li> <li>{this.props.age}</li> <li>{this.props.sex}</li> </ul> ) } } //渲染组件到页面 ReactDom.render(<Person name="sjh" age=12 sex="male"/>,doument.getElementById("d1"))
-
props的语法糖(批量传递标签属性)
-
babel和react允许你在标签属性传递的过程中,用展开运算符去展开一个对象
-
//原始写法 ReactDom.render(<Person name="sjh" age=12 sex="male"/>,doument.getElementById("d1")) //语法糖写法 let p1={ name:"sjh",age:12,sex:"male"} ReactDom.render(<Person {...p1}/>,doument.getElementById("d1"))
-
注意点:传递的对象内的属性名,必须与需要的属性名匹配
-
规定props中的属性类型
-
给类添加事先定义好的规定属性类型属性Person.propTypes,就可以定义prop中的属性类型了
-
//对标签属性进行类型、必要性限制 Person.propTypes={ name:PropTypes.string//规定props中的name属性必须是字符串类型 age:PropTypes.number.isRequired //规定props中的age属性必须是数字且是必填的 speak:PropTypes.func//注意注意,如果想要规定函数类型,不能写小写的function,因为与默认的函数数据类型冲突了,需要写func } //指定默认标签属性值 Person.defaultProps={ sex:"不男不女",//如果实例没有给传sex属性的值,则sex为不男不女 age:18 }
-
Person.propTypes:组件限制属性类型的默认属性
-
PropTypes:定义属性类型的专门的类,想用它首先要引入
prop-types.js
-
Person.defaultProps:设置props属性中默认属性的值
-
注意注意,如果想要规定函数类型,不能写小写的function,因为与默认的函数数据类型冲突了,需要写func
-
props是只读的(单向数据流)
-
props的简写方式
-
将propTypes和defaultProps放到class类中,用static
-
class Person extends React.Component{ render(){ return ... } state={...} props={...} //将下面两个属性放在Person类中作为静态属性,而不是实例的私有或公有属性 static propTypes={...} static defaultProps={...} //自定义方法 func1=()=>{ ... } }
构造器中的props
- 构造器是否接收props,是否传递props,取决于是否需要在构造器中通过this访问props
函数式组件使用props
-
函数中所有传递的标签属性,都会传给函数的第一个参数对象props中
-
//props是一个对象类型的参数,接收所有给组件定义的属性 function Person(props){ return ( <ul> <li>姓名:{props.name}</li> <li>性别:{props.age}</li> </ul> ) } ReactDOM.render(<Person name="sjh" age=11/>,document.getElementById("p1"))
refs(引用)
-
原生js中获取真实DOM的方式:
let box=document.getElementById('box')
-
refs的作用是获取组件中标签的真实DOM,然后通过真实DOM实现事件或动作的交互
- ref属性值应该唯一,就像Id一样
- 每个实例都有一个refs私有属性,是对象类型,它会存放所有添加了ref标签的DOM元素
-
//第一步:在render渲染的标签中加上ref属性 class Demo extends React.Component{ render(){ return( <div> //在这里为input标签定义了ref属性 <input ref="inp"> <button onClick={this.show}>点击</button> </div> ) } //第二步:通过refs属性就能获取到已经添加了ref属性的标签:inp show=()=>{ console.log(this.refs.inp)//能够获取 } }
ref的定义方式
-
方式一(字符串类型):
<input ref="inp">
- 目前已经不推荐用了,因为字符串类型的ref存在效率问题
-
方式二(回调函数形式):
-
<input ref={c=>{this.inp1=c}}>
-
这种写法是利用了ref绑定的回调函数中能够接收到当前标签的节点,最后绑定到实例上作为实例的私有属性
-
如果回调函数是以内联形式定义的,在更新过程中会调用两次,第一次获取的参数是null,第二次获取的参数是当前节点,如果不想调用两次,可以用类中定义函数的形式
-
//类中定义回调函数方式 class Person extends React.Compnent{ getInput=()=>{ console.log(111) } render{ return( <input ref={this.getInput}/> ) } }
-
-
-
**方式三(createRef API)😗*生成一个容器,之后ref标签就可以直接放到这个容器中
-
该容器是专人专用的,每个容器中只能放一个标签
-
//类中定义回调函数方式 class Person extends React.Compnent{ //myRef就是一个容器 myRef=React.creatRef() myRef2=React.creatRef() render{ return( //将这个输入标签放到myRef容器中 <input ref={this.myRef}/> <input ref={this.myRef2}/> ) } }
-
总结:
- 尽量避免使用字符串的ref定义方式,内联目前看来是最舒服的
3.21
3.21react中标签的事件绑定
-
react中所有的原生html事件都被重写为驼峰命名的写法,比如:
- onclick->onClick
-
//原生js <button οnclick="demo()">按钮</button> //react:注意react中绑定事件的函数不能加括号,加括号会绑定成函数的回调函数 <button onClick={demo}>按钮</button> class Weather extends React.Component{ render(){ return <h1 onClick={this.changeWeather}></h1> } changeWeather(){ } }
-
当组件标签绑定事件时,一定要指明是哪里的函数this.changeWeather
3.27
3.27React中的事件处理
- 通过
onXxx
属性指定事件处理函数(需要注意大小写)- 1、React使用的是自定义(合成)事件,而不是使用原生DOM事件->为了更好的兼容性
- 2、React中的事件是通过事件委托的方式处理的(委托给组件最外层的元素)->为了高效
- 通过
event.target
得到发生事件的DOM元素对象->不要过度的使用ref
3.27React中收集表单中的数据
-
非受控组件:页面中所有输入类DOM(现用现取),它的内容不被state收录,这就是非受控组件(需要通过ref拿值)
-
受控组件:页面中的输入类DOM,他们的值被收录到state中,这就是受控组件,(不需要通过ref拿值)
3.27react中的柯里化函数
-
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的方式,就称为柯里化函数
-
对象中想读取一个变量,必须用[]括起来
-
class Person extends React.Component{ setFormData(dataType){ return (event)=>{ //注意:这里的dataType是在对象中设置,想要调用外部的dataType,必须加上中括号 this.setState({[dataType]:event.target.value}) } } render(){ return( <input onChange={{this.setFormData("username")}}> <input onChange={{this.setFormData("password")}}> ) } } ReactDOM.render(<Person/>,document.getElementById("p1"))
高阶函数
-
如果一个函数符合下面两个规范中的任何一个,它就是高阶函数:
- 若A函数,接收的参数是一个函数,那么他就是高阶函数
- 若A函数,return的值仍然是一个函数,那么它就是高阶函数
-
常见的高阶函数:
- Promise、setTimeOut、arr.map等
3.27组件的生命周期
**生命周期的作用:**在关键的点,调用某些函数,执行一些事情
生命周期回调函数<=>生命周期钩子函数<=>生命周期函数<=>生命周期钩子
生命周期图(旧):
![image-20220327213313399](https://i-blog.csdnimg.cn/blog_migrate/01fca49ff19e93ab53fdd98fb275a66d.png)
-
挂载过程中生命周期函数的执行过程:
constructor->componentWillMount->render->componentDidMount->componentWillUnmount
-
更新状态过程中生命周期函数的执行过程:
setState()->shouldComponentUpdate->
componentWillMount(组件将要挂载)
- 组件将要挂载的时候触发的方法(在render之前触发)
render(渲染组件)
- render是组件实例化后,组件的实例调用的第一个默认函数
- **调用时机:**初始化渲染、状态更新时
componentDidMount(组件挂载后)
- componentDidMount是组件挂载到页面之后调用的函数
componentWillUnmount(组件卸载之前)
- componentWillUnmount是组件将要卸载时调用的函数
shouldComponentUpdate(判断组件是否需要更新)
-
控制组件更新的阀门
-
当修改了信息之后,会调用shouldComponentUpdate来判断是否需要更新组件,如果该函数返回的是true则继续往下走,如果返回的是false则停止前进
-
如果不调用该函数,则会有一个默认的返回值,但是如果调用了该函数,则返回的值必须是布尔值,否则会报错
componentWillUpdate(组件将要更新时调用)
- 判断需要更新组件后调用的函数
componentDidUpdate(组件更新之后调用)
componentWillReceiveProps(组件将要接收props)
- 父组件触发了render时,子组件就会触发这个函数
- 父组件第一次render时不会调用,只有第二次触发render时才会调用
例子:
class Compon extends React.Component{
//组件实例化之后第一个调用的函数
constructor(){...}
//组件将要挂载的时候调用
componentWillMount(){...}
//组件渲染和更新的时候调用
render(){...}
//组件挂载到页面之后触发的函数
componentDidMount(){...}
//触发修改方式一:更改状态值数据
this.setState()
//修改方式二:强制更新组件
this.forceUpdate()
//用来判断组件是否需要更新时调用
shouldComponentUpdate(){ return false}
//组件更新之前调用
componentWillUpdate(){...}
//组件更新之后调用
componentDidUpdate(){...}
//组件将要被卸载时触发的函数
componentWillUnmount(){...}
}
3.27react组件中的父子关系
-
react中组件存在父子关系:在A的render方法中调用B组件标签,则组件A就是组件B的父
-
class A extends React.Component{ render(){ return( <div> <B/> </div> ) } } class B extends React.Component{ render(){ return( <div> B </div> ) } }