React学习笔记

一、React入门

1.1.1、官网

1)英文官网:https://reactjs.org/t

2)中文官网: React 官方中文文档 – 用于构建用户界面的 JavaScript 库

1.1.2、介绍

React 是一个用于构建用户界面的JavaScript库 用户界面:HTML页面(前端) React主要用来写HTML页面,或构建Web应用 如果从MVC的角度来看,React仅仅是视图层(V),也就是只负责视图的渲染,而并非提供了完整的M和C的功能 React 起源于Facebook的内部项目,后又用来架设Instagram的网站,并于2013年5月开源

1.1.3、React特点

1)声明式编码

2)组件化编码

3)React Native编写原生应用

4)高效(优秀的Diffing算法)

1.1.4、高效之处

1)它使用虚拟(virtual)DOM,不总是直接操作页面的真实DOM

2)DOM Diffing算法,最小化页面重绘

1.2、React的基本使用

1.2.1、创建虚拟DOM

const vdom = <h1>hello,react</h1>; //创建
ReactDOM.render(vdom, document.getElementById('app')(容器)); //渲染
const vdom = React.createElement('h1', { id: 'title' }, 'hello,react'); //创建(了解即可)
ReactDOM.render(vdom, document.getElementById('app')(容器)); //渲染

关于虚拟dom:

1、本质是object类型的对象

2、虚拟dom方法少,真实dom方法多

3、虚拟dom会被react转换为真实dom

1.3、React JSX

1.3.1、JSX语法规则

const ids = 'llll';
const ele = (
  <h2 className='dddd' id={ids}>
    555
    <span style={{ color: 'white' }}></span>
  </h2>
);
ReactDOM.render(vdom, document.getElementById('app')(容器)); //渲染

1、定义虚拟dom时不用引号

2、标签中使用js表达式时使用{}

3、样式的类名要用className

4、内联样式要用style={{color:“white”}}

5、虚拟dom只有一个根标签

6、标签必须闭合

7、标签首字母

1)若为小写则转化为html中同名元素,其余则报错

2)若为大写则渲染对应组件,未定义则报错

1.4、模块与组件、模块化与组件化的理解

1.4.1、模块

1)理解:向外提供特定功能的js程序,一般就是一个js文件 2)为什么号拆成快:阳有业务逻排增加,代码越来越多且复杂 3)作用:复用js,简化js的编写,提高js运行效率

1.4.2、组件

1)理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等) 2)为什么:一个界面的功能更复杂 3)作用:复用编码,简化项目编码,提高运行效率名

1.4.3、模块化

当应用的js 都以换块来编写的,这个应用就是一个模块化的应用·

1.4.4、组件化

当应用是以多组件的方式实现,这个应用就是一个维件化的应用

二、React面向组件编程

2.1、创建组件两种方式

2.1.1函数式组件:

