react16.8-17 & 全家桶学习 Q&A

发布于:2022-12-17 ⋅ 阅读:(396) ⋅ 点赞:(0)

适用人群:刚开始学习 react 框架的同学,当作参考查阅。

说明:多以自己分析整理为主,如有错误理解请指正,谢谢!

笔记来源

开始 – React (Q1-2,有点傻的问题已经删掉了)

尚硅谷React教程(2022加更,B站超火react教程)_哔哩哔哩_bilibili (Q3-)

Q3:jsx语法规则详细

  • A3:如下

  • 定义虚拟dom时,不要写引号

  • 标签中混入js表达式要用{}

  • 样式的类名指定不要用class,用className

  • 内联样式,要用style={{key:value}}的形式去写

  • 只有一个根标签

  • 标签必须闭合

  • 标签首字母

    • 若小写字母开头,则将该标签转为html中的同名元素,若html中无该标签对应的同名元素。则报错
    • 若大写字母开头,react就去渲染对应的组件,若组件未定义,则报错

Q4:react创建虚拟dom的两种方式

A4:jsx和React.createElement()

Q5:真实dom与虚拟dom是什么与其的区别

是什么?virtual dom本质是js对象形式对dom的描述,而页面渲染出的每一个节点则是一个Real dom。

区别1:虚拟dom不会进行排版和重绘操作,而真实dom会频繁重排与重绘。

区别2:虚拟dom的总消耗是“虚拟dom增删改 + 真实dom差异增删改 + 排版与重绘”,真实dom的总消耗是“真实dom的完全增删改 + 排版与重绘”

本题题解:https://github.com/febobo/web-interview/issues/181

Q6:react类组件中this的指向问题?构造函数/render方法/自定义方法

  1. 构造函数一定指向类实例本身

  2. render方法是react new出实例后调用,因此this为类实例本身

  3. 类中自定义方法this指向,如下changeWeather中this为undefined

//A6案例 此时自定义函数中this指向存在问题
class Weather extends React.Component{
  constructor(props){
    super(props)
    this.state = {isHot :false}
  }

  render() {
    return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1>
  }

  changeWeather(){
    //changeWeather放在了哪里? -- Weather原型对象上,供所有实例使用
    //通过Weather实例调用changeWeather时,changeWeather中的this就是Weather实例
    //但由于一般自定义方法都是绑定在onClick上,因此不是实例调用,只是在绑定时将这个函数给了onClick做回调而已。当用户点击时是直接去堆内存中找到这个函数然后直接调用,因此this在严格模式下一般都为undefined
    console.log(this)
  }
}

Q7:react类组件自定义的方法中的this为undefined,如何解决?两种解决方案如下

//A7,1(第一种解决方案:强制绑定this:通过函数对象的bind())
class Weather extends React.Component{
  constructor(props){
    super(props)
    this.state = {isHot :false}
    //实例对象自身多了一个changeWeather,后续调用即可
    this.changeWeather = this.changeWeather.bind(this)
  }

  render() {
    return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1>
  }

  changeWeather(){
    //changeWeather放在了哪里? -- Weather原型对象上,供所有实例使用
    //通过Weather实例调用changeWeather时,changeWeather中的this就是Weather实例
    //但由于一般自定义方法都是绑定在onClick上,因此不是实例调用,只是在绑定时将这个函数给了onClick做回调而已。当用户点击时是直接去堆内存中找到这个函数然后直接调用,因此this在严格模式下一般都为undefined
    console.log(this)
  }
}
//A7,2(第二种解决方案:使用赋值语句和箭头函数书写changeWeather 
class Weather extends React.Component{
  constructor(props){
    super(props)
    this.state = {isHot :false}
  }

  render(){
    return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1>
  }

  changeWeather = () => {
    console.log(this)
  }
}

Q8:什么是state? 如何在类组件中使用state?

A8,1什么是state?
  1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)

  2. 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

//A8,2如何在类组件中使用state?
//this.state.xxx = this.state.xxx + 1 //错误写法(状态数据,不能直接修改或更新)
this.setState({xxx:!xxx})//正确写法(使用react内置api去更改),注意此api的使用是合并操作

Q9:精简类组件?干掉constructor

//A9 
class Weather extends React.Component{
  //内部状态存在于Weather实例对象
  state = {isHot :false}
   
