react学习笔记

react学习笔记

前言准备:
vscode常用插件安装:
输入react,安装 ES7+ React/Redux/React-Native snippets
rcc:生成类代码片段(react class component)
rfc:生成函数代码片段(react function component)
输入Chinese ,安装Chinese (Simplified) (简体中文) Language Pack for Visu
中文界面
prettier ,安装Prettier - Code formatter 代码格式化 shift+alt+F
sneak mark检测英文语句中的中文符号
Auto Close Tag 自动补全标签和联名重复标签
open in browser → 可以通过快捷键 “Alt + B”(mac: “option + B”) 来在浏览器上打开
html 文件
Simple React Snippets
EasyLess

1、react入门

一、简介
  1. 中文官网: https://react.docschina.org/
  2. 用于动态构建用户界面的 JavaScript 库(只关注于视图),由Facebook开源
二、特点
  1. 声明式编码
  2. 组件化编码
  3. React Native 编写原生应用
  4. 高效(优秀的Diffing算法)
三、高效原因
  1. 使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
  2. DOM Diffing算法, 最小化页面重绘。
四 、jsx语法规则:

1、定义虚拟DOM时,不能加引号
2、标签中混入js表达式时要用{}才能取到值
3、样式的类名指定要用className
4、内联样式要用style={{key:value}}去写
5、jsx的跟标签只能有一个
6、标签必须闭合,也就是有 />
7、标签首字母
(1)若小写字母开头则转为html中的同名元素,如果html中无对应同名标签则报错
(2)若大写字母开头,react就去渲染对应的组件,若没有定义组件则报错

五、数组遍历的例子
 <script type="text/babel">
        //摸你数据
        const data = ["Angular", "React", "Vue"];
        //1、创建虚拟DOM
        const VDOM = (
          <div>
            <h1>前端js框架列表</h1>
            <ul>
              {data.map((item,index) => {
                return <li key={index}>{item}</li>;
              })}
            </ul>
          </div>
        );
        ReactDOM.render(VDOM, document.getElementById("test"));
      </script>

2、React面向组件编程

1 、组件
1.1、渲染组件

执行了:

 ReactDOM.render(<Demo/>, document.getElementById("test"));

之后发生了什么?
1、React解析组件标签,找到Demo组件
2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真是DOM,随后呈现在页面

1.2 函数式组件
function Demo(){
          return <h2>我是函数式组件</h2>
        }
 ReactDOM.render(<Demo/>, document.getElementById("test"));
1.3 类式组件
		//1、创建类式组件
        class MyComponent extends React.Component {
          render() {
            return <div>我是用类定义的组件</div>;
          }
        }
        ReactDOM.render(<MyComponent />, document.getElementById("test"));
         /*
         * 执行了ReactDOM.render(<Demo/>, document.getElementById("test"));之后发生了什么?
         * 1、React解析组件标签,找到Demo组件
         * 2、发现组件是使用类定义的,随后new 出来该类的实例,并通过该实例调用到原型上render上的方法
         * 3、将render返回的虚拟dom转为真是dom,随后呈现在页面中
         */
2、组件的三大属性
2.1 state
  1. state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
  2. 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
  3. 组件中render方法中的this为组件实例对象 组件自定义的方法中this为undefined,如何解决?
    a) 强制绑定this: 通过函数对象的bind()
    b) 箭头函数 状态数据,不能直接修改或更新.要用setState
<script type="text/babel">
        //创建类式组件
        class Weather extends React.Component {
          //初始化状态
          state = { isHot: false, wind: "大风" };
          //每次更新状态都重新渲染 调用 1+n次
          render() {
            const { isHot, wind } = this.state;
            return (
              <h1 onClick={this.changeWeather}>
                今天天气真 {isHot ? "热" : "凉快"},今天:{wind}
              </h1>
            );
          }
          //箭头函数没有自己的this,它找外层函数的this作为自己的this
          changeWeather = () => {
            const isHot = this.state.isHot;
            //状态不可直接更改,需使用其api setState。它是一种合并更新,不是替换
            this.setState({
              isHot: !isHot,
              wind: isHot ? "有风,吹得冷" : "没有风,热死了",
            });
          };
        }
        ReactDOM.render(<Weather />, document.getElementById("test"));
      </script>
2.2 props
2.21 展开运算符 …