function Demo() {
  return <h1>44555555</h1>;
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.1.2类式组件:

class Demo extends React.Component {
  render() {
    return <h2>5555555555</h2>;
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.2、state

2.2.1、state写法

class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      iss: true,
    };
  }
  render() {
    return <h2>{this.state.iss ? '男' : '女'}</h2>;
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.2.2、绑定事件

class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      iss: true,
    };
    //解决this指向问题
    this.demo = this.demo.bind(this);
  }
  demo() {
    //demo放在哪里?Weather的原型对象上,供实例使用
    //由于demo是作为onClick的回调,所以不是通过实例调用的,是直接调用
    //类中的方法默认开启了局部的严格模式、所以demo中的this为undefined
    console.log(this);
  }
  render() {
    return <h2 onClick={this.demo}>{this.state.iss ? '男' : '女'}</h2>;
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.2.3、修改state数据

使用setstateAPI更新state里面的数据,执行合并操作

constructor:仅仅调用一次

render:1+n次,1:初始化,n:点击的次数

标准语法如下:

class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      iss: true,
    };
    //解决this指向问题
    this.demo = this.demo.bind(this);
  }
  demo() {
    //demo放在哪里?Weather的原型对象上,供实例使用
    //由于demo是作为onClick的回调,所以不是通过实例调用的,是直接调用
    //类中的方法默认开启了局部的严格模式、所以demo中的this为undefined
    this.setstate({ iss: !this.state.iss }); //调用setstateAPI
  }
  render() {
    return <h2 onClick={this.demo}>{this.state.iss ? '男' : '女'}</h2>;
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

简写语法:

class Demo extends React.Component {
  state = {
    iss: true,
  };
  demo = () => {
    this.setstate({ iss: !this.state.iss }); //调用setstateAPI
  };
  render() {
    return <h2 onClick={this.demo}>{this.state.iss ? '男' : '女'}</h2>;
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.3、Props

2.3.1、props基本使用

class Demo extends React.Component {
  render() {
    return <h2>{this.props.name}</h2>;
  }
}
ReactDOM.render(<Demo name='tom' />, document.getElementById('app')); //渲染
ReactDOM.render(<Demo name='John' />, document.getElementById('app'));

2.3.2、批量传值

class Demo extends React.Component {
  render() {
    return <h2>{this.props.name}</h2>;
  }
}
const k = { name: 'lll', age: '18', height: '185' };
ReactDOM.render(<Demo {...k} />, document.getElementById('app')); //渲染

2.3.3、传值限制

15.5版本已经弃用

class Demo extends React.Component {
  render() {
    return <h2>{this.props.name}</h2>;
  }
  static propTypes = {
    name: React.PropTypes.string,
  };
}
ReactDOM.render(<Demo name='tom' />, document.getElementById('app')); //渲染

16版本以上:

npm install prop-types//安装
import PropType from 'prop-types';
class Demo extends React.Component {
  render() {
    return <h2>{this.props.name}</h2>;
  }
  static propTypes = {
    name: PropTypes.string.isrequired,
    age: PropTypes.string,
  };
  static defaultProps = {
    sex: '男',
  };
}
ReactDOM.render(<Demo name='tom' />, document.getElementById('app')); //渲染

props:只读不可修改

2.3.4、函数式组件props

function Demo(props) {
  return <h2>{props.name}</h2>;
}
ReactDOM.render(<Demo name='tom' />, document.getElementById('app')); //渲染

props总结:

1、批量传输使用{...p}

2、传值限制给组件加propsTypes或者defaultProps

3、props不可修改

2.5、refs

2.5.1、字符串形式的ref(避免使用)

如果你之前使用过 React,你可能了解过之前的 API 中的 string 类型的 ref 属性,例如 "textInput"。你可以通过 this.refs.textInput 来访问 DOM 节点。我们不建议使用它,因为 string 类型的 refs 存在 一些问题。它已过时并可能会在未来的版本被移除。

class Demo extends React.Component {
  click = () => {
    console.log(this.refs.inp);
  };
  render() {
    return <h2 ref='inp'>{this.props.name}</h2>;
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.5.2、回调形式的ref

从this中取数据,因为箭头函数中的this指向Demo。

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

内联写法:

class Demo extends React.Component {
  click = () => {
    console.log(this.h2);
  };
  render() {
    return (
      <h2
        ref={(e) => {
          this.h2 = e;
        }}
      >
        {this.props.name}
      </h2>
    ); //e为当前节点:h2
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

h2:是当前ref的名字

解决上述问题:通过class的绑定函数的方式(无关紧要的知道即可)

class Demo extends React.Component {
  click = () => {
    console.log(this.h2);
  };
  h2s = (c) => {
    console.log(c);
  };
  render() {
    return (
      <h2 onClick={this.click} ref={this.h2s}>
        {this.props.name}
      </h2>
    );
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.5.3、createRefAPI

注:createRef专人专用,如果有多个可创建多个

class Demo extends React.Component {
  myref = React.createRef();
  h2s = (c) => {
    console.log(this.myref.current);
  };
  render() {
    return (
      <h2 onClick={this.click} ref={this.myref}>
        {this.props.name}
      </h2>
    );
  }
}
ReactDOM.render(<Demo />, document.getElementById('app')); //渲染

2.5.4、ref总结

1、字符串ref,简单,已使用,非条件不允许情况下尽量避免

2、回调ref,绑定class方式:繁杂,需单独写方法,内联写法简单

3、使用createRef API最为复杂的一种也是react最为推荐的一种

2.5.5、事件处理

(1).通过onXxx属性指定事件处理函数(注意大小写)-------onclick--onClick a.React使用的是自定义(合成)事件,而不是使用的原生DOM事件------为了更好的兼容性 b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)--------为了高效 (2).通过event,target得到发生事件的DOM元素对象----------不要过度使用Ref

2.6、表单数据

2.6.1、非受控组件

现用现取:什么时候需要什么使用获取

class Test extends React.Component {
  handlesubmit = (e) => {
    e.preventDefault(); //组织默认事件
    console.log(this.username.value);
  };
  render() {
    return (
      <form onSubmit={this.handlesubmit}>
        <input ref={(c) => (this.username = c)} name='username' type='text' />
      </form>
    );
  }
}

2.6.2、受控组件

自动获取:不需要你调用自己获取

class Test extends React.Component {
  state = {
    username: '',
  };
  handlesubmit = (e) => {
    e.preventDefault(); //组织默认事件
    console.log(this.username.value);
  };
  changeinput = (e) => {
    console.log(e.target);
    this.setState({
      username: e.target.value,
    });
  };
  render() {
    return (
      <form onSubmit={this.handlesubmit}>
        <input onChange={changeinput} ref={(c) => (this.username = c)} name='username' type='text' />
      </form>
    );
  }
}

2.7、高阶函数和函数的柯里化

2.7.1、高阶函数

若函数的参数是一个函数,那么该函数是一个高阶函数

若函数的返回值是一个函数,那么该函数是一个高阶函数

class Test extends React.Component {
  state = {
    username: '',
    password: '',
  };
  handlesubmit = (e) => {
    e.preventDefault(); //组织默认事件
    console.log(this.state.username);
    console.log(this.state.password);
  };
  changeinput = (inpType) => {
    return (e) => {
      this.setState({
        [inpType]: e.target.value,
      });
    };
  };
  render() {
    return (
      <form onSubmit={this.handlesubmit}>
        <input onChange={this.changeinput('username')} name='username' type='text' />
        //此处将函数的返回值作为change的回调函数
        <input onChange={this.changeinput('password')} name='password' type='text' />
      </form>
    );
  }
}

2.7.2、函数的柯里化

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

function sum(a) {
  return (b) => {
    return (c) => {
      return a + b + c;
    };
  };
}
sum(1)(2)(3); //6

高阶函数中的changeinput就是利用了函数的柯里化

2.8、组件的生命周期

2.8.1、componentWillMount(){}

页面将要挂载时调用{}

class Test extends React.Component{
  componentWillMount() {}
}

2.8.2、render(){}

常用渲染,用于将组件挂载到页面

class Test extends React.Component{
  render() {}
}

2.8.3、componentDidMount(){}

页面挂载完毕时调用{}

class Test extends React.Component{
  componentDidMount() {}
}

2.8.4、componentWillUnmount(){}

页面即将卸载的时候调用{}

class Test extends React.Component{
  componentWillUnmount() {}
}

2.8.5、shouldComponentUpdate(){}

控制组件更新的阀门:默认是打开(true),false关闭,调用setState()时会调用shouldComponentUpdate(){}

class Test extends React.Component{
  shouldComponentUpdate() {}
}

2.8.6、componentWillUpdate(){}

组件将要更新时调用

class Test extends React.Component {
  componentWillUpdate() {}
}

2.8.7、componentDidUpdate(){}

组件更新完毕时调用

三个参数

1、prevProps:更新完成前上一次传值的内容

2、prevState:更新完成前上一次state的内容

3、snapshotValue:拿到getSnapshotBeforeUpdate的返回值内容

class Test extends React.Component {
  componentDidUpdate() {}
}

forceUpdate()强制更新

class Test extends React.Component {
  test = () => {
    this.forceUpdate();
  };
}

注:强制更新时不走shouldComponentUpdate!!!

2.8.8、componentWillReceiveProps(){}

父组件给子组件传值时调用(第一次不算)

class Test extends React.Component {
  componentWillReceiveProps() {}
}

2.8.9、总结React旧生命周期

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

        1.constructor()

        2,componentWillMount()

        3.render()

        4.componentDidMount()

2,更新阶段:由组件内部this.setSate()或父组件重新render触发

        1.shouldComponehtUpdate()

        2.componentwillUpdate()

        3.render()

        4.componentDidUpdate()

3,卸载组件:由ReactDON.unmountComponentAtNode()触发

        1.componentWi11Unmount()

2.8.10、react18版本新生命周期

在旧的生命周期基础上进行修改,修改如下(不推荐使用,未来可能废弃)

UNSAFE_componentWillMount()

UNSAFE_componentWillReceiveProps()

UNSAFE_componentwillUpdate()

用法不变只是将名字修改

增添了两个生命周其函数

1)getDerivedStateFromProps(){}

必须有return :可以是null和状态对象

可以接收值,接收props和state值

props:组件接收的值

state:组件内state值

用法:state的值在任何时候都取决于props

class Test extends React.Component {
  static getDerivedStateFromProps(props, state) {
    return null;
  }
}

2)getSnapshotBeforeUpdate(){}

必须有return :可以是null和snapshotValue对象

class Test extends React.Component {
  getSnapshotBeforeUpdate() {
    return null;
  }
}

小案例

class Test extends React.Component {
  state = { newsArr: [] };
  componentDidMount() {
    setInterval(() => {
      //创建定时器每一秒增加一条新闻
      let news = '新闻' + (this.state.newsArr.length + 1);
      this.setState({ newsArr: [news, ...newsArr] });
    }, 1000);
  }
  getSnapshotBeforeUpdate() {
    //获取更新前的元素高度
    return this.list.scrollHeight;
  }
  componentDidUpdate(prevprops, preostate, prevheight) {
    //接收到更新后的高度
    this.list.scrollTop += this.list.scrollHeight - prevherght; //获取差值,回调形式的ref从当前实例拿值
  }
  render() {
    return (
      <div
        className='list'
        ref={(c) => {
          this.list = c;
        }}
      >
        //回调形式的ref
        {this.state.newsArr.map((n, index) => {
          return <div key={index}>{n}</div>;
        })}
      </div>
    );
  }
}

2.8.11、总结新生命周期

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.9、Diffing算法

算法对比的最小力度是标签

1.在遍历过程中,key的作用

1)简单的说:是虚拟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,随后渲染到到页山 2.用index作为key可能会引发的问题

        1,若对数据进行:逆序添加、逆序删除等破坏顺序操作会产生没有必要的真实DOM更新==〉界面效果没问题,但效率低(注意看)

        2。如果结构中还包含输入类的DOM:会产生错误DOM更新==〉界面有问题

        3,注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

3.开发过程中如何选择值

        1、最好选择每条数据的唯一值做标识,作为key

        2、如果只是简单的展示数据,用index也是没问题的

三、React脚手架

3.1、使用脚手架

3.1.1、create-react-app创建应用

1)全局安装:npm install -g create-react-app

2)创建项目:create-react-app my-item

3)进入项目:cd my-item

4)启动项目:npm start

3.1.2、文件解释

public---静态资源文件夹

faviconicon --网站页签图标

index.html --主页面:

logo192.png -- logo图

logo512.png --- logo图

manifestjson --应用加壳的配置文件

robots.txt -----爬虫协议文件·

src---源码文件夹

App.css-App 组件的样式

App.js-App 组件 App

.testjs -用于给App做测试

index.css-样式

indexjs --入口文件

logo.svg -----logo图

reportWebVitals.js--页面性能分析文件(需要 web-vitals库的支持)

setupTests.js----组件单元测试的文件(需要jest-dom库的支持)”

3.2、index.js书写步骤

1)引入react核心库:import React from 'react'

