一:React入门

1.1 React介绍

1.1.1 介绍描述

  1. 中文官网: https://react.docschina.org/
  2. 英文官网: https://reactjs.org/
  • 用于动态构建用户界面的 JavaScript 库(只关注于视图)
  • 由Facebook开源

    1.1.2 React特点

  1. 声明式编码
  2. 组件化编码
  3. React Native 编写原生应用
  4. 高效(优秀的Diffing算法)


高效原因: 1. 使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。 2. DOM Diffing算法, 最小化页面重绘。

1.2 React的基本使用

1.2.1相关JS库

  1. react.js:React核心库。
  2. react-dom.js:提供操作DOM的react扩展库
  3. babel.min.js:解析JSX语法代码转为JS代码的库。

    1.2.2 创建虚拟DOM的两种方式

  • 纯JS方式(一般不使用)

    1. <script type="text/javascript" >
    2. //1.创建虚拟DOM
    3. const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
    4. //2.渲染虚拟DOM到页面
    5. ReactDOM.render(VDOM,document.getElementById('test'))
    6. </script>
  • JSX方式

    1. <script type="text/babel" > /* 此处一定要写babel */
    2. //1.创建虚拟DOM
    3. const VDOM = ( /* 此处一定不要写引号,因为不是字符串 */
    4. <h1 id="title">
    5. <span>Hello,React</span>
    6. </h1>
    7. )
    8. //2.渲染虚拟DOM到页面
    9. ReactDOM.render(VDOM,document.getElementById('test'))
    10. </script>

    1.2.3 虚拟DOM与真实DOM

  1. React提供了一些API来创建一种 “特别” 的一般js对象

    const VDOM = React.createElement(‘xx’,{id:’xx’},’xx’) 创建的就是一个简单的虚拟DOM对象

  2. 虚拟DOM对象最终都会被React转换为真实的DOM

  3. 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界 ```javascript //1.创建虚拟DOM const VDOM = (

    Hello,React

    ) //2.渲染虚拟DOM到页面 ReactDOM.render(VDOM,document.getElementById(‘test’))

const TDOM = document.getElementById(‘demo’) console.log(‘虚拟DOM’,VDOM); console.log(‘真实DOM’,TDOM); debugger; // console.log(typeof VDOM); // console.log(VDOM instanceof Object); / 关于虚拟DOM: 1.本质是Object类型的对象(一般对象) 2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。 3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。 /

  1. ---
  2. <a name="kx7Tb"></a>
  3. ## 1.3 React JSX
  4. <a name="rtgPK"></a>
  5. ### 1.3.1 JSX
  6. 1. 全称: JavaScript XML
  7. 1. react定义的一种类似于XMLJS扩展语法: JS + XML本质是React.createElement(component, props, ...children)方法的语法糖
  8. 1. 作用: 用来简化创建虚拟DOM
  9. - 写法:var ele = **<h1>****Hello JSX!****</h1>**
  10. - 注意1:它不是字符串, 也不是HTML/XML标签
  11. - 注意2:它最终产生的就是一个JS对象
  12. 4. 标签名任意: HTML标签或其它标签
  13. 4. 标签属性任意: HTML标签属性或其它
  14. 4. 基本语法规则<br />
  15. > 1.定义虚拟DOM时,不要写引号。
  16. > 2.标签中混入JS表达式时要用{}。<br />3.样式的类名指定不要用class,要用className。<br />4.内联样式,要用style={{key:value}}的形式去写。<br />5.只有一个根标签<br />6.标签必须闭合<br />7.标签首字母<br /> (1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。<br /> (2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
  17. 7. babel.js的作用
  18. - 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
  19. - 只要用了JSX,都要加上type="text/babel", 声明需要babel来处理
  20. <a name="qZGk6"></a>
  21. ### 1.3.2 **渲染虚拟DOM(元素)**
  22. 1. 语法: **ReactDOM****.****render****(****virtualDOM****, ****containerDOM****)**
  23. 1. 作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示
  24. 1. 参数说明
  25. - 参数一: jsjsx创建的虚拟dom对象
  26. - 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
  27. ---
  28. <a name="fmQQ6"></a>
  29. # **二:React面向组件编程**
  30. <a name="FLr7o"></a>
  31. ## **2.1 基本理解和使用**
  32. <a name="CLZDb"></a>
  33. ### 2.1.1 注意
  34. 1. 组件名必须首字母大写
  35. 1. 虚拟DOM元素只能有一个根元素
  36. 1. 虚拟DOM元素必须有结束标签
  37. <a name="piXWk"></a>
  38. ### 2.1.2 渲染类组件标签的基本流程
  39. 1. React内部会创建组件实例对象
  40. 1. 调用render()得到虚拟DOM, 并解析为真实DOM
  41. 1. 插入到指定的页面元素内部
  42. <a name="NXyE7"></a>
  43. ## 2.2 函数组件和类式组件
  44. 函数组件:
  45. > 输出为 undefined
  46. ```javascript
  47. <script type="text/babel">
  48. //1.创建函数式组件
  49. function MyComponent(){
  50. console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
  51. return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
  52. }
  53. //2.渲染组件到页面
  54. ReactDOM.render(<MyComponent/>,document.getElementById('test'))
  55. /*
  56. 执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
  57. 1.React解析组件标签,找到了MyComponent组件。
  58. 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
  59. */
  60. </script>