展开运算符:…
①展开数组 例如:

let arr = [1,2,3,4]  ====> console.log(...p) ===>1,2,3,4

②连接数组

let arr1 = [1,2,3]  let arr2 = [5,6,7] 
===>let arr3=[...arr1,arr2]
===>console.log(arr3)
===>[1,2,3,5,6,7]

③数组的归约运算(java8中的归约也是差不多这样的)

console.log(sum(1,2,3,4));
===> function sum(...numbers){	
	return numbers.reduce((preValue,currentValue)=>{
		return preValue+currentValue
})}

输出数组中的数字的和。

2.22 基本使用(props传值)

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

  1. 通过标签属性从组件外向组件内传递变化的数据
  2. 注意: 组件内部不要修改props数据
 <script type="text/babel">
        class Person extends React.Component {
          render() {
            const { name, age, sex, salary } = this.props;
            return (
              <ul>
                <li>姓名:{name}</li>
                <li>年龄:{age}</li>
                <li>性别:{sex}</li>
                <li>工资:{salary}</li>
              </ul>
            );
          }
        }
        const p = { name: "李四", age: "18", sex: "女", salary: "74323" };
        ReactDOM.render(<Person {...p} />, document.getElementById("test"));
      </script>
2.23 对props进行限制
 Person.propTypes = {
        name: PropTypes.string.isRequired, //限制name必传,且为字符串
        sex: PropTypes.string,//限制sex,为字符串
        age: PropTypes.number,//限制age必传,为数字
        speak: PropTypes.func,//限制speak,为函数
      };
      Person.defaultProps = {
        sex: "不男不女",
      };
2.24 函数使用props
function Person(props){
        const {name,age,sex,salary} = props;
        return (
            <ul>
              <li>姓名:{name}</li>
              <li>年龄:{age + 1}</li>
              <li>性别:{sex}</li>
              <li>工资:{salary}</li>
            </ul>
          );
      }
      const p = { name: "李6", age: 18, sex: "女", salary: 74323 };
      ReactDOM.render(<Person {...p} />, document.getElementById("test3"));
3.refs

ref即是给当前所在行打标签,组件内的标签可以定义ref属性来标识自己

①字符串形式的ref(不推荐)
<script type="text/babel">
        //创建组件
        class MyComponent extends React.Component {
          showData1 = () => {
           const {input1} = this.refs
           alert(input1.value)
          };
          showData2 = () => {
           const {input2} = this.refs
           alert(input2.value)
          };
          render() {
            return (
              <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据" />
                &nbsp;
                <button onClick={this.showData1}>点我提示左侧数据</button>&nbsp;
                <input ref = "input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
              </div>
            );
          }
        }
        ReactDOM.render(<MyComponent />, document.getElementById("test"));
      </script>
②回调函数形式(内联)
<script type="text/babel">//创建组件class MyComponent extends React.Component {showData1 = () => {const {input1} = thisalert(input1.value)};showData2 = () => {const {input2} = thisalert(input2.value)};render() {return (<div><input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据"/>&nbsp;<button onClick={this.showData1}>点我提示左侧数据</button>&nbsp;<input ref={c=>this.input2=c} type="text" onBlur={this.showData2} placeholder="点击按钮提示数据"></input>&nbsp;</div>);}}
​        ReactDOM.render(<MyComponent />, document.getElementById("test"));</script>
③类的绑定
 <script type="text/babel">
        //创建组件
        class MyComponent extends React.Component {
          state = { isHot: true };
          showData1 = () => {
            const { input1 } = this;
            alert(input1.value);
          };
          changeWeather = () => {
            const { isHot } = this.state;
            this.setState({ isHot: !isHot });
          };
          saveInput = (c)=>{
              this.input1 = c
              console.log('@@',c)
          }
          render() {
            const { isHot } = this.state;
            return (
              <div>
                <h2>今天天气很{isHot ? "炎热" : "凉爽"}</h2>
                {/*<input ref={c => {this.input1 = c;console.log('111')}} type="text" placeholder="点击按钮提示数据"/>&nbsp;*/}
                <input
                  ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/>
                &nbsp;
                <button onClick={this.showData1}>点我提示数据</button>&nbsp;
                <button onClick={this.changeWeather}>点我切换天气</button>&nbsp;
              </div>
            );
          }
        }
        ReactDOM.render(<MyComponent />, document.getElementById("test"));
      </script>