  render() {
    return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1>
  }
  
  //自定义方法 - 要用赋值语句的形式 + 箭头函数
  changeWeather = () => {
    console.log(this)
  }
}

Q10

  1. 什么是props?

  2. 在类组件中props的使用?

  3. 对props的一些限制?

  4. props简写方式?

  5. 类构造函数中props的使用?

  6. 函数组件中props的使用?

A10,1 什么是props?
  1. 每个组件对象都有props(properties的简写)属性

  2. 组件标签所有属性都保存在props中

//A10,2在类组件中props的使用?
class Person extends React.Component{
  //内部状态存在于Weather实例对象
  render() {
    const {name,sex,age} = this.props
    
    return (
     <ul>
         <li>姓名:{name}</li>
         <li>性别:{sex}</li>
         <li>年龄:{age}</li>
     </ul>
    )
  }
  
  //自定义方法 - 要用赋值语句的形式 + 箭头函数
  changeWeather = () => {
    console.log(this)
  }
}

//props传参方法1,要求键值对的形式,如果要传数值,要用{}表示是js中的数值
ReactDOM.render(<Person name="whh" age={18} sex="男"/>,document.querySelector(".test"))

//props传参方法2
const p1 = {name:"whs",age:18,sex:"男"}
ReactDOM.render(<Person {...p}/>,document.querySelector(".test1"))
//A10,3对props的一些限制与指定默认值?需要引入 prop-type.js 对标签属性类型以及必要性的限制
//目前是给Person类自身加属性,在外侧
Person.propTypes = {
    name:PropTypes.string.isRequired,
    sex:PropTypes.string,
    age:PropTypes.number
    speak:PropTypes.func//方法限制要用func而不是function关键字
}
//指定默认标签属性值
Person.defaultProps = {
    sex:"男"
    age:18
}
//A10,4props简写方式?在类中给Person类自身加属性
class Person extends React.Component{
   static propTypes = {
    name:PropTypes.string.isRequired,
    sex:PropTypes.string,
    age:PropTypes.number
    speak:PropTypes.func//方法限制要用func而不是function关键字
   }
    
    //指定默认标签属性值
  static defaultProps = {
    sex:"男"
    age:18
  }
  
  //内部状态存在于Weather实例对象
  render() {
    const {name,sex,age} = this.props
    
    return (
     <ul>
         <li>姓名:{name}</li>
         <li>性别:{sex}</li>
         <li>年龄:{age}</li>
     </ul>
    )
  }
  
  //自定义方法 - 要用赋值语句的形式 + 箭头函数
  changeWeather = () => {
    console.log(this)
  }
}
//A10,5构造函数中props的使用?接住props并传给super与不接住不传的区别
class Person extends React.Component{
   constructor(props){
   //接住props并传给super后在可以访问构造函数中this.props,但不传给super则constructor中this.props为undefined,可以分别打开以下两行代码试试看
       //super(props)
       //super()
       console.log(this.props)
   }
   
   static propTypes = {
    name:PropTypes.string.isRequired,
    sex:PropTypes.string,
    age:PropTypes.number
    speak:PropTypes.func//方法限制要用func而不是function关键字
   }
    
    //指定默认标签属性值
   static defaultProps = {
    sex:"男"
    age:18
   }
  
  //内部状态存在于Weather实例对象
  render() {
    const {name,sex,age} = this.props
    
    return (
     <ul>
         <li>姓名:{name}</li>
         <li>性别:{sex}</li>
         <li>年龄:{age}</li>
     </ul>
    )
  }
  
  //自定义方法 - 要用赋值语句的形式 + 箭头函数
  changeWeather = () => {
    console.log(this)
  }
}
//A10,6函数组件中props的使用?
function Person(props){
    const {name,sex,age} = props
    
    return (
        <ul>
             <li>姓名:{name}</li>
             <li>性别:{sex}</li>
             <li>年龄:{age}</li>
         </ul>
     )
}

//对标签属性类型以及必要性的限制
Person.propTypes = {
    name:PropTypes.string.isRequired,
    sex:PropTypes.string,
    age:PropTypes.number
    speak:PropTypes.func//方法限制要用func而不是function关键字
}
//指定默认标签属性值
Person.defaultProps = {
    sex:"男"
    age:18
}