类式组件:

输出为 render中的this: MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}

  1. <script type="text/babel">
  2. //1.创建类式组件
  3. class MyComponent extends React.Component {
  4. render(){
  5. //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
  6. //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
  7. console.log('render中的this:',this);
  8. return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
  9. }
  10. }
  11. //2.渲染组件到页面
  12. ReactDOM.render(<MyComponent/>,document.getElementById('test'))
  13. /*
  14. 执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
  15. 1.React解析组件标签,找到了MyComponent组件。
  16. 2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
  17. 3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
  18. */
  19. </script>

2.3 组件三大核心属性1: state

2.3.1 理解

  1. state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
  2. 组件被称为”状态机”, 通过更新组件的state来更新对应的页面显示(重新渲染组件)

    2.3.2 强烈注意

  3. 组件中render方法中的this为组件实例对象

  4. 组件自定义的方法中this为undefined,如何解决?
    • 强制绑定this: 通过函数对象的bind()
    • 箭头函数
  5. 状态数据,不能直接修改或更新

    2.3.3 state基本使用

    1. //1.创建组件
    2. class Weather extends React.Component{
    3. //构造器调用几次? ———— 1次
    4. constructor(props){
    5. console.log('constructor');
    6. super(props)
    7. //初始化状态
    8. this.state = {isHot:false,wind:'微风',stat:{}}
    9. //解决changeWeather中this指向问题
    10. this.changeWeather = this.changeWeather.bind(this)
    11. }
    12. //render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
    13. render(){
    14. console.log('render');
    15. //读取状态
    16. const {isHot,wind} = this.state
    17. return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
    18. }
    19. //changeWeather调用几次? ———— 点几次调几次
    20. changeWeather(){
    21. //changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
    22. //由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
    23. //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
    24. console.log('changeWeather');
    25. //获取原来的isHot值
    26. const isHot = this.state.isHot
    27. //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
    28. this.setState({isHot:!isHot})
    29. console.log(this);
    30. //严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
    31. //this.state.isHot = !isHot //这是错误的写法
    32. }
    33. }
    34. //2.渲染组件到页面
    35. ReactDOM.render(<Weather/>,document.getElementById('test'))

    上述state简写方式 (看state就行 其他一致) :
    简写只是把constructor去掉 初始化更加方便

    1. class Weather extends React.Component{
    2. //初始化状态
    3. state = {isHot:false,wind:'微风'}
    4. render(){
    5. const {isHot,wind} = this.state
    6. return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
    7. }
    8. //自定义方法————要用赋值语句的形式+箭头函数
    9. changeWeather = ()=>{
    10. const isHot = this.state.isHot
    11. this.setState({isHot:!isHot})
    12. }
    13. }
    14. //2.渲染组件到页面
    15. ReactDOM.render(<Weather/>,document.getElementById('test'))

    2.4 组件三大核心属性2: props

    2.4.1 理解

  6. 每个组件对象都会有props(properties的简写)属性
    2. 组件标签的所有属性都保存在props中

    2.4.2 作用

  7. 通过标签属性从组件外向组件内传递变化的数据
    2. 注意: 组件内部不要修改props数据

    2.4.3 编码操作

  8. 内部读取某个属性值

    this.props.name

  9. 对props中的属性值进行类型限制和必要性限制

    1. 第一种方式(React v15.5 开始已弃用):
    2. Person.propTypes = {
    3. name: React.PropTypes.string.isRequired,
    4. age: React.PropTypes.number
    5. }
    6. 第二种方式(新):使用prop-types库进限制(需要引入prop-types库):
    7. Person.propTypes = {
    8. name: PropTypes.string.isRequired,
    9. age: PropTypes.number.
    10. }
  10. 扩展属性: 将对象的所有属性通过props传递

  11. 默认属性值
  12. 组件类的构造函数 ```javascript
    1. //创建组件
    2. class Person extends React.Component{

//5.组件类的构造函数 constructor(props){ //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props // console.log(props); super(props) console.log(‘constructor’,this.props); }

//2对标签属性进行类型、必要性的限制 static propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 }

//4指定默认标签属性值 static defaultProps = { sex:’男’,//sex默认值为男 age:18 //age默认值为18 }

  1. render(){

//1内部读取某个属性值 const {name,age,sex} = this.props //props是只读的 //this.props.name = ‘jack’ //此行代码会报错,因为props是只读的 return (

  • 姓名:{name}
  • 性别:{sex}
  • 年龄:{age+1}
) } }

//3扩展属性 //渲染组件到页面 ReactDOM.render(,document.getElementById(‘test1’))

  1. <a name="GtN0U"></a>
  2. ### 2.4.4 函数式组件使用 props
  3. ```javascript
  4. //创建组件
  5. function Person (props){
  6. const {name,age,sex} = props
  7. return (
  8. <ul>
  9. <li>姓名:{name}</li>
  10. <li>性别:{sex}</li>
  11. <li>年龄:{age}</li>
  12. </ul>
  13. )
  14. }
  15. Person.propTypes = {
  16. name:PropTypes.string.isRequired, //限制name必传,且为字符串
  17. sex:PropTypes.string,//限制sex为字符串
  18. age:PropTypes.number,//限制age为数值
  19. }
  20. //指定默认标签属性值
  21. Person.defaultProps = {
  22. sex:'男',//sex默认值为男
  23. age:18 //age默认值为18
  24. }
  25. //渲染组件到页面
  26. ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))