④createRef(推荐)
<script type="text/babel">
        //创建组件
        class MyComponent extends React.Component {
          /*
          *①React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点
          */
         myRef = React.createRef()
          showData1 = () => {
           console.log(this.myRef.current.value)
           //③从Ref容器中取值
           const myRefValue = this.myRef.current.value
           alert(myRefValue)
          };
          render() {
            return (
              <div>
                {/**②把当前节点存在容器myRef中**/}
                <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData1}>点我提示左侧数据</button>&nbsp;
              </div>
            );
          }
        }
        ReactDOM.render(<MyComponent />, document.getElementById("test"));
      </script>
3、受控与非受控组件

非受控组件:现用现取成为非受控组件
受控组件:随着输入把输入值维护到state中

非受控组件

<script type="text/babel">
        class Login extends React.Component 
          handleSubmit = (envent) => {
            //组织默认事件,即不会触发表单提交事件
            envent.preventDefault();
            const { username,password } = this;
            alert(`你输入的用户名:${username.value} 你输入的密码:${password.value}`);  
          };
          render() {
            return (
              <form
                action="http://www.atguigu.com"
                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>
            );
          }
        }
        ReactDOM.render(<Login />, document.getElementById("test"));
      </script>

受控组件

 <script type="text/babel">
        class Login extends React.Component {
          //初始化
          state = { username: "", password: "" };
          handleSubmit = (envent) => {
            //组织默认事件,即不会触发表单提交事件
            envent.preventDefault();
            const { username, password } = this.state;
            alert("用户名: " + username + " 密码:" + password);
          };
          usernameChange = (event) => {
            this.setState({ username: event.target.value });
          };
          passwordChange = (event) => {
            this.setState({ password: event.target.value });
          };
          render() {
            return (
              <form onSubmit={this.handleSubmit}>
                用户名:{" "}
                <input
                  onChange={this.usernameChange}  type="text" name="username" />
                密码:{" "}
                <input
                  onChange={this.passwordChange}
                  type="password"
                  name="password" />
                <button>登录</button>
              </form>
            );
          }
        }
        ReactDOM.render(<Login />, document.getElementById("test"));
      </script>
4、函数的柯里化

这里的saveFormData就是高阶函数:
高阶函数符合任一一个就是:
1.若A函数,接收到的一个参数是函数
2.若A函数,调用的返回值依然是一个函数
函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数,最后统一处理的函数编码形式
常见高阶函数:Promise/setTimeout/arr.map()

 <script type="text/babel">
        class Login extends React.Component {
          handleSubmit = (envent) => {
            //组织默认事件,即不会触发表单提交事件
            envent.preventDefault();
            const { username, password } = this;
            alert(
              `你输入的用户名:${username.value} 你输入的密码:${password.value}`
            );
          };
          saveFormData = (dataType) => {
            return (event) => {
             //[dataType] 是去获取dataType的值
              this.setState({ [dataType]: event.target.value });
            };
          };
          render() {
            return (
              <form onSubmit={this.handleSubmit}>
                用户名:{" "}
                <input
                  onChange={this.saveFormData("username")}
                  type="text"
                  name="username"
                />
                密码:{" "}
                <input
                  onChange={this.saveFormData("password")}
                  type="password"
                  name="password"
                />
                <button>登录</button>
              </form>
            );
          }
        }
        ReactDOM.render(<Login />, document.getElementById("test"));
      </script>

简单的例子

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

不用柯里化解决上面的例子:

<script type="text/babel">class Login extends React.Component {handleSubmit = (envent) => {//组织默认事件,即不会触发表单提交事件
​            envent.preventDefault();const { username, password } = this.state;alert("你输入的用户名:"+username+ "你输入的密码:"+password
​            );};saveFormData = (dataType, value) => {this.setState({ [dataType]: event.target.value });};render() {return (<form onSubmit={this.handleSubmit}>
​                用户名:{" "}<input
​                  onChange={(event) => this.saveFormData("username", event)}
​                  type="text"
​                  name="username"/>
​                密码:{" "}<input
​                  onChange={(event) => this.saveFormData("password", event)}
​                  type="password"
​                  name="password"/><button>登录</button></form>);}}
​        ReactDOM.render(<Login />, document.getElementById("test"));</script>
5、组件的生命周期