//props传参方法1,要求键值对的形式,如果要传数值,要用{}表示是js中的数值
ReactDOM.render(<Person name="whh" age={18} sex="男"/>,document.querySelector(".test"))

Q11:ref的三种创建方法

  1. string绑定在标签

  2. 回调Refs (内联与类的绑定函数)

  3. React.createRef()

//string绑定在标签
class Demo extends React.Component {
  showData = () => {
    console.log(this.refs.input);
  };

  render(): React.ReactNode {
    return (
      <div>
        <input ref="input" type="text" placeholder="点击按钮提示数据"></input>
        <button onClick={this.showData}>点我提示左侧数据</button>
      </div>
    );
  }
}
//回调Refs 内联回调
class Demo extends React.Component {
  showData = () => {
    const {input} = this
    console.log(input)
  };

  render(): React.ReactNode {
    return (
      <div>
        <input ref={(currectNode) => {this.input = currectNode}} type="text" placeholder="点击按钮提示数据"></input>
        <button onClick={this.showData}>点我提示左侧数据</button>
      </div>
    );
  }
}
//回调Refs 类的绑定函数
class Demo extends React.Component {
  showData = () => {
    const {input} = this
    console.log(input)
  };
  
  savaInput = (c) => {
      this.input = c
      console.log(c)
  }

  render(): React.ReactNode {
    return (
      <div>
        <input ref={this.savaInput} type="text" placeholder="点击按钮提示数据"></input>
        <button onClick={this.showData}>点我提示左侧数据</button>
      </div>
    );
  }
}
//React.createRef()
class Demo extends React.Component {
  myRef = React.creatRef()
  
  showData = () => {
    const {input} = this
    console.log(input)
  };
  
  savaInput = (c) => {
      this.input = c
      console.log(c)
  }

  render(): React.ReactNode {
    return (
      <div>
        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"></input>
        <button onClick={this.showData}>点我提示左侧数据</button>
      </div>
    );
  }
}

Q12:非受控组件与受控组件的理解?

  1. 非受控组件,一般是用ref实现,当用户提交表单时再去获取内容(现用现取节点值,不受我们监听state状态)

  2. 受控组件,一般用onChange来实现的,当用户改变input内容的时候自动调用onChange的回调然后去改变state中的状态(类似于vue中的双向绑定)

//非受控组件案例 ,现用现取
class Login extends React.Component {
  handleSubmit = (event) => {
    const { username, password } = this;
    if (username != null && password != null) {
      alert(
        `你输入的用户名是:${username.value}},你输入的密码是:${password.value}`
      );
    }
  };

  render(): React.ReactNode {
    return (
      <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>
    );
  }
}
//onChange与state是受控组件实现的重点
class Register extends React.Component {
  state = { username: "", password: "" };

  saveUsername = (event: any) => {
    this.setState({ username: event.value });
  };

  savePassword = (event: any) => {
    this.setState({ username: event.value });
  };

  handleSubmit = (event: any) => {
    const { username, password } = this.state;
    alert(`你输入的用户名是:${username},你输入的密码是:${password}`);
  };

  render(): React.ReactNode {
    return (
      <form onSubmit={this.handleSubmit}>
        <input onChange={this.saveUsername} type="text" name="username" />
        <input onChange={this.savePassword} type="password" name="password" />
        <button>登录</button>
      </form>
    );
  }
}

Q13:什么是高阶函数与函数柯里化?在react中的使用场景?

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。

  1. 若A函数,接收的参数是一个函数,那么A就可以称为高阶函数

  2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数

常用的高阶函数有:Promise、setTimeout、数组的方法

函数柯里化:通过调用 继续返回函数 的方式,实现多次接受参数最后统一处理的函数编码方式

//A13:函数柯里化,原生js案例

//未使用函数柯里化的求和函数
/* function sum(a,b,c){
    return a+b+c
} */

function sum (a) {
    return (b) => {
        return (c) => {
            return a+b+c
        }
    }
}

const result = sum(1)(2)(3)
//A13 函数柯里化在react中的使用
class Register extends React.Component {
  state = { username: "", password: "" };

  saveUsername = (event: any) => {
    this.setState({ username: event.value });
  };

  savePassword = (event: any) => {
    this.setState({ username: event.value });
  };

