react中的组件定义方式有两种: 函数组件(无状态/UI组件) 类组件(状态组件/容器组件)
react组件一定要有返回值: 描述页面展示内容的React元素(jsx)
函数组件创建
函数组件本质: 就是一个函数
import React from 'react';
const AppFunction = () => {
return (
具体的页面内容
);
}
export default AppFunction;
安装插件: js jsx Snippets ===> rfc 可以快速创建模版代码
类组件创建
类组件: 一个类
import React from "react";
class App extends React.Component {
render(){
return (
class组件
)
}
}
export default App
快捷方式: rcc
函数具有二义性 : 在js中函数可以被当作构造函数通过new来调用,为了和类区分用new.target来判断是否是被new执行的
function Person(){
// 用new.target 来禁用函数被new, 只能当作函数来调用
if(new.target){
throw new Error('如果不是通过new执行的就返回undefined, 报次错误')
}
}
在react中一个函数一个类就是一个组件,区别与vue(一个文件就是一个组件),所以react中可以有多个组件
import React from 'react';
// const Child = (props) => { // 两种接受props的方式,一种可以直接接受整个props,但有个问题 ,在子组件中使用的时候,要通过对象调用的方式一个一个向外拿
const Child = ({title,num ='第二种:props是个对象,所以可以直接将里面的内容结构出来,还可以结构的同时进行赋初值'}) => {
// console.log(props)
return (
{/* 子组件 --{props.title}
*/}
子组件 --{title} {num}
);
}
const AppProps01 = () => {
const title = '我是父组件传过来的标题'
const num = undefined
return (
父组件
{/*
父组件,他是通过自定义属性的方式向子组件传递数据‘props’
props是只读属性并且是单向数据流,只能父传递给子 ,子只能读取值,不能修改值
*/}
{/* 调用自组建 */}
);
}
export default AppProps01;
import React, { Component } from 'react';
// 类组件间传值
class Child extends Component {
// 类组件中的子组件通过成员属性 this.props 来获取父组件中传过来的值
// this.props 是一个对象
render() {
// console.log(this.props)
const {title} = this.props
return (
class子组件 -- {title}
);
}
}
class AppClassProps01 extends Component {
// es8以后的成员属性,可以不用在construct中定义
title = 'class父类中的标题'
render() {
return (
class父组件
{/* 在类组件中,获取成员属性和成员方法时 ,一定要通过this来获取 */}
);
}
}
export default AppClassProps01;
面试题: TS中已经可以用联合属性来解决问题问什么还要用多态?
react中的事件分为原生事件和合成事件: 原生就是通过addEventLisenter的js操作进行的,而合成事件: 直接定义在react中的事件会被react处理,语法上有一些不同
要求
import React from 'react';
const clickHandler2 = () => ()=>{
console.log('webg == 我必搞')
}
//定义在组件函数的上面,const var let 没有限制
var clickHandler = () =>{
console.log('clickHandler')
}
const AppFunction01 = () => {
return (
{/*
1, 函数名用小驼峰
2, 绑定的事件方法不能加小括号,并且如果将执行的函数体放在jsx下面(组件函数的后面)的时候,一定要用function 来定义函数
3, 因为return会中断后面的代码执行,要用function来声明方法,是的变量提升并赋值
*/}
{/* 如果想用小括号,那么事件绑定的是一个函数体就可以 */}
);
}
// function clickHandler(){
// console.log('clickHandler')
// }
// 小括号触发事件
// function clickHandler2(){
// return function(){
// console.log('wgb==我必搞')
// }
// }
// function clickHandler2(){
// return ()=>{
// console.log('wbg === 我必搞')
// }
// }
export default AppFunction01;
思考两个问题 1,this的指向问题 2,call、apply、bind的作用和用法都是什么
import React, { Component } from 'react';
class AppFunction02 extends Component {
clickHandle1(){
console.log('clickHandle1')
}
clickHandle3(){
return ()=>{
console.log('clcikHandler3')
}
}
num = 100
clickHandle4(){
return ()=>{
console.log( this.num ) // 这样不报错
}
// console.log(this.num) // 这样就报错了,为什么, 解决方案可以用bind来改变this指向,为社么可以用bind解决
}
clickHandle5(){
console.log(this.num)
}
render() {
return (
{/* 类组件中的方法定义,不用关注方法的书写顺序是在return前还是return后 */}
{/* 同样的如果要加小括号,可以用柯里化函数 */}
{/* 类组件中有函数的调用有this指向问题 , */}
);
}
clickHandle2(){
console.log('clickHandle2')
}
}
export default AppFunction02;
react将原生事件进行处理:将所有的事件都绑定到挂在节点,目的: 对事件进行统一代理,使dom上不用绑定事件也可以用react挟持事件触发来实现操作,进行性能优化 , 这样可以通过对原事件的优先级定义来确定真是事件的优先级,再进而确定真实事件内触发的更新是什么优先级,最终确定对应更新应在什么时机更新 , 可以解决跨平台问题,抹平浏览器差异
react16之前绑定到body上,缺点: 只能有一个委托节点
16之后委托到挂载节点元素中(root),可以有多个委托节点,如果有多入口的操作应用时,可以分开委托(root1 , root2 ,…)
事件的执行分为: 捕获阶段 --> 目标阶段 --> 冒泡阶段
以类来举例
import React, { Component } from "react";
class APPs extends React.Component {
click() {
console.log("合成-冒泡- click1")
}
clickCapture() {
console.log('合成-捕获-click2')
}
componentDidMount() {
// addEventListener("事件类型" , function(){事件函数} , boolean(true : 捕获阶段执行 / false: 冒泡阶段执行))
document.getElementById('btn').addEventListener('click', () => {
console.log('原生- 冒泡 - btnClick')
}, false)
document.getElementById('btn').addEventListener('click', () => {
console.log('原生 - 捕获 - btnClick')
}, true)
document.getElementById('root').addEventListener('click', () => {
console.log('原生 - 冒泡 - 顶层元素click')
} , false )
document.getElementById('root').addEventListener('click', () => {
console.log('原生 - 捕获 - 顶层元素click')
} , true )
document.body.addEventListener('click' , () => {
console.log('原生 - 冒泡 - bodyClick')
} ,false )
document.body.addEventListener('click' , ()=>{
console.log('原生 - 捕获 - bodyClick')
} , true)
}
render() {
return (
)
}
}
export default APPs
合成事件和原生事件相结合的具体执行顺序: body-原生-捕获 —> 目标元素上级各节点绑定合成事件的捕获(capture) —> root-原生-捕获 (这两个阶段可以看作委托的合成事件在挂载节点先执行,然后执行挂在节点的原生捕获) —> 目标元素-原生-捕获 —> 目标元素-原生-冒泡 —> 目标元素上级个节点绑定的合成事件的冒泡执行 —> root-原生-冒泡(捕获的回程冒泡)—> body-原生-冒泡
react中合成事件的冒泡函数就是正常默认的函数,捕获阶段的函数为: 莫某Capture事件
原生事件的绑定: addEventLisenter(‘事件类型’ , 事件函数 , true/false 控制冒泡/捕获执行) 默认为false=== 冒泡执行,true===捕获执行
event.stopPropergation() event.stopImmediatePropergation()
stopImmediatePropergation
在js顺序执行到此事件时, 后面所有的事件都不在执行,包括当前元素自身的冒泡事件, 但书写顺序在此事件之前的冒泡事件不受影响(阻止当前元素中的未执行事件)react中为了减少元素增删时,相应绑定事件的销毁和再创建带来的性能损耗,将事件做成了合成事件,其实现原理如下
我是一个按钮