2)引入ReactDOM:import ReactDOM from 'react-dom'

3)引入App组件:import App from './App'

4)渲染App组件:ReactDOM.render(<App/>,document.getElementById('root'))

3.3、App.js书写步骤

创建外壳组件:import React from 'react'

创建组件:

class App extends React.Component {
  render() {
    return <div></div>;
  }
}
export default App;

3.4、vscode快速编写插件

ES7 React/Redux/GraphQL/React-Native snippets(复制搜索即可)

rcc:一键创建类组件

import React, { Component } from 'react';
​
export default class FileName extends Component {
  render() {
    return <div>$2</div>;
  }
}

rfc:一键创建函数组件

import React from 'react';
​
export default function $1() {
  return <div>$0</div>;
}

3.5、功能界面的组件化编码流程

1.拆分组件:拆分界面,抽取组件 2.实现静态组件:使用组件实现静态页面效果 3.实现动态组件 3.1动态显示初始化数据 3.1.1数据类型 3.1.2数据名称 3.1.2保存在哪个组件? 3.2交互(从绑定事件监听开始)

3.6、子项父传值

子组件:

class B extends React.Component {
  changeValue = (e) => {
    this.props.increase(e.targrt.value);
  };
  render() {
    return <input onBlur={this.changeValue} />;
  }
}