  saveData = (dataType:string) => {
    return (event:any) => {
      this.setState([dataType],event.target.value)
    }
  }

  handleSubmit = (event: any) => {
    const { username, password } = this.state;
    alert(`你输入的用户名是:${username},你输入的密码是:${password}`);
  };

  render(): React.ReactNode {
    return (
      <form onSubmit={this.handleSubmit}>
        <input onChange={this.saveData("username")} type="text" name="username" />
        <input onChange={this.saveData("password")} type="password" name="password" />
        <button>登录</button>
      </form>
    );
  }
}

Q14:不用高阶函数和函数柯里化也能接受到监听事件中传入的dataType和event(如下列代码所示)

//A13 函数柯里化在react中的使用
class Register extends React.Component {
  state = { username: "", password: "" };

  saveUsername = (event) => {
    this.setState({ username: event.value });
  };

  savePassword = (event) => {
    this.setState({ username: event.value });
  };

  saveData = (dataType,event) => {
    this.setState([dataType],event.target.value)
  }

  handleSubmit = (event) => {
    const { username, password } = this.state;
    alert(`你输入的用户名是:${username},你输入的密码是:${password}`);
  };

  render(): React.ReactNode {
    return (
      <form onSubmit={this.handleSubmit}>
        <input onChange={(event) => {this.saveData("username",event)}} type="text" name="username" />
        <input onChange={(event) => {this.saveData("password",event)}} type="password" name="password" />
        <button>登录</button>
      </form>
    );
  }
}

Q15:旧生命周期钩子流程介绍?(React16.x以及之前)

初始化阶段:由ReactDOM.render()触发 --初次渲染

  1. constructor()
  2. componentWillMount()
  3. render()
  4. componentDidMount() -->常用 (一般在这个钩子中做一些初始化的工作,例如:开启定时器、发送网络请求、订阅消息)

更新阶段:由组件内部this.setState()或父组件render触发

  1. shouldComponentUpdate()
  2. componentWillUpdate()
  3. render() -->必须调用一个

卸载组件:由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount() -->常用 (一般在这个钩子中做一些收尾的工作,例如:关闭定时器、取消订阅消息)

Q16:新生命钩子的流程介绍?(React17.x-至今)

初始化阶段:由ReactDOM.render()触发 --初次渲染

  1. constructor()
  2. getDerivedStateFromProps
  3. render()
  4. componentDidMount() -->常用 (一般在这个钩子中做一些初始化的工作,例如:开启定时器、发送网络请求、订阅消息)

更新阶段:由组件内部this.setState()或父组件render触发

  1. getDerivedStateFromProps
  2. shouldComponentUpdate()
  3. render() -->必须调用一个
  4. getSnapshotBeforeUpdate
  5. componentDidUpdate()

卸载组件:由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount()

Q17

  1. react/vue中的key有什么作用?(key的内部原理是什么)

  2. 为什么遍历列表时,key最好不要用index?

虚拟DOM中key的作用:

  1. 简单的说:key是虚拟DOM对象的表示,在更新显示时key起着至关重要的作用
  2. 详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】和【旧虚拟DOM】的diff比较,比较规则如下:
    1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
      1. 若虚拟DOM中内容没有变,则直接用之前的真实DOM
      2. 若虚拟DOM中内容变了,则生成新的真实DOM,替换掉页面之前的真实DOM
      3. 旧虚拟DOM未找到与新虚拟DOM相同的key,根据数据创建新的真实DOM,随后渲染到页面

用index作为key可能会引发的问题:

  1. 若对数据进行:逆向添加,删除等破坏规则操作:会产生没有必要的真实DOM的更新 --> 虽然界面没问题,但效率低
  2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 --> 界面有问题
  3. 注意!如果不存在对数据的逆向添加、逆向删除等破坏顺序操作。仅用了渲染列表用于展示,使用index作为key是没有问题的

Q18:todoList案例相关知识点

  1. 拆分组件、实现静态组件,注意className、style的写法
  2. 动态初始化列表、如何确定将数据放在哪个组件的state中?
    • 某个组件使用:放在其自身的state中
    • 某些组件使用:放在他们的共同的父组件state中(官方称为:状态提升)
  3. 关于父子之间通信:
    1. 【父组件】给【子组件】传递数据:通过props传递
    2. 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
  4. 注意 defaultChecked 和 checked 的区别,类似还有:defaultValue 和 value
  5. 状态在哪里,操作状态的方法就在哪里