2.4 组件三大核心属性3: refs与事件处理

2.4.2 理解

组件内的标签可以定义ref属性来标识自己 ———— 不太建议过多使用React准备废弃

2.4.3 编码

  1. 字符串形式的ref
    2. 回调形式的ref
    3. createRef创建ref容器· ```javascript //字符串形式的ref const {input1} = this.refs //直接调用

//回调形式的ref

this.input1 = c } type=”text”/> const {input1} = this //调用赋值

//函数方式调用 saveInput = (c)=>{ this.input1 = c; console.log(‘@’,c); }

//createRef创建ref容器· //React.createRef调用后可以返回一个容器,该容器可以存储被 ref所标识的节点,该容器是”专人专用”的 myRef = React.createRef()

  1. <a name="FjIKe"></a>
  2. ### **2.4.4 事件处理**
  3. 1. 通过onXxx属性指定事件处理函数(注意大小写)
  4. - React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ——————— 为了更好的兼容性
  5. - React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)————--为了的高效
  6. 2. 通过event.target得到发生事件的DOM元素对象 ——————————————不要过度使用ref
  7. ```javascript
  8. //事件处理
  9. showData = (event)=>{
  10. console.log(event.target);
  11. alert(this.myRef.current.value);
  12. }
  13. <button onClick={this.showData}>点我提示左侧的数据</button>
  14. //在父子组件中使用
  15. export default class SliderList extends Component {
  16. sliderRef = React.createRef();
  17. componentDidMount(){
  18. this.sliderRef.current.doSlider(); //在父组件中调用
  19. }
  20. render() {
  21. return (
  22. <div className = 'slider-container'>
  23. <Slider ref={this.sliderRef}/>
  24. </div>
  25. );
  26. }
  27. }
  28. //子组件定义方法
  29. export default class Slider extends Component {
  30. doSlider() {
  31. console.log(111111)
  32. }
  33. render() {
  34. return (
  35. <div>Slider</div>
  36. )
  37. }
  38. }

2.5 受控组件和非受控组件

包含表单的组件分类

  1. 受控组件

实际上是Vue的双向绑定

  1. class Login extends React.Component{
  2. //初始化状态
  3. state = {
  4. username:'', //用户名
  5. password:'' //密码
  6. }
  7. //保存用户名到状态中
  8. saveUsername = (event)=>{
  9. this.setState({username:event.target.value})
  10. }
  11. //保存密码到状态中
  12. savePassword = (event)=>{
  13. this.setState({password:event.target.value})
  14. }
  15. //表单提交的回调
  16. handleSubmit = (event)=>{
  17. event.preventDefault() //阻止表单提交
  18. const {username,password} = this.state
  19. alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
  20. }
  21. render(){
  22. return(
  23. <form onSubmit={this.handleSubmit}>
  24. 用户名:<input onChange={this.saveUsername} type="text" name="username"/>
  25. 密码:<input onChange={this.savePassword} type="password" name="password"/>
  26. <button>登录</button>
  27. </form>
  28. )
  29. }
  30. }
  1. 非受控组件

不太建议使用 ref使用较多

  1. class Login extends React.Component{
  2. handleSubmit = (event)=>{
  3. event.preventDefault() //阻止表单提交
  4. const {username,password} = this
  5. alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
  6. }
  7. render(){
  8. return(
  9. <form onSubmit={this.handleSubmit}>
  10. 用户名:<input ref={c => this.username = c} type="text" name="username"/>
  11. 密码:<input ref={c => this.password = c} type="password" name="password"/>
  12. <button>登录</button>
  13. </form>
  14. )
  15. }
  16. }

2.6 高阶函数_函数柯里化

2.6.1 高阶函数的概念:

如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等

2.6.2 函数的柯里化

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

2.6.3 编码操作

  1. function sum(a){
  2. return(b)=>{
  3. return (c)=>{
  4. return a+b+c
  5. }
  6. }
  7. }
  8. //实例 保存表单数据
  9. class Login extends React.Component{
  10. //初始化状态
  11. state = {
  12. username:'', //用户名
  13. password:'' //密码
  14. }
  15. //保存表单数据到状态中
  16. saveFormData = (dataType)=>{
  17. return (event)=>{
  18. this.setState({[dataType]:event.target.value})
  19. }
  20. }
  21. //表单提交的回调
  22. handleSubmit = (event)=>{
  23. event.preventDefault() //阻止表单提交
  24. const {username,password} = this.state
  25. alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
  26. }
  27. render(){
  28. return(
  29. <form onSubmit={this.handleSubmit}>
  30. 用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>
  31. 密码:<input onChange={this.saveFormData('password')} type="password" name="password"/>
  32. <button>登录</button>
  33. </form>
  34. )
  35. }
  36. }

2.7 组件的生命周期

2.7.1 理解

  1. 组件从创建到死亡它会经历一些特定的阶段。
    2. React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
    3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

    生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子

2.7.2 生命周期流程图(旧)

1. 初始化阶段: 由ReactDOM.render()触发—-初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render()
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
图片1.png

2.7.3 生命周期流程图(新)