父组件:

class A extends React.Component {
  increase = (data) => {
    console.log(data); //传过来的数据
  };
  render() {
    return <B increase={this.increase} />;
  }
}

3.7、服务器代理:

第一种:package.json文件修改,在最后添加下面代码

{
  "proxy":"http://localhost:5000"
}
import axios from 'axios';
export default class A extends React.Component {
  test = () => {
    axios.get('http://localhost:3000/student').then(
      //注意端口号是3000并不是5000
      (response) => {
        console.log(response.data);
      },
    );
  };
  render() {
    return <button onClick={this.test}></button>;
  }
}

第二种:建立文件:setupProxy.js:内容为:下面代码

const proxy = require('http-proxy-middleware');
module.exports = function (app) {
  app.use(
    proxy('/api1', {
      target: 'http://localhost:5000',
      changeOrigin: true,
      pathRewrite: { '^/api1': '' },
    }),
  );
};
import axios from 'axios';
export default class A extends React.Component {
  test = () => {
    axios.get('http://localhost:3000/api1/student').then(
      (response) => {
        console.log(response.data);
      },
      (err) => {
        console.log(err.message);
      },
    );
  };
  render() {
    return <button onClick={this.test}></button>;
  }
}

target:请求转发地址

changeOrigin:控制服务器收到的请求头中的Host字段的值

true:收到的值为localhost:5000,

false:收到的值为locahost:3000

默认为false

pathRewrite:重写请求路径

3.8、兄弟组件传值

PubSubjs(适用于任何组件间的传值)

npm install pubsub-js --save

使用时引入:import PubSub from 'pubsub-js'

发布数据:

PubSub.publish('jjj',{kk:55s})//两个参数:第一个参数为传key,第二个是传value

接收数据:

let token = PubSub.subscribe('jjj', (_, data) => {
  //两个参数:第一个参数是key,第二个是回调:回调中还有两个参数:1为key,2为value
  console.log(data);
});

不使用时:

PubSub.unsubscribe(this.token)

3.9、使用fetch请求数据

fetch('')
  .then((response) => {
    console.log(response);
    return response.json();
  })
  .then((response) => {
    console.log(response);
  })
  .catch((error) => {
    console.log(error);
  });
search = async () => {
  try {
    const response = fetch('');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log(error);
  }
};
​

四、React路由

4.1、路由模式

4.1.1、history

创建一个history:

let history = createBrowserHistory()

监听history:

history.listen((location)=>{
  console.log(location)
})

goBack()回退,goForward()前进

push():添加

replace():替换(不会生成记录)

4.1.2、hash(兼容性极佳)

创建一个hash:

let history = createHashHistory()

4.2、react-router-dom

安装:这里安装的时5版本

npm install react-router-dom@5 --save

4.2.1、Link

import {Link} from 'react-router-dom'

使用时:

<Link to=''></Link>

to:填写地址名,Link被编译时,会变成a标签

如若使用时需高亮效果请使用:NavLink

定义activeClassName类:使用时注意权限

通过this.props.chirdren获取标签体内容

<NavLink to=''></NavLink>

4.2.2、BrowserRouter和HashRouter

import {BrowserRouter,HashRouter} from 'react-router-dom'
<BrowserRouter>//告诉浏览器history模式
  <App/>
</BrowserRouter>
<HashRouter>//告诉浏览器history模式
  <App/>
</HashRouter>

4.2.3、Route

import {Route} from 'react-router-dom'
<Route path='' component=''/>

path:地址名

component:组件名

4.2.4、路由组件和一般组件的区别

1,写法不同: 一般组件:<Demo/> 路由组件:<Route path="/demo" component={Demo}/ 2,存放位置不同: 一般组件:components 路由组件::pages 3,接收到的props不同 一般组件:写组件标签时传递了什么,就能收到什么 路由组件:接收到三个固定的属性 history: go:f go(n) goBack:f goBack() goForward:f goForvard() push:f push(path,state) replace:f replace(path,state) location: pathname;"/about search:"" state: undefined match: params:{} path: "/about" url:"/about"

4.2.5、switch

提高路由效率:用法如下

import {Switch} from 'react-router-dom'
<switch>
  <Route to='/home' component='home'></Route>
  <Route to='/home' component='test'></Route>
</switch>

如上述代码所示,如没有switch,Route会一直匹配下去,结果是两个组件内容都显示

相反,外面包一层switch,则只匹配第一个。

4.2.6、多级目录样式丢失问题

解决方案:

1、index.html中引用的样式表前面去点

<link href='./css/bootstrap.css'/>
<link href='/css/bootstrap.css'/>

2、index.html中引用的样式表前面加代码

<link href='./css/bootstrap.css'/>
<link href='%PUBLIC_URL%/css/bootstrap.css'/>

3、将history设为hash模式

使用方法看4.1.2

4.2.7、模糊匹配

从前往后匹配

<Link to='/home/a/b/c'/>
<Route path='/home'/>

原则Route要的有即可,但是顺序不能变

路由嵌套时:就是采用模糊匹配

子组件的路径为:父组件的路径名+子组件的路径名

<Link to='/home'/>父组件
<Link to='/home/news'/>子组件