① unmountComponentAtNode 卸载组件
② componentDidMount 组件渲染完成去调
③ componentWillUnmount组件将要卸载时执行

<script type="text/babel">
        //创建组件
        class Life extends React.Component {
          state = { opacity: 0.54 };
          fuckMao = () => {
            //①unmountComponentAtNode 卸载组件
            ReactDOM.unmountComponentAtNode(document.getElementById("test"));
          };
          //②componentDidMount 组件渲染完成去调
          componentDidMount() {
            this.timer = setInterval(() => {
              console.log("@@@@");
              //const定义是不能改变的
              let { opacity } = this.state;
              opacity -= 0.1;
              if (opacity <= 0) {
                opacity = 1;
              }
              this.setState({ opacity });
            }, 1000);
          }
          //③组件将要卸载时执行
          componentWillUnmount() {
            clearInterval(this.timer);
          }
          render() {
            return (
              <div>
                <h2 style={{ opacity: this.state.opacity }}>
                  *雪梅c不到怎么办?
                </h2>
                <button onClick={this.fuckMao}>**</button>
              </div>
            );
          }
        }
        ReactDOM.render(<Life />, document.getElementById("test"));
      </script>

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

  1. constructor() //构造器
  2. componentWillMount() //组件将要挂载
  3. render() //渲染
  4. componentDidMount() //组件挂载完成调用。常用。做初始化的事,例如开启定时器、发送网络请求、订阅消息

2.更新阶段:由组件内部this.setSate()或父组件重新render触发
1、shouldComponentUpdate() //组件是否要更新(根据返回的true false决定)
2、componentWillUpdate() //组件将要更新
3、render()
4、componentDidUpdate() //执行更新组件

3.卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1、componentWillUnmount() //将要卸载组件。常用。例如关闭定时器、取消订阅消息
在这里插入图片描述
17.x与16.x之间的差别:

  1. componentWillMount ==UNSAFE_componentWillMount
  2. componentWillReceiveProps == UNSAFE_componentWillReceiveProps
  3. componentWillUpdate ==UNSAFE_componentWillUpdate

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

重要的勾子 常用

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

为什么遍历时,key最好不要用index?

A.虚拟DOM中的key的作用
1.简单说:key是虚拟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,随后渲染到页面
B.用index作为key可能会引发问题:
1.若对数据进行:逆序添加、逆序删除等破坏顺序操作
会产生没必要的真实DOM 更新,==> 界面效果没问题,但效率低
2.如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题
3.注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表展示,用index作为key没有问题。
C.开发中如何选择key?
1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
2.如果确定只是简单的数据展示,用index也是可以的

3、reat脚手架

1、安装脚手架
第一步,全局安装:npm install -g create-react-app
第二步,切换到想创项目的目录,使用命令:(hello-react是项目名字,随意起)
create-react-app hello-react

2、安装生成随机数的库
安装命令:yarn add nanoid
引入: import {nanoid} from ‘nanoid’
nanoid() 就是全局唯一的字符串,直接用

1、组件间传值:
1.1 父组件 -> 子组件

父组件传值:

<List todos={todos} />

子组件接收:

const { todos } = this.props;
1.2 子组件 -> 父组件

父组件传递函数:

 {/**子组件向父组件传值 */}
<Header addTodo={this.addTodo}/>

父组件中的函数:

 //用于添加一个todo,接收参数是todo对象
  addTodo=(todoObj)=>{
    console.log(todoObj)
    //1.获取原todo,接收参数是todo对象
    const {todos} = this.state
    //2.追加一个todo
    const newTodos = [todoObj,...todos]
    //3.更新状态
    this.setState({todos:newTodos})
  }

子组件:

export default class Header extends Component {
  //键盘按键松开
  handleKeyUp = (event) => {
    //结构赋值
    const { target, keyCode } = event;
    //判断回车
    if (keyCode != 13) {
      return;
    }
    //添加的名字不能为空
    if(target.value.trim() === ''){
        alert('输入不能为空!')
        return
    }
    //准备好一个todo对象
    const todoObj = { id: nanoid(), itemName: target.value, done: false };
	//向父组件传值
    this.props.addTodo(todoObj);
    //清空输入框
    target.value=''
  };
  render() {
    return (
      <div className="todo-header">
        <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" />
      </div>
    );
  }
}