1. 初始化阶段: 由ReactDOM.render()触发—-初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount() =====> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()====> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
图片2.png
强制刷新:this.forceUpdate() ————跟vue强制刷新一致

  1. class Count extends React.Component{
  2. //构造器
  3. constructor(props){
  4. console.log('Count---constructor');
  5. super(props)
  6. //初始化状态
  7. this.state = {count:0}
  8. }
  9. //加1按钮的回调
  10. add = ()=>{
  11. //获取原状态
  12. const {count} = this.state
  13. //更新状态
  14. this.setState({count:count+1})
  15. }
  16. //卸载组件按钮的回调
  17. death = ()=>{
  18. ReactDOM.unmountComponentAtNode(document.getElementById('test'))
  19. }
  20. //强制更新按钮的回调
  21. force = ()=>{
  22. this.forceUpdate()
  23. }
  24. //若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
  25. static getDerivedStateFromProps(props,state){
  26. console.log('getDerivedStateFromProps',props,state);
  27. return null
  28. }
  29. //在更新之前获取快照
  30. getSnapshotBeforeUpdate(){
  31. console.log('getSnapshotBeforeUpdate');
  32. return 'atguigu'
  33. }
  34. //组件挂载完毕的钩子
  35. componentDidMount(){
  36. console.log('Count---componentDidMount');
  37. }
  38. //组件将要卸载的钩子
  39. componentWillUnmount(){
  40. console.log('Count---componentWillUnmount');
  41. }
  42. //控制组件更新的“阀门”
  43. shouldComponentUpdate(){
  44. console.log('Count---shouldComponentUpdate');
  45. return true
  46. }
  47. //组件更新完毕的钩子
  48. componentDidUpdate(preProps,preState,snapshotValue){
  49. console.log('Count---componentDidUpdate',preProps,preState,snapshotValue);
  50. }
  51. render(){
  52. console.log('Count---render');
  53. const {count} = this.state
  54. return(
  55. <div>
  56. <h2>当前求和为:{count}</h2>
  57. <button onClick={this.add}>点我+1</button>
  58. <button onClick={this.death}>卸载组件</button>
  59. <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
  60. </div>
  61. )
  62. }
  63. }
  64. //渲染组件
  65. ReactDOM.render(<Count count={199}/>,document.getElementById('test'))

2.7.4 重要的勾子(主要使用的函数)

  1. render:初始化渲染或更新渲染调用
    2. componentDidMount:开启监听, 发送ajax请求
    3. componentWillUnmount:做一些收尾工作, 如: 清理定时器

    2.7.5 即将废弃的勾子

  2. componentWillMount
    2. componentWillReceiveProps
    3. componentWillUpdate
    现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

2.8 虚拟DOM与DOM Diffing算法