4.2.8、严格匹配

默认是模糊匹配

要想开启严格匹配

两张写法

<Route exact path='/home'/>
<Route exact={true} path='/home'/>

必须一摸一样

非特殊情况下不要随意开启,有些时候可能会影响二级路由的匹配

4.2.9、Redirect

自闭和标签,一般放在最后

使用如下:

import {Redirect} from 'react-router-dom'
<Redirect to=''/>

4.3、路由传值

4.3.1、params参数

路由链接(携带参数):

<Link to='/dome/test/18'></Link>

注册路由(声明接收):

<Route path='/dome/test/:age' component={Test}/>

接收参数:通过props.match.params接收

console.log(this.props.match.params)

4.3.2、search参数

形似ajax中query

路由链接(携带参数):

<Link to={`/dome/test/?age=${}&name=${}`}></Link>

注册路由(正常注册):无须接收

<Route path='/dome/test' component={Test}/>

接收参数:通过props.location.search接收:接受的数据需要自己加工(推荐一个库:querystring(无需安装,react已经安装))

import qs from 'querystring';
let result = qs.parse(this.props.location.search.slice(1));
console.log(this.props.location.search);

多组key=value用&链接的方式:urlencoded编码方式

4.3.3、state参数

优点:浏览器url中无参数

清楚浏览器历史记录会报错

采用es6中||语法即可解决:看接收参数

路由链接(携带参数):

<Link to={{ pathname: '/home/test', state: { age: xx.age } }}></Link>;

注册路由(正常注册):无须接收

<Route path='/dome/test' component={Test} />;

接收参数:通过props.location.state接收

const { age } = this.props.location.state || {};
console.log(this.props.location.state);

4.4、路由跳转模式

4.4.1、push

默认就是push,可以在浏览器中留下历史记录

可以通过goBack()实现后退

可以通过goForward()实现前进

4.4.2、replace

如果想开启,方法如下:

<Link replace to={{pathname:'/home/test',state:{age:xx.age}}}></Link> 
<Link replace={true} to={{ pathname: '/home/test', state: { age: xx.age } }}></Link>;

开启后则不会留下历史记录

4.5、编程式路由跳转

4.5.1、push

import React, { Component } from 'react';
​
export default class Index extends Component {
  pushjump = () => {
    //1) 传递params参数
    this.props.history.push(`/home/test/${name}/${age}`);
    //2) 传递search参数
    this.props.history.push(`/home/test/?name=${name}&age=${age}`);
    //3) 传递state参数
    this.props.history.push(`/home/test`, { name, age });
  };
  render() {
    return (
      <div>
        <button onClick={this.pushjump()}></button>
      </div>
    );
  }
}

4.5.2、replace

import React, { Component } from 'react';
​
export default class Index extends Component {
  pushjump = () => {
    //1) 传递params参数
    this.props.history.push(`/home/test/${name}/${age}`);
    //2) 传递search参数
    this.props.history.push(`/home/test/?name=${name}&age=${age}`);
    //3) 传递state参数
    this.props.history.push(`/home/test`, { name, age });
  };
  render() {
    return (
      <div>
        <button onClick={this.pushjump()}></button>
      </div>
    );
  }
}

补充:常用的还有go()、goBack()、goForward()

4.6、withRouter

在一般组件中使用路由组件中的API时,需调用withRouter来实现,否则报错,方法如下

import { withRouter } from 'react-router-dom';
export default withRouter(
  class Test extends React.Component {
    render() {
      return;
    }
  },
);
import { withRouter } from 'react-router-dom';
class Test extends React.Component {
  render() {
    return;
  }
}
export default withRouter(Test);

4.7、BrowserRouter和HashRouter的区别

1.底层原理不一样

        BrowserRouter使用H5的history API,不兼容IE9一下版本

        HashRouter使用的时URL的Hash值

2.path表现形式不一样

        BrowserRouter的路径中没有#

        HashRouter的路径包含#

3.刷新后对路由state参数的影响

        BrowserRouter没有任何影响,因为state存放在history之中

        HashRouter刷新后会导致state的数据丢失!!!!!!!!!!!

 4.注:HashRoute会解决一些路径产生的问题

五、redux学习

5.1、安装

npm install redux 
yarn add redux

5.2、store文件

在src文件下创建一个文件夹,名为store

1)store.js:创建store实例

2)actions:专门用于创建action

3)reducer:

4)constant:创建常量文件

内容为:

import { createStore } from 'redux';
//引入为组件服务的reducer
import test from './test';
//导出store
export default createStore(test);

reducer内容为:

const initState = '根据需要写对应的数据';
export default function test(preState = initState, action) {
  /*switch(){
      case  :  return
      default: return preState
    }*/
  return;
}

参数preState:之前的状态

参数action:动作状态

注:开始前要初始化状态

5.2.1、getState()

获取状态值:

注:只有一个组件时用这个api!!!!!!

store.getState()

5.2.2、subscribe()

检测redux状态更新

import React, { Component } from 'react';
​
export default class index extends Component {
  componentDidMount() {
    store.subscribe(() => {
      this.setState({});
    });
  }
  render() {
    return <div></div>;
  }
}

如果使用较多建议写在index.js之中:一劳永逸

DOM的Diff算法不会引起大面积更新

store.subscribe(() => {
  ReactDOM.render(<App />, document.getElementById('root'));
});

如果使用了react-redux,则不需要检测redux状态更新,react-redux会帮你检测

5.2.3、dispatch()

通知redux更改状态

store.dispatch()

5.2.4、异步action

export const test = () => {
  return () => {};
};

需借助:redux-thunk:安装

npm install redux-thunk
import thunk from 'redux-thunk'