Q19:github搜索案例涉及知识点

  1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办

  2. ES6小知识点:解构赋值 + 重命名

let obj = {a:{b:1}}

const {a} = obj

const {a:{b}} = obj

const {a:{b:value}} = obj   
  1. 消息订阅与发布机制
    1. 先订阅,再发布
    2. 适用于任何组件通信
    3. 要在组件中componentWillUnmount中取消订阅
  2. fetch发送请求(关注分离的设计思想)

Q20:前端路由基本原理

如果想在原生中自己封装一个路由库可以基于history封装好的基石(去bootcdn上搜索history)

方法一:直接使用h5推出的history身上的api

方法二:hash值(瞄点)路径带#(兼容性强)

Q21:react路由的基本使用

温馨提示:目前尚硅谷教的是5版本 npm i react-router-dom@5

1.明确好界面导航区和展示区

2.导航区的a标签改为Link标签

3.展示区些Route标签进行路径的匹配

<Route path='/xxx' component={Demo}/>

4.<App>的最外侧包裹了一个<BrowserRouter>或者<HashRouter>

Q22:路由组件与一般组件

  1. 写法不同

    1. 一般组件:<Demo />
    2. 路由组件:<Route path="/demo" component={Demo} />
  2. 存放位置不同

    1. 一般组件:component
    2. 路由组件:pages
  3. 接收的props不同

    1. 一般组件:写组件标签时传递了什么,就能收到什么
    2. 路由组件:接受3个固定的属性
    history:
    go: f go(n)
    goBack:f goBack()
    goForward:f goForward()
    push:f push(path,state)
    replace:f replace(path,sstate)
    
    Location:
    pathname:"/about"
    search:""
    state:undefined
    
    match:
    params:{}
    path:"/about"
    url:"/about"
    

Q23:NavLink与封装NavLink

  1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

  2. 标签内容是一个特殊属性(children)

  3. 通过this.props.children可以获取标签体中的内容

Q24: react解决样式丢失问题

  1. 在public下的html中解决
<!--不要使用的方式-->
<link href="./css/bootstrap.css" rel="stylesheet">
<!--方案一:在本html中直接往css中查找,而不会带上前端路由-->
<link href="/css/bootstrap.css" rel="stylesheet">
<!--方案二:在public相对路径下查找,%PUBLIC_URL%只有在react中有用-->
<link href="%PUBLIC_URL%/css/bootstrap.css" rel="stylesheet">
  1. 使用router的HashRouter解决,因为#后的网络访问时认为是前端资源,因此不会将前端路由带上。

Q25:react路由的严格匹配和模糊匹配

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)

  2. 开启严格匹配

<Route exact={true} path="/about" component={About}/>
  1. 严格模式不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

Q26:路由重定向的使用

<Switch>
    <Route path="/about" component={About} />
    <Route path="/home" component={Home} />
    <!--重定向兜底使用,上面的都匹配不到就去找to的路径-->
    <Redirect to="/about" />
</Switch>

Q27:嵌套路由

  1. 注册子路由时要写上父路由的path值

  2. 路由的匹配是按照注册路由的顺序进行的

Q28:向路由组件传递参数(使用优先级从上到下)

  1. params参数
<!--路由链接(携带参数)-->
<Link to="/demo/test/tom/18">详细</Link>
<!--注册路由(声明接收)-->
<Route path="/demo/test/:name/:age" component={Test} />
<!--接收参数:this.props.match.params-->
  1. search参数
<!--路由链接(携带参数)-->
<Link to="/demo/test?name='tom'&age=18">详细</Link>
<!--注册路由(无需声明,正常注册即可)-->
<Route path="/demo/test" component={Test} />
<!--接收参数:this.props.location.search-->
<!--备注:获取到的search是urlencoded编码字符串,需要借助querystring解析-->

3 state参数(需要处理保密数据用)

<!--路由链接(携带参数)-->
<Link to={{path:'/demo/test',state:{name:'tom',age:18}}}>详细</Link>
<!--注册路由(无需声明,正常注册即可)-->
<Route path="/demo/test" component={Test} />
<!--接收参数:this.props.location.state-->
<!--备注:刷新也可以正常保留参数-->