图片4.png
关于diff算法的问题
1). react/vue中的key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表时,key最好不要用index?

  1. 虚拟DOM中key的作用:
    详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,
    随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
    a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
    (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
    (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
    根据数据创建新的真实DOM,随后渲染到到页面
    2. 用index作为key可能会引发的问题:
    a.若对数据进行:逆序添加、逆序删除等破坏顺序操作:

    会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低

b.如果结构中还包含输入类的DOM:

会产生错误DOM更新 ==> 界面有问题。

c.注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,

仅用于渲染列表用于展示,使用index作为key是没有问题的。

  1. 开发中如何选择key?:
    1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
    2.如果确定只是简单的展示数据,用index也是可以的。

三:React脚手架

3.1 创建项目并启动React项目

第一步,全局安装:
npm i -g create-react-app
第二步,切换到想创项目的目录,使用命令:
create-react-app hello-react
yarn create react-app hello-react
第三步,进入项目文件夹:cd hello-react
第四步,启动项目:
npm start
yarn start

3.2 配置代理

  1. 第一步:创建代理配置文件

在src下创建配置文件:src/setupProxy.js

  1. 编写setupProxy.js配置具体代理规则: ```javascript 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. 1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  3. 1. 缺点:配置繁琐,前端请求资源时必须加前缀。
  4. <a name="VoBJz"></a>
  5. # 四:消息发布订阅
  6. 1. 工具库: PubSubJS
  7. 1. 下载: npm install pubsub-js --save
  8. 1. 使用:
  9. ```javascript
  10. import PubSub from 'pubsub-js' //引入
  11. var token = PubSub.subscribe('delete', function(msg, data){ }); //订阅
  12. PubSub.publish('delete', data) //发布消息
  13. PubSub.unsubscribe(token); //取消订阅

五:React-router

5.1 SPA

  1. React是单页Web应用(single page web application,SPA)。
  2. 整个应用只有一个完整的页面。
  3. 点击页面中的链接不会刷新页面,只会做页面的局部更新。
  4. 数据都需要通过ajax请求获取, 并在前端异步展现。

    5.2 前端路由

    路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。
    在 Web 前端单页应用 SPA中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。

    5.3 前端路由主要有两种实现方案:hash、history

    了解即可 写的不是很清楚

hash 实现
  • hash 是 URL 中 hash (#) 及后面的那部分,常用作锚点在页面内进行导航,改变 URL 中的 hash 部分不会引起页面刷新
  • 通过 hashchange 事件监听 URL 的变化,改变 URL 的方式只有这几种:通过浏览器前进后退改变 URL、通过<a>标签改变 URL、通过window.location改变URL,这几种情况改变 URL 都会触发 hashchange 事件
    hash优缺点:
    优点:优点: 实现简单,兼容性好(兼容到 ie8) 绝大多数前端框架均提供了给予 hash 的路由实现 不需要服务器端进行任何设置和开发 除了资源加载和 ajax 请求以外,不会发起其他请求

缺点: 对于部分需要重定向的操作,后端无法获取 hash 部分内容,导致后台无 法取得 url 中的数据,典型的例子就是微信公众号的 oauth 验证 服务器端无法准确跟踪前端路由信息 对于需要锚点功能的需求会与目前路由机制冲突

history 实现
  • history 提供了 pushState 和 replaceState 两个方法,这两个方法改变 URL 的 path 部分不会引起页面刷新
  • history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或<a>标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便
    history 优缺点
    优点 :对于重定向过程中不会丢失 url 中的参数。后端可以拿到这部分数据。绝大多数前段框架均提供了 history 模式的路由实现。后端可以准确跟踪路由信息 可以使用 history.state 来获取当前 url 对应的状态信息

缺点:兼容性不如 hash 路由(只兼容到 IE10) 需要后端支持,每次返回 html 文档

简单的说两者之前的区别

  1. hash
  2. 只修改#后面内容
  3. 新值不能与旧值相同,一样的不会触发动作将记录添加到栈中
  4. 对服务器无需改动
  5. 即不会发送请求
  6. history
  7. 可以设置同源下任意的URL
  8. 新旧值可以相同,pushSate该添加的会添加到栈中
  9. 刷新时,若服务器没有响应数据或资源,会404。需要对服务器做一些改造,对不同的路由进行相应的设置。
  10. 会向服务器发送请求,避免404服务器应该做处理。当匹配不到资源时,应返回同一个html页面

5.4 react-router-dom

  1. react的一个插件库。
  2. 专门用来实现一个SPA应用。
  3. 基于react的项目基本都会用到此库。

    安装:yarn add react-router-dom

    5.4.1 内置组件—Link

    1. 1.导航区为Link标签
    2. <Link className="list-group-item" to="/about">About</Link>
    3. 2.展示区写Route标签进行路径的匹配
    4. <Route path="/about" component={About}/>
    5. 3.<App>的最外侧包裹了一个<BrowserRouter><HashRouter>
    6. //index.js
    7. ReactDOM.render(
    8. <BrowserRouter>
    9. <App/>
    10. </BrowserRouter>,
    11. document.getElementById('root')
    12. )

    5.4.2 内置组件—NavLink

    跟link一样的写法
    NavLink可以实现路由链接的高亮,通过activeClassName指定样式名 ```jsx

    About

//封装组件
//props中有children children正好是NavLink的属性名显示文字内容

//调用时

About

  1. <a name="6hMga"></a>
  2. ### 5.4.3 内置组件--switch
  3. 1.通常情况下,path和component是一一对应的关系。<br /> 2.Switch可以提高路由匹配效率(单一匹配)。 找到之后不再继续找寻
  4. <a name="rDxdn"></a>
  5. ##### 未使用Switch包裹注册路由:
  6. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/21445020/1627350403814-6de83f18-805a-4327-871e-2ebd17f0f3dd.png#align=left&display=inline&height=189&margin=%5Bobject%20Object%5D&name=image.png&originHeight=189&originWidth=483&size=8161&status=done&style=none&width=483)
  7. <a name="a8fBd"></a>
  8. ##### 具体编码
  9. ```jsx
  10. {/* 注册路由 */}
  11. 用Switch包裹之后仅出现Home组件
  12. <Switch>
  13. <Route path="/about" component={About}/>
  14. <Route path="/home" component={Home}/>
  15. <Route path="/home" component={Test}/>
  16. </Switch>

5.4.4 内置组件Redirect

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由(重定向)

具体编码
  1. <Switch>
  2. <Route path="/about" component={About}/>
  3. <Route path="/home" component={Home}/>
  4. <Redirect to="/about"/>
  5. </Switch>

5.5 解决多级路径刷新页面样式丢失的问题

  1. public/index.html 中 引入样式时不写 ./ 写 / (常用)
  2. public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
  3. 使用HashRouter (不建议使用)

    1. <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    2. <link rel="stylesheet" href="/css/bootstrap.css">
    3. ReactDOM.render(
    4. <HashRouter>
    5. <App/>
    6. </HashRouter>,
    7. document.getElementById('root')
    8. )

    5.6 路由的严格匹配与模糊匹配

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

  5. 开启严格匹配:

<Route exact={true} path="/about" component={About}/> 或者简写
<Route exact path="/about" component={About}/> _

  1. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

    5.7 嵌套路由

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

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

    1. //子路由的写法
    2. <Switch>
    3. <Route path="/home/news" component={News}/>
    4. <Route path="/home/message" component={Message}/>
    5. <Redirect to="/home/news"/>
    6. </Switch>

    5.8 路由组件传递参数

  4. params参数

路由链接(携带参数): 详情
注册路由(声明接收):
接收参数:this.props.match.params

  1. search参数

    路由链接(携带参数):详情
    注册路由(无需声明,正常注册即可):
    接收参数:this.props.location.search
    备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

  2. state参数

    路由链接(携带参数):详情
    注册路由(无需声明,正常注册即可):
    接收参数:this.props.location.state
    备注:刷新也可以保留住参数

    具体编码:

    ```jsx messageArr.map((msgObj)=>{ return (

  3. {/ 向路由组件传递params参数 /}

    {msgObj.title}

    {/ 向路由组件传递search参数 /}

    {msgObj.title}

    {/ 向路由组件传递state参数 /} {msgObj.title}

  4. ) })

{/ 声明接收params参数 /}

{/ search参数无需声明接收,正常注册路由即可 /}

{/ state参数无需声明接收,正常注册路由即可 /}

子组件 import qs from ‘querystring’ export default class Detail extends Component { render() { // 接收params参数 const {id,title} = this.props.match.params

  1. // 接收search参数
  2. const {search} = this.props.location
  3. const {id,title} = qs.parse(search.slice(1))
  4. // 接收state参数
  5. const {id,title} = this.props.location.state || {}
  6. const findResult = DetailData.find((detailObj)=>{
  7. return detailObj.id === id
  8. }) || {}
  9. return (
  10. <ul>
  11. <li>ID:{id}</li>
  12. <li>TITLE:{title}</li>
  13. <li>CONTENT:{findResult.content}</li>
  14. </ul>
  15. )
  16. }

}

  1. <a name="UqqW7"></a>
  2. ## 5.9 编程式路由导航
  3. - push是往 history 里面增加一层堆栈,可以返回上一层
  4. - replace跳转不会形成history,不可返回到上一层。
  5. ```javascript
  6. //借助this.prosp.history对象上的API对操作路由跳转、前进、后退
  7. this.prosp.history.push() //push查看
  8. //push跳转+携带params参数
  9. this.props.history.push(`/home/message/detail/${id}/${title}`)
  10. //push跳转+携带search参数
  11. this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
  12. //push跳转+携带state参数
  13. this.props.history.push(`/home/message/detail`,{id,title})
  14. this.prosp.history.replace() //replace查看
  15. //replace跳转+携带params参数
  16. this.props.history.replace(`/home/message/detail/${id}/${title}`)
  17. //replace跳转+携带search参数
  18. this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
  19. //replace跳转+携带state参数
  20. this.props.history.replace(`/home/message/detail`,{id,title})
  21. this.props.history.goBack() //回退
  22. this.props.history.goForward() //前进
  23. this.props.history.go(n) //负为退 正为进

5.10 BrowserRouter与HashRouter的区别

  1. 1.底层原理不一样:
  2. BrowserRouter使用的是H5history API,不兼容IE9及以下版本。
  3. HashRouter使用的是URL的哈希值。
  4. 2.path表现形式不一样
  5. BrowserRouter的路径中没有#,
  6. 例如:localhost:3000/demo/test
  7. HashRouter的路径包含#,
  8. 例如:localhost:3000/#/demo/test
  9. 3.刷新后对路由state参数的影响
  10. (1).BrowserRouter没有任何影响,因为state保存在history对象中。
  11. (2).HashRouter刷新后会导致路由state参数的丢失!!!
  12. 4.备注:HashRouter可以用于解决一些路径错误相关的问题。

六:redux

6.1 基本理解

6.1.1 学习文档

  1. redux是一个专门用于做管理状态的JS库 (不是react插件库)
  2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
  3. 作用: 集中式管理react应用中多个组件的状态。

    6.1.3. 什么情况下需要使用redux

    1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
      2. 一个组件需要改变另一个组件的状态(通信)。
      3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

      6.1.4 工作流程

      图片1.png

      6.2 redux的三个核心概念

      6.2.1 action

  1. 动作的对象
  2. 包含两个属性
    • type:标识属性, 值为字符串, 唯一, 必要属性
    • data:数据属性, 值类型任意, 可选属性
  3. 编码:

    1. //该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止单词写错
    2. import {ADD_PERSON} from '../constant'
    3. //专门为组件生成action对象
    4. export const addPerson = data => ({type:ADD_PERSON,data})

    6.2.2 reducer

  4. 用于初始化状态、加工状态。

  5. 加工时,根据旧的state和action, 产生新的state的纯函数。

    1. const initState = xx
    2. export default function xxxReducer(preState =initState, action) {
    3. const {type ,data} = action
    4. switch (type) {
    5. case JIA:
    6. return preState+1
    7. default :
    8. return preState
    9. }
    10. }

    6.2.3 store

  6. 将state、action、reducer联系在一起的对象

  7. 如何得到此对象?
    1. //引入createStore,专门用于创建redux中最为核心的store对象
    2. import {createStore} from 'redux'
    3. //引入为Count组件服务的reducer
    4. import countReducer from './count_reducer'
    5. //暴露store
    6. export default createStore(countReducer)
  8. 此对象的功能?
  • getState(): 得到state
  • dispatch(action): 分发action, 触发reducer调用, 产生新的state
  • subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
    1. componentDidMount(){
    2. //检测redux中状态的变化,只要变化,就调用render
    3. store.subscribe(()=>{
    4. this.setState({})
    5. })
    6. }
    7. store.dispatch(createIncrementAction(value*1))
    8. store.getState()

    6.3 redux核心api

    6.3.1 createStore

    作用:创建包含指定reducer的store对象

    6.3.2 store对象

  1. 作用: redux库最核心的管理对象
  2. 它内部维护着:
    1. state
    2. reducer
  3. 核心方法:
    1. getState()
    2. dispatch(action)
    3. subscribe(listener)
  4. 具体编码:
    1. store.getState()
    2. store.dispatch({type:’INCREMENT’, number})
    3. store.subscribe(render)

      一般这样写 假更新一下 store.subscribe(()=>{
      _this.setState
      ({})
      })_

6.3.3 applyMiddleware()

作用:应用上基于redux的中间件(插件库)

6.3.4 combineReducers()

作用:合并多个reducer函数

6.4 redux异步

  1. redux默认是不能进行异步处理的,
    2. 某些时候应用中需要在redux**中执行异步任务**(ajax, 定时器)
    必须下载插件:
    npm install —save redux-thunk

    //引入redux-thunk,用于支持异步action import thunk from ‘redux-thunk’

6.5 react-redux (关键)

6.5.1 理解

  1. 一个专门的react插件
  2. 专门简化react应用中使用redux

    6.5.2 react-redux组件拆分两大类

    UI**组件**

    • 只负责 UI 的呈现,不带有任何业务逻辑
    • 通过props接收数据(一般数据和函数)
    • 不使用任何 Redux 的 API
    • 一般保存在components文件夹下

容器组件

  • 负责管理数据和业务逻辑,不负责UI的呈现
  • 使用 Redux 的 API
  • 一般保存在containers文件夹下

    6.5.3 相关API

  1. Provider:让所有组件都可以得到state数据

    1. import {Provider} from 'react-redux'
    2. <Provider store={store}>
    3. <App/>
    4. </Provider>
  2. connect:用于包装 UI 组件生成容器组件

  3. mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
  4. mapDispatchToProps:将分发action的函数转换为UI组件的标签属性 ```javascript //引入Count的UI组件 import CountUI from ‘../../components/Count’ //引入action import { createIncrementAction, } from ‘../../redux/count_action’

//引入connect用于连接UI组件与redux import {connect} from ‘react-redux’

/ 1.mapStateToProps函数返回的是一个对象; 2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value 3.mapStateToProps用于传递状态 / function mapStateToProps(state){ return {count:state} }

/ 1.mapDispatchToProps函数返回的是一个对象; 2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value 3.mapDispatchToProps用于传递操作状态的方法 / function mapDispatchToProps(dispatch){ return { jia:number => dispatch(createIncrementAction(number)) } }

//使用connect()()创建并暴露一个Count的容器组件 export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

//简写 export default connect( state => ({count:state}), {jia:createIncrementAction} )(Count)

  1. store.js
  2. ```javascript
  3. /*
  4. 该文件专门用于暴露一个store对象,整个应用只有一个store对象
  5. */
  6. //引入createStore,专门用于创建redux中最为核心的store对象
  7. import {createStore,applyMiddleware} from 'redux'
  8. //引入汇总之后的reducer
  9. import reducer from './reducers'
  10. //引入redux-thunk,用于支持异步action
  11. import thunk from 'redux-thunk'
  12. //引入redux-devtools-extension
  13. import {composeWithDevTools} from 'redux-devtools-extension'
  14. //暴露store
  15. export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))

6.6 纯函数和高阶函数

6.6.1 纯函数

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

    6.6.2 高阶函数

  2. 理解: 一类特别的函数
    1) 情况1: 参数是函数
    2) 情况2: 返回是函数
    2. 常见的高阶函数:
    1) 定时器设置函数
    2) 数组的forEach()/map()/filter()/reduce()/find()/bind()
    3) promise
    4) react-redux中的connect函数
    3. 作用: 能实现更加动态, 更加可扩展的功能