还需借助:applyMiddleware

export default createStore(test, applyMiddleware(thunk));

异步action中一般都会调用同步action,异步action不是必须要用的

六、react-redux

1、所有的UI组件都应该包裹一个容器组件,他们是父子关系。

2、容器组件是真正和redux打交道的,里面可以随意的使用redux的api。 3、UI组件中不能使用任何Redux的API。

4、容器组件会传给UI组件:(1).redux中所保存的状态。(2).用于操作状态的方法。

5、备注:容器给UI传递:状态、操作状态的方法,均通过props传递。

6、建议:先看不懂的话,将整合版跟上面的一起看

6.1、安装react-redux

npm install react-redux

创建一个js文件作为容器组件:

1)引入UI组件、引入redux中的store

import testui from './'

引入store需通过上层组件传递

在App组件中先引入容器组件,通过props的方式传递store(仅仅限于组件不多的情况)

import store from './store';
import Test from '';
<Test store={store} />;

在组件过多的情况下需借助:Provider,场景:在最外层的index.js里面

import store from './';
import { Provider } from 'react-redux';
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);

引入store,引入Provider,用Provider包裹App,将store传给Provider,react-redux会自动将需要store的组件,将store传过去

2)链接组件:借助connnet组件

import { connet } from 'react-redux';
import testui from './';
export default connet(mapStateToProps, mapDispatchToProps)(testui);

mapStateToProps:

1)用于传递状态

2)返回的是一个对象

3)返回对象中的key就作为传递传递给ui组件props的key,value就作为传递传递给ui组件props的value

mapDispatchToProps:用于操作状态的方法

1)用于传递操作状态的方法

2)返回的是一个对象

3)返回对象中的key就作为传递传递给ui组件props的key,value就作为传递传递给ui组件props的value

从一般写法到简写:推荐最下面的简写

function mapStateToProps(state) {
  return { count: state };
}
function mapDispatchToProps(dispatch) {
  return {
    jia: () => {
      dispatch({});
    },
    jian: () => {
      dispatch({});
    },
  };
}
const mapStateToProps = (state) => ({ count: state });
const mapDispatchToProps = (dispatch) => ({
  jia: () => {
    dispatch({});
  },
  jian: () => {
    dispatch({});
  },
});
export default connet((state) => ({ count: state }), {
  jia: testaction,
  jian: testaction,
  /*jian:直接写action,react-redux会自动分发dispatch*/
})(testui);

总结:

(1)、明确两个概念

1)UI组件不能使用任何redux的api,只负责页面交互

2)容器组件负责和redux通信,将结果交给UI组件

(2)、如何创建容器组件:借助react-redux中connnet函数

        connet(mapStateToProps,mapDispatchToProps)(UI组件)

        mapStateToProps:映射状态,返回值是一个对象

        mapDispatchToProps:映射操作状态的方法,返回值是一个对象

(3)、容器组件中的store是靠props传进去的而不是在容器组件中直接引入

6.2、整合版

容器组件+UI组件

import React, { Component } from 'react';
import { actions } from './';
import connet from 'react-redux';
class Test extends Component {
  divonclick = () => {
    this.props.jia, this.props.jian;
  };
  render() {
    return <div onClick={this.divonclick}></div>;
  }
}
export default connet((state) => ({ count: state }), {
  jia: testaction,
  jian: testaction,
  /*jian:直接写action,react-redux会自动分发dispatch*/
})(Test);

总结:

1.容器组件跟UI组件整合为一个组件

2.无需自己给容器传store,借助Provider一劳永逸

3.无需自己检测redux状态改变,使用react-redux之后,react-redux会帮你检测

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

5.跟redux交流

1)定义UI组件不暴露

2)定义容器组件暴露,

3)在UI组件中通过this.props调用

6.3、redux数据共享

6.3.1、分门别类

当有多个组件时,redux中文件过多,就需要分门别类

首先创建两个文件夹

1、actions:所有的action放里面

2、reducers:所有的reducer放里面

6.3.2、多个reducer合并

借助combineReducer合并多个reducer:在store中修改如下

import { createStore, applyMiddleware, combineReducer } from 'redux';
import match from './';
import test from './';
const allReducers = combineReducer({
  count: test,
  age: match,
});
export default createStore(test, applyMiddleware(thunk));

combineReducer()传输一个对象,

对象中有多组key:value

key是自己起名字

value是reducer容器组件的返回值,直接写容器组件即可

6.3.3、纯函数

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

2、必须遵守一些约束

1)不得改写参数数据

2)不会产生任何副作用,例如网络请求

3)不能调用Date.now()或者Math.random()等不纯的方法

3、redux的reducer函数必须是一个函数

6.3.4、redux开发者工具

安装:

npm install redux-devtools-extension

在store中做修改,引入redux-devtools-extension

import { composeWithDevTools } from 'redux-devtools-extension';
//无异步actions时
export default createStore(test, composeWithDevTools());
//有异步actions时
export default createStore(test, composeWithDevTools(applyMiddleware(thunk)));

七、打包

npm build

借助serve开启服务器:安装serve

npm install serve -g

运行:

serve build

八、react扩展

8.1、setState

1)setState(stateChange,[callback])-----对象式的setState

1.stateChange为状态改变对象(该对象可以体现出状态的更改)

2.callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用

2)setState(updater,[callback])-----函数式的setState

1.updater为返回stateChange对象的函数

2.stateChange可以接收到state和props

3.callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用

this.setState((state, props) => {
  return {};
});
this.setState((state, props) => ({}));

总结:

1.对象式setState是函数式setState的简写方式(语法糖)

2.使用原则