Q29:编程式路由导航

借助this.props.history对象上的api对操作路由跳转、前进、后退

  • this.props.history.push()

  • this.props.history.replace()

  • this.props.history.goBack()

  • this.props.histroy.goForward()

  • this.props.history.go()

Q30:withRouter的使用

  1. withRouter可以加工一般组件,让一般组件具备由组件所特有的api

  2. withRouter的返回值是一个新组件

Q31:BrowserRouter和HashRouter的区别

  1. 底层原理不一样

    1. BrowserRouter使用的是H5的history api,不兼容ie9及以下版本
    2. HashRouter使用的是url的哈希值
  2. url表现形式不一样

    1. BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
    2. HashRouter的路径中包含#,例如localhost:3000/#/demo/test
  3. 刷新后对路由state参数的影响

    1. BrowserRouter没有任何影响,因为state保存在history对象中
    2. HashRouter刷新后会导致由state参数的丢失
  4. 备注:HashRouter可以用于解决一些路径错误相关的问题

Q32:UI组件库

国内使用多的antd

官方文档:https://ant.design/docs/react/introduce-cn

主要掌握:按需引入和自定义主题

国外使用多material ui

官方文档:https://material.io/

Q33:求和案例_redux精简版

  1. 去除Count组件自身状态

  2. src下建立:

    1. -redux
      1. store.js
      2. count_reducer.js
  3. store.js

    1. 引入redux的createStore函数,创建一个store
    2. createStore调用时要传入一个为其服务的reducer
    3. 暴露store给你的组件使用
  4. count_reducer.js

    1. reducer的本质是一个函数,接收:preState,action,返回加工后的状态
    2. reducer有两个作用:初始化状态,加工状态
    3. reducer被第一次调用时,是store自动触发的,传递的preState是undefined
  5. 在index.js中检测store中状态的改变,一旦发生改变重新渲染

    1. 备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写

Q34:求和案例_redux异步action版

  1. 明确:延迟的动作不想交给组件自身,想交给action

  2. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。

  3. 具体编码:

    1. npm i redux-thunk,并配置在store中
    2. 创建action的函数不再返回一个一般对象,而是一个正在的函数,该函数中写异步任务
    3. 异步任务有结果后,分发一个同步的action去真正操作数据
  4. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步的action

Q35:react-redux

使用目的是讲react组件和redux api分开

在这里插入图片描述

  1. 明确两个概念:

    1. UI组件:不能使用任何的redux的api,只负责页面的呈现和交互等
    2. 容器组件:负责和redux通信,将结果交给UI组件
  2. 如何创建一个容器组件 - 靠react-redux的connect函数

    1. connect(mapStateToProps.mapDispatchToProps)(组件)
      1. -mapStateToProps:映射状态,返回的是一个对象
      2. -mapDispatchToProps:映射操作状态的方法,返回值是一个对象
  3. 备注:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入

Q36:求和案例-react-redux优化

  1. 容器组件和UI组件合并为一个文件

  2. 无需自己给容器组件传递store,给<App/>包裹一个 <Provider store={store}></Provider>即可

  3. 使用react-redux后不再需要自己去检测redux状态的变化,容器组件自动帮你完成这个工作

  4. mapDispatchToProps也可以简单的写成一个对象

  5. 一个组件要和redux"打交道"要经过那几步?

    1. 定义好UI组件–不要暴露

    2. 引入react-redux的connect方法生成一个容器组件,并暴露,写法如下:

      connect((state) => ({ count: state }), {
        jia: createIncrementAction,
        jian: createDecrementAction,
        jiaAsync: createIncrementAsyncAction,
      })(Count);
      
    3. 在UI组件中通过this.props.xxxx读取和操作状态

Q37:求和案例-react_redux数据共享版

  1. 定义一个Person组件,和Count组件通过redux共享数据

  2. 为Person组件编写:reducer、action,配置constant常量

  3. 重点:Person的reducer和Count的Reducer要使用combineReducer进行合并,合并是重点

  4. 交给store的是总reducer,最后注意在组件中取出状态的时候,记得要“取到位”