注意: 在onClick传值时需要用高阶函数或者回调函数

 //删除一条数据
  handleDelete=(id)=>{
  	//todo
  }
  
 <button onClick={()=>this.handleDelete(id)}
          className="btn btn-danger"
          style={{ display: mouse ? "block" : "none" }}
        >
          删除
        </button>

defaultChecked 只在第一次起作用,后面不起作用。要用checked.类似的还有defaultValue和value

2、axios

安装 axios: yarn add axios

setupProxy.js

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": "" },})
  );
};

github项目遇到项目: 在setupProxy.js中,原写法,但是项目一直报错:crbug/1173575, non-JS
module files deprecated

const proxy = require('http-proxy-middleware')
module.exports = function (app) {
    app.use(
        proxy('/api1', {//遇见/api1前缀的请求,就会触发该代理配置
            target: 'http://localhost:5000',//请求转发给谁
            changeOrigin: true,//控制服务器收到的响应中Host字段的值
            pathRewrite: {
                '^/api1': ''
            }
        })
}

解决办法:proxy.createProxyMiddleware 代替proxy

module.exports = function (app) {
  app.use(
    proxy.createProxyMiddleware("/api1", {
      //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
      target: "http://localhost:5000", //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: { "^/api1": "" }, //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    })
  );
};
3、消息订阅与发布

安装: npm install pubsub-js --save 或 yarn add pubsub-js
使用步骤:
① 安装npm install pubsub-js --save 引入 import PubSub from 'pubsub-js
② 发布 PubSub.publish(‘atguigu’,{isFirst:false,isLoading:true})
③订阅 PubSub.subscribe

 componentDidMount(){
       this.token = PubSub.subscribe('atguigu',(msg,stateObj)=>{
            console.log("list组件收到订阅发布数据:"+stateObj);
            this.setState(stateObj);
        })
    }

④关闭订阅(这里的this.token是上面订阅处的名字)

 componentWillUnmount(){
        PubSub.unsubscribe(this.token)
    }
4、 fetch的使用

fetch 请求 优化版 await 的使用要在最近的函数加 async

 search = async () => {
 try {const response = await fetch(`https://api.github.com/search/users?q=${value}`);const data = await response.json();
​      console.log(data);
​      PubSub.publish("atguigu", { isLoading: false, users: data.items });} catch (error) {
​      console.log("请求出错(自己处理出错)", error);}
}

4、路由

常用路由组件

1. <BrowserRouter>
2. <HashRouter>
3. <Route>
4. <Redirect>
5. <Link>
6. <NavLink>
7. <Switch>

其它

  1. history对象
  2. match对象
    3.withRouter函数
1、使用路由
1.1 下载 (@是指定版本)

react-router-dom: npm install --save react-router-dom @5.2.0 或者 yarn add react-router-dom

1.2、原生html中靠<a>标签跳转到不同页面,在react中靠路由链接实现切换组

点击按钮About 跳往路径/about

<Link className="list-group-item" to="/about">About</Link>
1.3.路径映射组件

/about 路径映射组件About

   <Route path="/about" component={About} />
1.4.Route与Link都需要包在BrowserRouter里面所以在最外层(这里是index.js)包上BrowserRouter
ReactDOM.render(
<BrowserRouter>
	<App />
</BrowserRouter>,
document.getElementById('root'));

注:react-router-dom 6版本写法不一样①Route必须要包在Routes组件里②element 代替compoent

<Routes>
 <Route path="/about" element={About} />
 <Route path="/home" element={Home} />
</Routes>
2、路由组件与一般组件的区别:

①写法不同:
一般组件:<Demo/> 路由组件: <Route path="/demo" component={Demo}/>
②存放位置不同:一般组件放在components中,而路由组件放在 pages
③ 接收到的props不同:
一般组件:写组件标签时传递了什么就能接收到什么
路由组件:接收到3个固定属性,history、location、match。各自里面又有一堆属性
history常用属性:go/goBack/goForward/push/replace
location常用属性: pathname/search/state
match常用属性: params/path/url

3、NavLink与封装NavLink

1、NavLink可以实现路由连接的高亮,通过activeClassName指定样式名
2、标签体内容是一个特殊的标签属性
3、通过this.props.children可以获取标签体内容

export default class MyNavLink extends Component {
  render() {return (<NavLink activeClassName="piaoran" className="list-group-item" {...this.props}/>)
  }
}
4、Switch的使用

注册路由 Switch 匹配到一个路径就停止匹配其他了:这里两个/home,匹配上路第一个就不再匹配第二个了

 <Switch>
   <Route path="/about" component={About} />
   <Route path="/home" component={Fuck} />
   <Route path="/home" component={Home} />
 </Switch>
5、多路径(/atguigu/about)时样式加载问题

原因它加载时会把多级路径的前面那个(/atpingan)认为是请求路径(在使用的情况下),也就是请求css路径变为了localhost:3000/atpingan/css/bootstrap.css
解决办法:
多路径(/atpingan/about)时样式加载问题:
1.在public/index.html中引入样式时,不写./ 写 /(常用)
2.在public/index.html中引入样式时,不写./ 写 %PUBLIC_URL%(常用:仅限于react脚手架)
3.使用HashRouter (不常用)因为#后面的它认为是hash值是前端资源不带给请求端口

6 模糊匹配与精准匹配

不能随便开启严格匹配,有时候无法匹配二级路由不写就是模糊匹配,只要 <MyNavLink to="/home/a/b">Home</MyNavLink>的路径有path="/home"对应的就可以匹配即输入的路径必须包含要匹配得路径,且顺序要一致加了exact(或者exact={true})就是必须完全一样才能匹配

   <MyNavLink className="list-group-item" to="/atpingan/about" a={1}>
                About
        </MyNavLink>
        <MyNavLink className="list-group-item" to="/atpingan/home/a" b={2}>
                Home
        </MyNavLink>
<Switch>
    <Route path="/atpingan/about" component={About} />
    <Route exact path="/atpingan/home" component={Fuck} />
    <Route path="/atpingan/home" component={Home} />
</Switch>

这里点击Home 会匹配到component={Home}这个组件,不会匹配到component={Fuck}

7 Redirect

重定向:
①谁都匹配不上的时候听从 Redirect的路径
②一加载进来就用Redirect给个默认的展示

   <Switch>
        <Route  path="/about" component={About} />
        <Route  path="/home" component={Home} />
        <Redirect to="/home"/>
    </Switch>
8 嵌套路由

注册嵌套路由:
1、注册子路由时要写上父路由的path( 例如:to=“/home/message”)
2、路由的匹配是按照注册路由的顺序进行的

App.jsx

   {/*路由链接*/}
    <MyNavLink  to="/about">About</MyNavLink>
    <MyNavLink  to="/home">Home</MyNavLink>
    <MyNavLink to="/fuck" >Fuck</MyNavLink>
    {/*注册路由*/}          
    <Switch>
        <Route path="/about" component={About} />   
        <Route path="/home" component={Home} />
        <Route path="/fuck" component={Fuck} />
        <Redirect to="/home"/>
    </Switch>

Home.jsx

      render() {
        return (
          <div>
            <h3>我是Home的内容</h3>
            <div>
            <ul className="nav nav-tabs">
    {/*路由链接*/}
              <li>
                <MyNavLink to="/home/news">News</MyNavLink>
              </li>
              <li>
               <MyNavLink to="/home/message">Message</MyNavLink>
              </li>
            </ul>
    {/*注册路由*/}  
            <Switch>
              <Route path="/home/news" component={News} />
              <Route path="/home/message" component={Message} />
              <Redirect to="/home/message"/>
            </Switch>
            </div>
          </div>
        );
      }
9 路由组件传递参数的三种方式
9.1 路由组件传递params参数

第一步:携带params参数传递
注意写法:to后面是js写法要用{}包起来,${item.id}是携带参数

   {messageArr.map((item) => {
       return (
         <li key={item.id}>
          {/*携带params参数 */}
         <Link to={`/home/message/detail/${item.id}/${item.title}`}>{item.id}{item.title}</Link>
        </li>
         );
     })}

第二步:声明接收params参数(/:id 就对应上面的第一个参数,/:title对应第二个参数)

  <Switch>
              {/**生命接收params参数 */}
       <Route path="/home/message/detail/:id/:title" component={Detail} />
    </Switch>

第三步:接收方组件接收参数(例如上面指定的component={Detail},Deatil就是指定接收参数方)

const {id,title} =  this.props.match.params
9.2 路由组件传递search参数

第一步:向路由组件传递search参数

{/**向路由组件传递search参数 */}
<Link to={`/home/message/detail/?id=${item.id}&title=${item.title}`}>{item.id}{item.title}</Link>

第二步:无需声明 正常注册路由组件即可

   <Switch>
        {/**无需声明 正常注册路由组件即可 */}
       <Route path="/home/message/detail" component={Detail} />
     </Switch>

第三步:接收方组件接收参数

①引入包 import qs from ‘querystring’
重点:querystring原本是Node.js自带的库,再新版本中已被弃用,在名称上有所调整,功能基本没有太大变化
import qs from ‘qs’
② 接收search参数:const { search } = this.props.location
此时接收到的参数是 ?id=18&title=“张三” 这种形式。
③截取并转换 search.slice(1)是截取第2个到最后
const { id, title } = qs.parse(search.slice(1))

9.3 路由组件传递state参数

第一步 向路由组件传递state参数,to里面是传递对象
<Link to={{ pathname: "/home/message/detail", state: { id: item.id, title: item.title }, }} >
第二步 接收state参数:无需声明 正常注册路由组件即可
<Route path="/home/message/detail" component={Detail} />
第三步 接收方组件接收参数
const { id, title} = this.props.location.state||{}

10 路由跳转的两种模式 replace与push

replace={true}

默认是push模式,开启replace模式的方法:

① replace={true}

② push模式是点击回退(<-)就回到上一次的页面,如果开启replace模式那么就不能回到上次页面
<Link replace={true} to={{ pathname: '/home/message/detail/', state: { id: msgObj.id, title: msgObj.title } }} {msgObj.title}</Link>

11、编程式路由导航

replace方式实现编程式路由导航(有param,search,state三种方式):

第一步 传递参数
//1.携带param参数:编写一段代码,让其实现跳转到Detail,且为replace跳转
//this.props.history.replace(/home/message/detail/${id}/${title});
//2.携带search参数:编写一段代码,让其实现跳转到Detail,且为replace跳转
//this.props.history.replace(/home/message/detail/?id=${id}&title=${title});
//3.携带state参数:编写一段代码,让其实现跳转到Detail,且为replace跳转
this.props.history.replace(/home/message/detail,{id,title});

第二步 注册路由

 {/*携带params参数 */}
 {/* <Link to={`/home/message/detail/${item.id}/${item.title}`}>{item.id}{item.title}</Link> */}
{/**向路由组件传递search参数 */}
{/* <Link to={`/home/message/detail/?id=${item.id}&title=${item.title}`}>{item.id}{item.title}</Link> */}
{/**向路由组件传递state参数 */}
<Link replace={true} to={{ pathname: "/home/message/detail", state: { id: item.id, title: item.title }, }} > {item.id} {item.title} </Link>

第三步接收

//1.接收params参数
    // const {id,title} =  this.props.match.params
    //2.接收search参数
     //const { search } = this.props.location
    //search.slice(1)是截取第2个到最后,因为获取到的值是 ?id=18&title="张三"
     //const { id, title } = qs.parse(search.slice(1))
    //3.接收state参数
    const { id, title} = this.props.location.state||{}

push方式实现编程式路由导航(有param,search,state三种方式):

第一步

1.编写一段代码,让其实现跳转到Detail,且为push跳转
    //this.props.history.push(`/home/message/detail/${id}/${title}`);
    //2.携带search参数:编写一段代码,让其实现跳转到Detail,且为push跳转
    //this.props.history.push(`/home/message/detail/?id=${id}&title=${title}`);
     //3.携带state参数:编写一段代码,让其实现跳转到Detail,且为push跳转
     this.props.history.push(`/home/message/detail`,{id,title});

第二第三步与replace一致

history中的属性:

    回退:this.props.history.goBack();
     前进  this.props.history.goForward();
    指定前进或后退N(N可为负)  this.props.history.go(N)
    
12 withRouter 把一般组件转换为路由组件

一般组件(例如这里的Header)想要使用路由组件的属性 例如: this.props.history

  • 需要:①把类的export default去掉 ②在类最底写上export default withRouter(Header)
  • 解释:withRouter可以加工一般组件,让一般组件具有路由组件所特有的API,路由组件返回的是一个新组件
   class Header extends Component {
        back = () => {
          this.props.history.goBack();
        };
        forward = () => {
          this.props.history.goForward();
        };
        go = () => {
          this.props.history.go(-2);
        };
        render() {
          return (
            <div>
              <div className="page-header">
                <h2>React Router Demo</h2>
              </div>
              <button onClick={this.back}>回退</button>&nbsp;&nbsp;
              <button onClick={this.forward}>前进</button>&nbsp;&nbsp;
              <button onClick={this.go}>go按钮</button>&nbsp;&nbsp;
              <hr />
            </div>
          );
        }
      }
      export default withRouter(Header)
      
13 BrowserRouter与HashRouter的区别

1、底层原理不一样
BrowserRouter使用的是H5 的history API不兼容IE9及一下版本,HashRouter使用的是URL的哈希值。
2、path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter 的路径包含#,例如:localhost:3000/#/demo/test
3、刷新后对路由state参数的影响
①BrowserRouter没有任何影响,因为state保存在history对象中
②HashRouter刷新后会导致路由state参数的丢失
4、HashRouter可以用来解决一些路径错误的相关问题(比如样式丢失的解决办法其中之一就是使用HashRouter)

5、扩展组件

1、懒加载

①引入包 import React, { Component, lazy,Suspense } from “react”;
②引入组件 (不能用以前的引入方式)
const Home = lazy(() => import("./Home/index"));
const About = lazy(()=> import("./Home/index"))

③注册路由时用Suspense包裹

<Suspense fallback={<Loading/>}>
  <Route path="/about" component={About} />
  <Route path="/home" component={Home} />
  <Redirect to="/home" />
 </Suspense>

④必须写fallback

这是自定义Loading组件(切记Loading组件的引入不能再是lazy模式)

import Loading from '../2_lazaLoad/loading/index'

    export default class Loading extends Component {
      render() {
        return (
          <div>
            <h1 style={{ backgroundColor: "grey", color: "orange" }}>
              Loading…………………………
            </h1>
          </div>
        );
      }
    }
2、函数式组件使用状态 useState

(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)
(3). useState()说明:
参数: 第一次初始化指定的值在内部作缓存
返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

   import React, { useState } from 'react';
    
    function Example() {
      // 声明一个叫 "count" 的 state 变量  
      const [count, setCount] = useState(0);
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }
3、副作用函数 useEffect

触发副作用钩子 useEffect ①初始化进来 ②第二个参数(即数组[]中的数据变化时)散③卸载组件时
卸载组件会触发useEffect里面的回调(即这里的return的函数)

import React, { useState, useEffect } from "react";
import ReactDOM from 'react-dom'
function useEffectHooks() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    let timer = setInterval(() => {
        setCount((count) => count + 1);
    }, 1000);
    return () => {
      clearInterval(timer);
    };
  }, []);
  function add(){
      setCount(count=>count+1)
  }
  //卸载组件
  function unmount(){
      ReactDOM.unmountComponentAtNode(document.getElementById('root'))
  }
  return (
    <div>
      <h1>当前求和为:{count}</h1>
      <button onClick={add}>fuck me</button>
      <button onClick={unmount}>卸载</button>
    </div>
  );
}
export default useEffectHooks;
4、useRef的神奇用法

在函数式组件中
①在函数里面定义 const myRef = React.useRef(null);
②在需要获取的组件上定义ref
<input type="text" ref={myRef}/>
③ 获取打标签节点的值
myRef.current.value
常用之处:获取初始值、弹出抽屉、弹出模态框
例如:弹出模态框
①在符组件定义 const myRef = React.useRef(null);
②在父组件引用子组件上定义ref >
<About ref={myRef } />
③在点击弹出模态框时定义点击事件(openModal是来自子组件自己定义的)

 onClick={
 	 myRef .current.openModal;
 }

④子组件中定义,即这里所谓的 About组件

 useImperativeHandle(ref,()=>{
  openModal;
})
  const openModal=()=>{
    	//这里定义打开模态框 例如: open=true 模态框的open属性指定这里的open。。。。就可以打开模态框
    	
     }   

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。useImperativeHandle 应当与
forwardRef 一起使用:

export default forwardRef(FancyInput);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神雕大侠mu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值