(1)如果新状态不依赖于原状态==》使用对象方式

(2)如果新状态依赖于原状态==》使用函数方式

(3)如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中获得

8.2、lazyLoad

借助lazy、Suspense

import react,{Component,lazy,Suspense} from 'react'
const =lazy(()=>{import('./')})//懒加载路由
<Suspense fallback={组件}>
  //注册路由
  <Route></Route>
</Suspense>

8.3、Hooks

8.3.1、State Hooks

function Dome() {
  const [name, setName] = React.useState('Tom');
  function changeContent() {
    setName('Jack'); //第一种写法
    setName((name) => {
      return 'Jack';
    }); //第二种写法
  }
  return <h2 onClick={changeContent}>{name}</h2>;
}

总结:

1.State Hook让函数组件也可以有state状态,并进行状态数据的读写操作,

2.语法:count [状态名称,改变状态的方法名称] = React.useState(初始化值)

3.useState()说明:

参数:第一次初始化指定的值在内部做缓存

返回值:包含两个两个元素的数组,第一个为当前状态值,第二个为更新状态的方法

4.setState()两种写法:

setState(newvalue):参数为非函数值,直接指定新的状态值

setState((oldvalue)=>{newvalue}):参数为函数,接收原来的状态值,返回新的状态值

8.3.2、Effect Hooks

React.useEffect(() => {
  return () => {
    //在组件卸载前执行
  };
}, []); 
//如果指定的是 [],回调函数只会在第一次render())后执行,如果指定值,就检测值是否更新,如果更新重新调用该方法