Q38:纯函数

  1. 一类特别的函数:只要是同样的输入(实参),必须得到同样的输出(返回)

  2. 必须遵从以下一些约束

    1. 不得改写参数数据
    2. 不会产生任何副作用,例如网络请求,输入和输出设备
    3. 不能调用Date.now()或者Math.random()等不纯的方法
  3. redux的reducer函数必须是一个纯函数

Q39:求和案例_react_redux开发者工具的使用

  1. npm i redux-devtools-extension

  2. .store中进行配置

    1. import { composeWithDevTools } from "redux-devtools-extension";
    2. createStore(reducers,composeWithDevTools(applyMiddleware(thunk)));

Q40:求和案例_react_redux最终版

  1. 所有变量名字要规范,尽量触发对象的简写形式

  2. reducers文件中,编写index.js专门用于汇总并暴露所有的reducer

Q41:Hooks

  1. React Hook/Hooks是什么?
Hook是React 16.8.0版本新增的新特性/新语法
可以让你在函数组件中使用state以及其他React特性
  1. 三个常用的Hook
State Hook:React.useState()
Effect Hook:React.useEffect()
Ref Hook:React.useRef()
  1. State Hook
(1)State Hook让函数组件也可以有state状态,并进行状态数据的读写操作
(2)语法:const [xxx,setXxx] = React.useState(initValue)
(3)useState()说明:
    参数:第一次初始化指定的值在内部作缓存
    返回值:包含2个元素的数组,第一个内部当前状态值,第二个为更新状态的函数
(4)setXxx()2种用法
    setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
    setXxx(value => newValue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
  1. Effect Hook
(1)Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2)React中的副作用操作:
    发ajax请求数据获取
    设置订阅 / 启动定时器
    手动更改真实DOM
(3)语法和说明
    useEffect(()=>{
        //在此可以执行任何带副作用的操作
        return ()=>{
            //在此像一些收尾工作,比如清楚定时器/取消订阅等
        }
    },[stateValue])//如果指定的是[],回调函数只会在第一次render()后执行
(4)可以把 useEffect Hook看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
        componentWillUnmount()
  1. Ref Hook
(1)Ref Hook可以在函数组件中存储/查找组件内的标签或任意其他数据
(2)语法:const refContainer = useRef()
(3)作用:保存标签对象,功能与React.createRef()一样

Q42:Fragment

使用

<Fragment></Fragment>
<></>

Q43:Context

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

(1)创建Context容器对象
    const XxxContext = React.createContext()
(2)渲染子组件时,外面包裹xxxContext.Provider,通过value属性给后代组件传递数据:
    <xxxContext.Provide value={数据}>
        子组件
    </xxxContext.Provide>
(3)后代组件读取数据:
    //第一种方式:使用类组件
    static conTextType = xxxContext //声明接收context
    this.context //读取context中的value数据
    
    //第二种方式:函数组件与类组件都可以使用(不是特别理解这种写法)
    <xxxContext.Consumer>
        {
            value => { //value就是context中的value数据
                要显示的内容
            }
        }
    </xxxContext.Consumer>

Q44:组件优化

Component的两个问题
  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来优化

Q45:render props

如何向组件内部动态传入带内容的结构(标签)?
Vue中:
    使用slot技术,也就是通过组件标签传入结构 <A><B/></A>
React中:
    使用children props:通过组件标签传入结构
    使用render props:通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
<A>
    <B>xxx</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} 

Q46:错误边界

理解:用来捕获后代组件错误,渲染出备用页面

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

使用方式:

getDerivedStateFromError配合componentDidCatch

//生命周期函数,一旦后台组件出错,就会触发
static getDerivedStateFromError(error){
    return {hasError:true}
}

componentDidCatch(error,info){
    //统计页面的错误,发送请求到后台去
    console.log(error,info)
}

Q47:组件通信方式

组件间的关系
  • 父子组件

  • 兄弟组件(非嵌套组件)

  • 祖孙组件(跨级组件)

几种通信方式
(1)props:
   a.children props
   b.render props
(2)消息订阅与发布
    pubs-sub、event等
(3)集中式管理
    redux、dva
(4)conText
    生产者-消费者模式

比较好的搭配方式

父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发中使用比较少,封装组件使用多)
本文含有隐藏内容,请 开通VIP 后查看