七:setState

(1). setState(stateChange, [callback])———对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用

(2). setState(updater, [callback])———函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取

  1. //对象式的setState
  2. //1.获取原来的count值
  3. const {count} = this.state
  4. //2.更新状态
  5. this.setState({count:count+1},()=>{
  6. console.log(this.state.count);
  7. })
  8. //console.log('12行的输出',this.state.count); //0
  9. //函数式的setState
  10. this.setState( (state,props) => ({count:state.count+1}),() =>{
  11. console.log(state.count)
  12. })

八:lazyLoad 路由懒加载

  1. //1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
  2. const Login = lazy(()=>import('@/pages/Login'))
  3. //2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
  4. fallback包裹一个一般组件也可以
  5. <Suspense fallback={<h1>loading.....</h1>}>
  6. <Switch>
  7. <Route path="/xxx" component={Xxxx}/>
  8. <Redirect to="/login"/>
  9. </Switch>
  10. </Suspense>

九: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). 语法和说明:

    1. useEffect(() => {
    2. // 在此可以执行任何带副作用操作
    3. return () => { // 在组件卸载前执行
    4. // 在此做一些收尾工作, 比如清除定时器/取消订阅等
    5. }
    6. }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

    (4). 可以把 useEffect Hook 看做如下三个函数的组合
    componentDidMount()
    componentDidUpdate()
    componentWillUnmount()

  5. Ref Hook
    (1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
    (2). 语法: const refContainer = useRef()
    (3). 作用:保存标签对象,功能与React.createRef()一样
    _

    1. function Demo(){
    2. const [count,setCount] = React.useState(0)
    3. const myRef = React.useRef()
    4. React.useEffect(()=>{
    5. let timer = setInterval(()=>{
    6. setCount(count => count+1 )
    7. },1000)
    8. return ()=>{
    9. clearInterval(timer)
    10. }
    11. },[])
    12. //加的回调
    13. function add(){
    14. //setCount(count+1) //第一种写法
    15. setCount(count => count+1 )
    16. }
    17. //提示输入的回调
    18. function show(){
    19. alert(myRef.current.value)
    20. }
    21. //卸载组件的回调
    22. function unmount(){
    23. ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    24. }
    25. return (
    26. <div>
    27. <input type="text" ref={myRef}/>
    28. <h2>当前求和为:{count}</h2>
    29. <button onClick={add}>点我+1</button>
    30. <button onClick={unmount}>卸载组件</button>
    31. <button onClick={show}>点我提示数据</button>
    32. </div>
    33. )
    34. }

    十:Fragment

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

    1. <Fragment key={1}> //能参与遍历
    2. <input type="text"/>
    3. <input type="text"/>
    4. </Fragment>
    5. 或者
    6. <>
    7. <input type="text"/>
    8. <input type="text"/>
    9. </>

    十一:Context

    一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
    1) 创建Context容器对象:
    const XxxContext = React.createContext()

    2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

    子组件


    3) 后代组件读取数据:
    //第一种方式:仅适用于类组件
    static contextType = xxxContext // 声明接收context
    this.context // 读取context中的value数据
    //第二种方式: 函数组件与类组件都可以

    {
    value => ( // value就是context中的value数据
    要显示的内容
    )
    }

    注意:在应用开发中一般不用context, 一般都它的封装react插件 ```jsx //创建Context对象 const MyContext = React.createContext() const {Provider,Consumer} = MyContext export default class A extends Component {

    state = {username:’tom’,age:18} render() {

    1. const {username,age} = this.state
    2. return (
    3. <div className="parent">
    4. <h3>我是A组件</h3>
    5. <h4>我的用户名是:{username}</h4>
    6. <Provider value={{username,age}}>
    7. <B/>
    8. </Provider>
    9. </div>
    10. )

    } }

class B extends Component { render() { return (

我是B组件

) } }

/ class C extends Component { //声明接收context static contextType = MyContext render() { const {username,age} = this.context return (

我是C组件

我从A组件接收到的用户名:{username},年龄是{age}

) } } /

function C(){ return (

我是C组件

我从A组件接收到的用户名: {value => ${value.username},年龄是${value.age}}

) }

  1. <a name="336f69b9"></a>
  2. # 十二:组件优化 Component
  3. 项目的2个问题 <br />1. 只要执行setState(),即使不改变状态数据, 组件也会重新render()<br />2. 只当前组件重新render(), 就会自动重新render子组件 ==> 效率低<br />效率高的做法:<br />只有当组件的state或props数据发生改变时才重新render()<br />原因:<br />Component中的shouldComponentUpdate()总是返回true<br />解决:<br /> 办法1: <br /> 重写shouldComponentUpdate()方法<br /> 比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false<br /> 办法2: <br /> 使用PureComponent<br /> PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
  4. 注意: <br /> 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false <br /> 不要直接修改state数据, 而是要产生新数据<br /> 项目中一般使用PureComponent来优化
  5. ```jsx
  6. import React, { PureComponent } from 'react'
  7. export default class Parent extends PureComponent {
  8. state = {carName:"奔驰c36",stus:['小张','小李','小王']}
  9. addStu = ()=>{
  10. const {stus} = this.state
  11. this.setState({stus:['小刘',...stus]})
  12. }
  13. changeCar = ()=>{
  14. this.setState({carName:'迈巴赫'})
  15. }
  16. /* shouldComponentUpdate(nextProps,nextState){
  17. // console.log(this.props,this.state); //目前的props和state
  18. // console.log(nextProps,nextState); //接下要变化的目标props,目标state
  19. return !this.state.carName === nextState.carName
  20. } */
  21. render() {
  22. console.log('Parent---render');
  23. const {carName} = this.state
  24. return (
  25. <div className="parent">
  26. <h3>我是Parent组件</h3>
  27. {this.state.stus}&nbsp;
  28. <span>我的车名字是:{carName}</span><br/>
  29. <button onClick={this.changeCar}>点我换车</button>
  30. <button onClick={this.addStu}>添加一个小刘</button>
  31. <Child carName="奥拓"/>
  32. </div>
  33. )
  34. }
  35. }
  36. class Child extends PureComponent {
  37. /* shouldComponentUpdate(nextProps,nextState){
  38. console.log(this.props,this.state); //目前的props和state
  39. console.log(nextProps,nextState); //接下要变化的目标props,目标state
  40. return !this.props.carName === nextProps.carName
  41. } */
  42. render() {
  43. console.log('Child---render');
  44. return (
  45. <div className="child">
  46. <h3>我是Child组件</h3>
  47. <span>我接到的车是:{this.props.carName}</span>
  48. </div>
  49. )
  50. }
  51. }

十三:render props

如何向组件内部动态传入带内容的结构(标签)?
Vue中:
使用slot技术, 也就是通过组件标签体传入结构
React中:
使用children props: 通过组件标签体传入结构
使用render props: 通过组件标签属性传入结构, 一般用render函数属性
children props

xxxx

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

十四:错误边界

理解:
错误边界:用来捕获后代组件错误,渲染出备用页面
特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:

  1. getDerivedStateFromError配合componentDidCatch
  2. // 生命周期函数,一旦后台组件报错,就会触发
  3. //当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
  4. static getDerivedStateFromError(error){
  5. console.log('@@@',error);
  6. return {hasError:error}
  7. }
  8. componentDidCatch(){
  9. console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
  10. }