(1)Effect Hook 可以让你在足数组件中执行副作用操作(用于模拟类组件中的生命周期钓子) (2)React中的副作用操作: 发ajax请求数据获取 设置订阅(启动定时器) 手动更改真实DOM (3)语法和说明: useEffect(O => { //在此可以执行任何带副作用操作 return() => {//在组件卸载前执行 //在此次做一些收尾工作,比和清除定时器/取消订阅等 } }, [stateValue])//如果指定的是 [ ],回调函数只会在第一次render())后执行,如果指定值,就检测值是否更新,如果更新重新调用该方法

(4)可以把useEffect Hook 看依如下二个函数的组合 componetnidMount() componentDidUpdate() componedEWTIUnmount()

8.3.3、Ref Hooks

function Dome() {
  const myRef = React.useRef();
  return <h2 ref={myRef}>555</h2>;
}

总结:

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

8.4、Fragment

作用:去除根标签

两种方法

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

当需要遍历时,请使用第一种,Fragment可以传key,空标签不可以

8.5、Context

适用于,祖组件给子组件传递数据(在开发中一般不用Context,一般用于封装react插件)

第一种:类组件给类组件传数据

const MyContext = React.createContext();
const { Provider } = MyContext;
export default class A extends React.Component {
  state = { name: 'Jack' };
  render() {
    const { name } = this.state;
    return (
      <div>
        <Provider value={{ name }}>
          <B />
        </Provider>
      </div>
    );
  }
}
class B extends React.Component {
  render() {
    return (
      <div>
        <C />
      </div>
    );
  }
}
class C extends React.Component {
  static contextType = MyContext; //声明
  render() {
    return <div>接收到的数据为:{this.context.name}</div>;
  }
}

第二种:

类组件给函数组件传数据

const MyContext = React.createContext();
const { Provider, Consumer } = MyContext;
export default class A extends React.Component {
  state = { name: 'Jack' };
  render() {
    const { name } = this.state;
    return (
      <div>
        <Provider value={{ name }}>
          <B />
        </Provider>
      </div>
    );
  }
}
class B extends React.Component {
  render() {
    return (
      <div>
        <C />
      </div>
    );
  }
}
function C() {
  return (
    <div>
      接收到的数据为:
      <Consumer>
        {(value) => {
          return value.name;
        }}
      </Consumer>
    </div>
  );
}

8.6、PureComponent

一下两种写法都可以,推荐PureComponent

export default class A extends React.Component {
  state = { name: 'Jack' };
  shouldComponentUpdate(preProps, preState) {
    return !this.state.name == preState.name;
  }
  render() {
    return <div></div>;
  }
}
export default class A extends React.PureComponent {
  state = { name: 'Jack' };
  render() {
    return <div></div>;
  }
}

PureComponent帮你书写shouldComponentUpdate这个生命周期函数,检测props和state是否更新

当检测未更新时,shouldComponentUpdate,return false

当检测更新时,shouldComponentUpdate,return true

提高效率

8.7、render props

组件内将组件传输过去:类似Vue中的插槽

组件A传递

<A
  render={(name) => {
    <B name={name} />;
  }}
></A>;

组件B接收:

this.props.render(name)

总结:

vue中: 使用slot技术,也就是通过组件标签体传入结构

<A><B/></A>

React中: 使用children props:通过组件标签体传入结构 使用render props:通过组件标签属性传入结构,一般用render函教属性

8.8、ErrorBoundary

错误边界:捕获后代组件错误,渲染出备用页面

注:只能捕获后代组件生命周期产生的错误!!!

使用:getDerivedStateFromError配合componentDidCatch

import React, { Component } from 'react';
​
export default class index extends Component {
  //生命周期函数,一旦后台组件报错,就会触发
  static getDerivedStateFromError() {
    return {
      hasError: true,
    };
  }
  //在render之前触发
  //返回新的state
  componentDidCatch(error, info) {
    //统计页面的错误。发送话求发送到后台去
    console.log(error, info);
  }
  render() {
    return <div></div>;
  }
}

8.9、组件间通信方式

组件间的关系: 父子组件 兄弟组件非嵌套组件 祖孙组件(跨级组件

通信方式:

1.props: (1).childr en props (2).render prOps 2消息订阅-发布: pubs-sub、event等等 3.集中式管理: redux、dva等等 4.conText: 生产者-消费者模式

搭配方式:

父子组件:props ​ 兄弟组件;消息订阅-发布、集中式管理 ​ 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

九、router6

react官网:默认使用函数式组件

安装最新版本router

npm install react-router-dom

1、之前的switch删除,现在用Routes包裹,功能一样

2、之前的component换成了element

3、之前组件注册直接写组件名,现在换成了写组件标签

import { Routes, Route } from 'react-router-dom';
<Routes>
  <Route path='/test' element={<Test />} />
</Routes>;

Route还可以传一个属性:caseSensitive是否区分大小写,默认是否

4、之前的重定向删除,现在用Navigate:用法看下面代码

import { Navigate } from 'react-router-dom';
<Routes>
  <Route path='/test' element={<Test />} />
  <Route path='/' element={<Navigate to='/test' />} />
</Routes>;

Navigate还可以传replace属性默认是push开启看下面代码

<Route path='/' element={<Navigate to='/test' replace={true} />} />;

注:当使用Navigate时,就会切换视图,修改路径

5、之前‘实现高亮效果加activeclassName,现在要求是一个函数

<NavLink
  className={({ isactive }) => {
    isactive ? '原来的类名+活跃时的类名' : '原来的类名';
  }}
></NavLink>;
function isactive({ isactive }) {
  return isactive ? '原来的类名+活跃时的类名' : '原来的类名';
}
<NavLink className={isactive}></NavLink>;

6、useRoutes

路由表创建:

export default function App() {
  const element = useRoutes([
    {
      path: '/test',
      element: <Test />,
    },
    {
      path: '/',
      element: <Navigate to='/test' />,
    },
  ]);
  return <div>{element}</div>;
}

项目开发时一般在src下创建一个文件夹名为routes,里面创建index.js文件,对路由表集中管理

7、Outlet

作用:指定路由组件呈现的位置:与Vue中的router-view相似

注:自闭和标签

import { Outlet } from 'react-router-dom';
​
<Outlet />;

8、路由嵌套

const element = useRoutes([
  {
    path: '/test',
    element: <Test />,
    children: [{}],
  },
  {
    path: '/',
    element: <Navigate to='/test' />,
  },
]);

跟vue很像:加children属性

在子组件的路由可以省略父组件的路由地址

path = '/test'; //父组件
path = 'new'; //子组件
path = './new'; //子组件
path = '/test/new'; //子组件

三种写法推荐第一种(省事)

9、路由传参

(1)params

<Link to={`test/${属性名}/${属性名}`}></Link>;

在路由表中做修改将原来的地址修改为path:'test/:属性名/:属性名'。

接收借助:useParams接收数据

import { useParams } from 'react-router-dom';
function Test() {
  const a = useParams();
  console.log(a);
}

扩展:了解即可

useMatch():需要传完整的路径才能解析到数据

const a = useMatch('/jjj/kkk/kkk/lll...');

(2)search

<Link to={`test/${属性名}/${属性名}`}></Link>;

接收借助:useSearchParams接收数据

import { useSearchParams } from 'react-router-dom';
const [search, setsearch] = useSearchParams();
const 属性名 = search.get('属性名');

(了解)setsearch:主要用于更新接收的数据,执行过后,地址栏会被修改,数据也会被修改

useLocation

import { useLocation } from 'react-router-dom';
const x = useLocation();
console.log(x);

(3)state

<Link to='/test' state={{ 属性名: 属性值 }}></Link>;

接收借助:useLocation

import { useLocation } from 'react-router-dom';
const {
  state: { state属性 },
} = useLocation(); //连续解构赋值

10、编程式路由跳转

借助:useNavigate

import { useNavigate } from 'react-router-dom';
​
const navigate = useNavigate();
function test() {
  navigate('路径', {
    replace: false,
    state: {
      属性名: 属性值,
    },
  });
}
​
<div onClick={test}></div>;

如需要接受值

import { useNavigate } from 'react-router-dom';
​
const navigate = useNavigate();
function test(接收值) {
  navigate('路径', {
    replace: false,
    state: {
      属性名: 属性值,
    },
  });
}
​
<div
  onClick={() => {
    test(传值);
  }}
></div>;

注:一般组件使用路由组件api时不需要之前的withRouter,直接使用useNavigate

前进useNavigate(1)

后退useNavigate(-1)

11-15知道即可不常用!!!

11、uselnRouterContext

作用:如果组件在Router的上下文中呈现,则 useInRouterContext钩子返回 true,否则返回 falsea

import { uselnRouterContext } from 'react-router-dom';
​
console.log(uselnRouterContext());

12、useNavigationType()

1.作用:返回当前的导航类型(用户是如何来到当前页面的) 2.返回值 POP PUSH REPLACE 3.备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)

import { useNavigationType } from 'react-router-dom';
​
console.log(useNavigationType());

13、useOutlet()

1,作用:用来呈现当前组件中渲染的嵌套路由

2.示例代码:

const result = useOutlet();
console, log(result);
//如果嵌套路由没有挂载,则result为nu11
//如果嵌套路由己经挂载,则展示嵌套的路由对象

14、10.useResolvedPath()

1.作用:给定一个URL值,解析其中的:path、search、hash值

import { useResolvedPath } from 'react-router-dom';
​
console.log(useResolvedPath('/test/?id=001&name=tom#has'));

内容到这里就结束啦, 谢谢大家的观看,如有错误:请联系qq:3259797986

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值