浅记React

一、React介绍

1.什么是react

1.react是facebook内部的一个javascript类库,用于构建用户界面的 JavaScript 库

2.react官网

https://react.docschina.org/

3.react不是一个弯针的MVC框架,最多可以认为是MVC中的V(View)

4.react引入虚拟DOM机制

5.react引入组件化思想

6.react使用facebook专门为其开发的一套语法糖–jsx

2.使用React的好处

1.react的运行速度快

2.跨浏览器兼容,兼容IE6以上

3.一切皆组件

4.单向数据流

5.语法报错提示非常清晰

3.React的缺点

react不适合做一个完整的框架使用,react本身只是一个视图而已,

二、基本使用

1.单页面

1.使用

(1)引入react核心库

<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

(2)设置一个挂载点

<body>
    <div id="box"></div>
</body>

(3)渲染虚拟DOM到挂载点

<script>
        let box = document.getElementById('box');
        console.log(box);
        // 创建一个虚拟dom
        // console.log(React);
        let h1 = React.createElement('h1',{class:'myh1'},'这是一个虚拟dom-h1')
        //渲染虚拟dom
         ReactDOM.render(h1,box)
    </script>

Rect中的createElement方法用来创建一个虚拟DOM的函数,支持三个参数

这个函数支持三个参数:

1.要创建的标签名称(只能是系统内置的标签)

2.要创建的标签属性(支持内置属性和自定义属性)

3.标签的内容

ReactDOM中的render方法用来渲染虚拟DOM到真是DOM中

这个函数支持三个参数:

1.要渲染的虚拟DOM元素

2.要渲染的位置

3.回调函数,一般不使用

2.脚手架

安装

npm i -g create-react-app

此命令仅执行一次,不用每次初始化项目时都来安装

初始化项目

create-react-app react-demo

运行项目

npm start

浏览器会自动打开,并且运行在3000端口上

项目目录结构

项目名称

​ node_modules 项目依赖目录

​ public 项目执行根目录

​ index.html 项目首页

​ 静态资源

​ src 项目源码目录

​ APP.css 项目根组件的样式文件

​ APP.js 项目根组件

​ APP.test.js 项目根组件测试文件

​ index.css 项目全局样式表文件

​ index.js 项目启动文件

package.json 项目依赖配置文件

运行流程

/public/index.html

/src/index.js

/src/APP.js

三、语法

1.jsx的使用

如果要在页面结构中创建一个层级比较深的虚拟DOM结构的话,代码如下:

ReactDOM.render(
     React.createElement('div',null,
         React.createElement('h1',{id:'myh1'},'这是一个h1标题'),
         React.createElement('p',null,
             React.createElement('a',{},
                 React.createElement('span',{},'')
             )
         )
     ),
     document.getElementById('box')
 )

如果虚拟DOM层级比较深/复杂,使用createElement方法就不合适了,所以要使用jsx。

jsx是js和xml的结合,是Facebook为react框架开发的一套语法糖

语法糖:糖衣语法,计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,而是更加方便程序员使用

jsx是一种js语法的扩展,允许js和html进行混写

(1)基本使用

引入babel核心库

<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

(2)修改script的type属性

<script type="text/babel"></script>

(3)创建虚拟DOM

let el = <div> 
    <h1 id="qaz">这是一个虚拟DOM-h1</h1>
	<p>p标签哦</p>
</div>

(4)渲染虚拟DOM到挂载点

ReactDOM.render(el,document.getElementById('box'))

2.其他注意事项

(1)Adjacent JSX elements must be wrapped in an enclosing tag

虚拟DOM中也只能有一个根标签

(2) Invalid DOM property class. Did you mean className

在react的jsx语法中,class是一个类的关键词,所以标签的class关键词要改成className

(3)babel.min.js:7 Uncaught SyntaxError: Inline Babel script: Unterminated JSX contents

在jsx中所有的标签,都要有闭合标签,比如input br img hr 要写成

(4)react-dom.development.js:82 Warning: Invalid DOM property for. Did you mean htmlFor

在react的jsx语法中,for是一个循环的关键词,所以for属性要改成htmlFor

示例代码

<body>
    <div id="box"></div>
    <script type="text/babel">
        let el = <div>
            <h1 className="111">标题1</h1>
            <h2>111</h2>
            <label htmlFor="name"></label>
            <input type="text" id="name"/>
        </div>
    ReactDOM.render(el,document.getElementById('box'))
    </script>
</body>

3.数据类型解析

react的jsx语法中,当<就会解析成html语法

​ 当遇到{开始就会解析成js语法

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jsx解析数据类型</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
    <div id="app"></div>
    <script type="text/babel">
        let name = '小飞'
        let islogin = false//布尔值不要直接进行输出,可以做判断使用
        let obj = { name : '中公优就业' }//对象不要直接输出,可以读取对象中的某个key属性
        //数组元素如果是非对象类型,可以直接输出,但是对象不能直接输出
        let arr = [
            {
                name : '小飞',
                age:18
            },
            {
                name : '小李',
                age:19
            }
        ]
        function randomStr(){
            return Math.random()
        }
        let el = (
            <div>
                { /* 字符串 */ }
                <p>姓名:{ name }</p>
                { /* 数字 */ }
                <p>{ 100 * 90 }</p>
                {/* 布尔值 */}
                <p> { islogin ? '已登录' : '未登录' } </p>
                {/* 对象 */}
                <p>{ obj.name }</p>
                {/* 数组 */}
                <p>{ /*arr*/ }</p>
                { /* 函数 */ }
                <p> { randomStr() } </p>
            </div>
        )
        let app = document.querySelector('#app')
        ReactDOM.render(el,app)

    </script>
</body>
</html>

4.属性绑定

{ /* 属性绑定 */ }
<p style={{ color:'#f00',fontSize:30 }}> { randomStr() } </p>
<img src={img} />

5.条件判断

//条件判断
let btn = (function(){
    if(islogin){
    	return <a className="login1" href="#">已登录</a>
    }else{
    	return <a className="login2" href="http://www.ujiuye.com">请登录</a>
    }
})()
let el = (
   <div>
   		{btn}
   </div>
)

6.列表循环

let arr = [
    {
        name : '小飞',
        age:18
    },
    {
        name : '小李',
        age:19
    }
]
let el = (
   <div>
   		<ul>
        {
            arr.map((item,index)=>{
            return (
                <li key={ index }>
                    <p>姓名:{ item.name }</p>
                    <p>年龄:{ item.age }</p>
                </li>
            )
            })
        }
        </ul>
   </div>
)

四、组件

1.函数式组件

又叫做无状态组件/木偶组件

格式:

import React from 'react'
let Home = ()=>{
    return (
        <div>home</div>
      )
}
export default Home;

在APP.js中引入

import Home from './home'

在标签中使用组件:

export default () => {
  return (
    <div>脚手架根组件
      <Home></Home>
    </div>
  )
}

注意:

在react中,引入组件时,组件名称首字母必须大写

2.类组件

又叫做状态组件/业务逻辑组件

在react中,更多的使用组件是类组件,因为类组件可以有状态、生命周期和逻辑方法函数

格式一

import React from 'react'
export default class 类名 extends React.Component{
    render(){
        return(
            <div>...</div>
        )
    }
}

格式二:

import React,{Component} from 'react'
export default class 类名 extends Component{
    render(){
        return(
            <div>...</div>
        )
    }
}

类组件的类名一般都首字母大写,表示类名

五、状态机

1.什么是状态

状态是一个核心概念,允许构建可以存储数据的react组件,并根据数据变化自动更新视图页面

2.使用状态

(1)在构造函数中定义状态

 constructor() {
        super();
        this.state = {
            time: new Date().toLocaleTimeString()
        }
    }

(2)直接在类中定义状态

state = {
     time: new Date().toLocaleTimeString()
}

3.更新状态

(1)直接修改状态

利用setState方法,来对状态进行修改(不要使用直接赋值的方式,页面不会变化)

直接对this.state进行赋值,state中的数据会变化,但是页面上的state引用出不会发生变化,因为页面已经渲染了

setState被调用时,react将数据和当前的state进行合并,然后调用render方法更新页面

export default class Clock extends Component {
    state = {
            time: new Date().toLocaleTimeString()
    }
    start(){
        setInterval(()=>{
            // setState是react中内置的更改状态的方法,他会自动的重现调用render方法,来重新渲染页面
            this.setState({
                time:new Date().toLocaleTimeString()
            })
        },1000)
    }
//组件挂载完成
    componentDidMount(){
        this.start();
    }
    render() {
        return (
            <div>
                <h1>clock</h1>
                <p>当前时间:{this.state.time}</p>
                <p>{this.name}</p>
            </div>
        )
    }
}

setSate方法是一个**异步操作,**在没有执行完数据的和并和页面渲染时,是无法直接获取到更新之后的数据的,如果想要获取在调用setState之后获取到最新的数据,可以在回调函数里对数据进行操作

export default class Home extends Component {
  state = {num:1}
  changeNum(){
    let n = this.state.num;
    n++;
    // setState参数
    // 第一个参数是要合并到原来状态的数据
    // 第二个参数是一个回调函数(可选),用来获取最新的数据
    this.setState({num:n,}, ()=>{
      console.log('最新',this.state.num);//在数据合并完成后,页面徐然完成后才执行
    })
    console.log('非最新',this.state.num)//此时获取到的数据不是最新的
  }
  render(){
    return (
        <div>
          <h2>{this.state.num}</h2>
          <button onClick={()=>{this.changeNum()}}>增加数量</button>
        </div>
      )
  }
}

六、事件

1.事件绑定

(1)ES5方式

handler1(){
        console.log('1111');
    }
    render(){
        return (
            <div>事件页面
                <button onClick={this.handler1}>按钮</button>
            </div>
        )
    }

不要在jsx语法中给函数添加小括号,否则就会自动执行

无法获取到this指向,如果想要保持this指向,需要bind进行this绑定

(2)ES6方式

 <button onClick={()=>this.函数名称()}>es6</button>

ES6方式的事件绑定,可以在指定的函数中保持this指向

<button onClick={this.handler1.bind(this)}>es5-this</button>

此时,按钮板顶的事件对应的函数中就可以获取到this指向

2.事件对象

(1)ES5方式

<button onClick = {this.handler2}>ES5</button>

es5方式的事件绑定,可以在对应函数中获取到事件对象

 handler2(e){
        console.log('222');
        console.log(e)
    }

(2)ES6方式

<button onClick = {()=>{this.handler2()}}>ES6</button>

ES6中默认在对应的函数中无法获取到事件对象,除非显示的传递事件对象

<button onClick = {(形参)=>{this.handler2(形参)}}>ES6</button>

3.参数传递

(1)ES5方式

 <button onClick = {this.handler3.bind(this,要传递的数据[,])}>ES5</button>

方式1:仅仅传递参数

es5方式的事件绑定中,bind(this)不是参数,要传递的参数放到this的后面

 handler3(num){
        console.log(num);
  }

方式2:既传递参数又获取事件对象,默认最后一个形参是事件对象,无需在事件触发时进行传递

handler4(num,ev){
        console.log(num);
        console.log(ev);
 }

(2)ES6方式

方式1:仅仅传递参数

<button onClick = {()=>this.handler3(200)}>ES6-仅传参</button>

方式2:既传递参数又获取事件对象

<button onClick = {(e)=>this.handler4(200,e)}>ES6-参数和获取事件对象</button>

注意:形参和实参位置要对应起来

 handler4(num,ev){
        console.log(num);
        console.log(ev);
    }

七、模拟表单元素双向绑定

由于react中没有vue中的指令系统,所以想要实现表单元素双向绑定,就要结合事件绑定和状态机来实现。

需要给指定的表单元素添加value属性来设置初始值,同时需要给表单元素再设置一个onChange事件(不设置的话它就是一个只读属性, 无法进行修改), 在onChange中结合事件绑定和setState方法来实现状态的变化并且页面上的内容也进行更新。

import React, { Component } from 'react'
export default class Form extends Component {
    state = {
        name: '小王',
        phone:18
    }
    iptChange(e,t) {
        // console.log(e.target.value)//获取事件对象,标签中的value属性
        // this.setState({
        //     name: e.target.value
        // })
        let data = this.state;
        data[e.target.id] = e.target.value;//法1
        data[t] = e.target.value;//法2
        this.setState(data);
    }
    submit() {
        console.log(this.state)
    }
    render() {
        return (
            <div>
                <div>
                <label htmlFor="name">用户名</label>
                <input id="name" type="text" value={this.state.name} onChange={(e) => { this.iptChange(e,'name')}} autoComplete="off"></input>
                <p>{this.state.name}</p>
                </div>
                <div>
                <label htmlFor="phone">手机号</label>
                <input id="phone" type="text" value={this.state.phone} onChange={(e) => { this.iptChange(e,'phone') }}></input>
                <p>{this.state.phone}</p>
                </div>
                <br></br>
                <button onClick={() => { this.submit() }}>增加数量</button>
            </div>
        )
    }
}

八、react组件通信

1.父子组件

使用自定义属性和props

react中父子组件通信和vue中的父子组件通信非常相似,都是在父组件使用子组件时,通过自定义属性进行传递数据,然后在子组件中通过props来接收数据

示例代码:

父组件:

import React,{Component} from 'react'
import Item from './item'
export default class Children extends Component{
  arr=[111,222,333]
    render(){
        return (
            <div>
              <h2>home组件</h2>
              {
                this.arr.map((d,i)=>{
                  return(
                  <Item key={i} msg={d} ind={i}></Item>
                  )
                })
              }
            </div>
          )
    }
}

子组件

类组件方式:

import React,{Component} from 'react'
export default class Item extends Component{
    render(){
        return (
            <div>
              <h2>{this.props.msg}</h2> 
              <h2>{this.props.ind}</h2> 
            </div>
          )
    }
}

函数式组件:

函数式组件默认,没有this,所以不能通过this.props来接收数据,但是他可以通过函数参数的方式来接收父组件传递的数据

import React from 'react'
export default (props)=>{
    return(
        <div >
             <p className="title">{props.obj.title}</p>
              <p className="zan">{props.obj.age}</p>
        </div>
    )
}

2.子父组件

在react中,子父组件通信用自定义函数来实现

父组件

addZan(num){
        this.arr[num].zanNum++;
        this.setState({})//调用setState,用来重新渲染页面
    } 
<Child key={index} obj={item} ind={index} addParent={(n)=>this.addZan(n)} ></Child>

子组件

import React, { Component } from 'react'
export default class child extends Component {
    zan(n){
        this.props.addParent(n)//通过props来触发父组件的自典故事件,同时传递参数
    }
    render() {
        return (
            <div className="qaz">
                <p className="title">{this.props.obj.title}</p>
                <p className="zan">点赞数量:{this.props.obj.zanNum}
                    <button onClick={()=>this.zan(this.props.ind)}>点赞</button>
                </p>
            </div>
        )
    }
}

3.非父子组件

公用容器和 e m i t , emit, emit,on

(1)创建一个公用容器

/src/bus.js

// 创建一个公用容器,实现非父子组件通信
const EventEmitter = require('events')
const myEvent = new EventEmitter();
export default myEvent;

在src/index.js中把公用容器引入并挂载到Component原型上,写最后

import React,{Component} from 'react';
import bus from './bus'//引入公用容器
Component.prototype.$bus = bus;

(2)在A组件中发送数据

sentData(){
    this.$bus.emit('事件名','要传递的数据')
  }

(3)在其他组件的构造函数上来通过$on,来接收数据

constructor(){
        super()
        this.$bus.on('事件名',(数据)=>{
        })
    }

九、生命周期

只有类组件才有生命周期,函数组件没有生命周期

1.页面渲染期

(1)construcr 构造函数,在所有函数执行之前它先执行

(2)UNSAFE_componentWillMount 组件将要挂载

(3)render 页面渲染

(4)componentDidMount 组件挂载完成【*】

2.页面更新期

(1)shouldComponentUpdate 组件是否要更新数据,需要返回一个布尔值,为true表示要更新数据,为false时,表示不要更新数据

(2)UNSAFE_componentWillUpdate 组件需要更新数据时,组件会触发将要更新数据的钩子函数

(3)render 页面渲染

(4)componentDidUpdate 组件更新完成

(5)UNSAFE_componentWillReceiveProps 父组件传递的数据变化时,子组件中会自动触发此钩子函数,

但是更多的应用场景是父子组件通信时,父组件数据变化

3.页面销毁期

componentWillUnmount 组件将要被销毁

十、DOM节点操作

1.字符串

import React, { Component } from 'react'
export default class Index extends Component {
    render() {
        return (
            <div>
                <h1 ref="myh1">index</h1>
            </div>
        )
    }
    componentDidMount(){
        this.refs.myh1.innerHTML = 'refs直接赋值'
    }
}

2.回调函数

在标签或组件上的ref属性,除了可以是字符串以外,还可以写成一个回调函数

回调函数会自动触发,并且不会在this.refs中存在

ref+回调函数也可以实现父子组件通信

如果需要子组件在展示前就获取到父组件传递的最新数据,可以考虑说那个回调函数

import React, { Component } from 'react'
import Child from './child'
export default class Index extends Component {
    render() {
        return (
            <div>
                <h1 ref="myh1">index</h1>
                <Child ref={(e)=>this.changeChild(e)}></Child>
            </div>
        )
    }
    changeChild(e){
        console.log(e)//此时e就是子组件本身
        e.setState({
            num:500
        })
    }
}

3.createRef

createRef是react给提供的一个API方法,利用此方法也可以实现通过ref来获取DOM元素或者子组件,从而实现DOM操作或者组件传值操作

import React, { Component } from 'react'
import Child from './child'
export default class Index extends Component {
    constructor(){
        super()
        this.chileEl = React.createRef()//创建空的ref对象
    }
    render() {
        return (
            <div>
                <h1 ref="myh1">index</h1>
                <Child ref={this.chileEl}></Child>
            </div>
        )
    }
    componentDidMount(){
        console.log(this.chileEl.current)//此时this.chileEl.current就是子组件本身
        this.chileEl.current.state.num =222
    }
}

十一、state和props

state一般是给组件本身设置的初始数据(组件可以对数据进行任意操作)

props一般是子组件接收父组件传递的数据(子组件无法修改props)

如果想要根据父组件传递的数据来改变子组件本身的状态时,可以使用props和state混用的方式。

如果子组件的数据依赖于父组件传递的数据,当子组件的数据方式变化时,子组件无法获取带父组件最新的数据

父组件

import React, { Component } from 'react'
import Item from './item'
export default class Home extends Component {
    state={
        num:100
    }
    componentDidMount(){
        this.setState({
            num:50
        })
    }
    render() {
        return (
            <div>
                <h1>home---{this.state.num}</h1>
                <Item num={this.state.num}></Item>
            </div>
        )
    }
}

子组件

import React, { Component } from 'react'
export default class Item extends Component {
    state={
        sum:200
    }
    componentDidMount(){
        // 此时子组件获取到的父组件传递的数据是100,而不是父组件更新之后的50
        this.setState({
            sum:this.state.sum+this.props.num
        })
    }
    render() {
        return (
            <div>
                <h1>item</h1>
                <p>结果是:{this.state.sum}</p>
            </div>
        )
    }
}

setState方法的参数,可以是一个对象+一个回调函数,还可以是一个函数

这个函数中的第一个参数是当前页面组件的初始状态,第二个参数是父组件传递过来的最新数据

子组件:

import React, { Component } from 'react'
export default class Item extends Component {
    state={
        sum:200
    }
    componentDidMount(){
        // 此时子组件获取到的父组件传递的数据是100,而不是父组件更新之后的50
        // this.setState({
        //     sum:this.state.sum+this.props.num
        // })
        this.setState(function(state,props){
            console.log(state);//当前组件状态
            console.log(props);//父组件中的修改之后的数据
            return {
                sum:state.sum+props.num
            }
        })
    }
    render() {
        return (
            <div>
                <h1>item</h1>
                <p>结果是:{this.state.sum}</p>
            </div>
        )
    }
}

十二、react路由

react也是SPA 应用

1.安装

npm i react-router-dom --save

2.引入

/src/index.js中,渲染虚拟DOM之前,引入路由模块

import {BrowserRouter} from 'react-router-dom'//注意顺序
ReactDOM.render(
  <BrowserRouter>
  <App />
  </BrowserRouter>
  ,document.getElementById('root')
);

3.基本使用

创建几个页面组件

然后在APP.js中引入创建好的页面

引入路由中内置的组件Switch、Router,用来设置路由出口和具体的路由规则

// 引入路由内置组件
import Home from './July/home'
import Mine from './July/mine'
import Order from './July/order'
import {Switch,Route} from 'react-router-dom'
export default () => {
  return (
    <div>
      {/* 相当于vue-touter-view */}
      <Switch>
        {/* ROUTE是具体的路由规则,需要设置路由关键词和一直对应的组件 */}
        <Route path="/home" component={Home}></Route>
        <Route path="/mine" component={Mine}></Route>
        <Route path="/order" component={Order}></Route>
      </Switch>
      )
  }

4.路由导航

(1)内置组件

相同:这两个内置标签都是在页面上生成超链接a标签,都需要设置to属性来告知跳转的链接地址

不同:link标签仅在页面上生成a标签和href属性,NavLink会在页面上生成a标签的同时会根据当前访问的路由地址来动态的设置激活状态

①Link

 <Link to='/index/student'>学生管理</Link>
 <Link to='/index/course'>课程管理</Link>

②NavLink

<NavLink to="/index/student">学生管理</NavLink>
<NavLink to='/index/course'>课程管理</NavLink>

(2)编程式导航

如果某个组件不是路由规则的页面组件,而是某个路由规则页面组件的组成部分时,想要使用react-router-dom的路由相关信息时,无法直接使用,想要解决这个问题,需要使用withRouter

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
class Header extends Component {
    render() {
        return (
            <div className="header">
                <h1>Header</h1>
                <button onClick={()=>{this.logout()}}>退出登录</button>
            </div>
        )
    }
}
export default withRouter(Header)

此时,被路由规则包含的组成部分组件就可以使用react-router-dom相关的信息了

在路由实例的history中有以下方法可以实现页面跳转

push

replace

go(-1)

goback

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
class Header extends Component {
    logout(){
        // this.props.history.push('/login')
        // this.props.history.replace('/login')
        // this.props.history.go(-1)
        this.props.history.goBack()
    }
    render() {
        return (
            <div className="header">
                <p>Header</p>
                <button onClick={()=>{this.logout()}}>退出登录</button>
            </div>
        )
    }
}
export default withRouter(Header)

5.路由嵌套

需要在哪个页面展示不用的子级页面,就自己在这个页面引入Switch、Router的内置路由组件,并定义具体的路由规格

/src/components/view/right.js

import React, { Component } from 'react'
import Student from '../pages/student'
import Course from '../pages/course'
import {Switch,Route} from 'react-router-dom'

export default class Right extends Component {
    render() {
        return (
            <div className="right">
                <Switch>
                    <Route path="/index/student" component={Student}></Route>
                    <Route path="/index/course" component={Course}></Route>
                </Switch>
            </div>
        )
    }
}

6.重定向

App.js

在react-router-dom包中引入Redirect组件

import {Switch,Route,Redirect} from 'react-router-dom'

在Switch中定义一个重定向的路由

 <Switch>
      <Route path="/index" component={Index}></Route>
      <Route path="/login" component={Login}></Route>
      <Redirect path="*" to="/index"></Redirect>
 </Switch>

二级路由重定向,特定的组件

<Redirect path="*" to="/index/course"></Redirect>

7.路由传参

1.动态路由

参数固定

关键词/:参数名

长地址要在短地址前面或者路由规则设置exact

<Route exact path="/index/student" component={Student}></Route>

exact属性用来标记路由规则精确匹配

(1)创建页面组件

(2)配置动态路由规则

<Route path="/index/student/:id" component={StudentInfo}></Route>

(3)在列表页面进行跳转并拼接参数

  <button className="btn btn-primary" onClick={()=>this.props.history.push('/index/student/'+item.id)}>编辑</button>

(4)获取动态路由参数地址

<p>学生编号:{this.props.match.params.id}</p>

2.查询参数

参数数量不固定

(1)定义一个固定的路由规则

<Route path="/index/student/info" component={StudentInfo}></Route>

页面跳转

toInfo(obj){
        this.props.history.push({
            pathname:'/index/student/info',
            search:`id=${obj.id}&name=${obj.name}`
        })
    }

3.获取查询参数

查询参数处理:

插件

npm i querystring --save

使用

import querystring from 'querystring'
let search = this.props.location.search.substr(1);//获取location中的search参数,并去掉问号
let obj = querystring.parse(search);

8.路由模式

在react-router-dom中Browser和history模式,HashRouter是hash模式

在src/index.js中

// import {BrowserRouter} from 'react-router-dom'
import {HashRouter} from 'react-router-dom'
ReactDOM.render(
  // <React.StrictMode>
  // <BrowserRouter>
  <HashRouter>
    <App />
  </HashRouter>
  // </BrowserRouter>

更换为hash模式,就能看见#

十三、状态管理

1.flux

单向数据流动,可预测的

1.流程

view 视图触发action

action 通知dispatcher(派发器)

dispatcher 通知仓库改变状态

store 仓库改变完成后通知视图(view)

2.安装

npm i flux --save

(1)创建仓库

在src下创建一个文件夹

定义初始数据

/src/store/index.js

let state = {
    name:'flux',
     age:16
}

(2)暴露数据

export default {state,dispatcher}

(3)普通页面组件使用仓库中的数据

import React, { Component } from 'react'
// 引入flux仓库
import store from '../store/index'
export default class Home extends Component {
    render() {
        return (
            <div className="main">
                <p>{store.state.name}</p>
            </div>
        )
    }
}

此时,多个页面组件都可以引入仓库,并使用仓库中的数据

(4)在页面发起动作,改变仓库中的数据

在仓库中通过派发器来注册改变仓库状态的具体方法

/src/store/index.js

// 引入派发器
import {Dispatcher} from 'flux'
// 引入事件监听器
import EventEmitter from 'events'
class State extends EventEmitter{
    name='flux'
    age=16
}
let state = new State()
// 实例化派发器
const dispatcher = new Dispatcher();
// 通过派发器来派发具体的数据修改操作
dispatcher.register(action=>{
    switch(action.type){
        case 'changeName':
            state.name = action.params;
            break;
        case 'changeAge':
            state.age = action.params;
            break;
        default:
            break;
    }
    state.emit('change')//触发事件
})

在普通页面组件中的挂载完成钩子函数中来监听数据的变化

componentDidMount(){
        store.state.on('change',()=>{
            this.setState({});//重新渲染页面
        })
    }

此时仓库中的数据变化了,页面也会跟着从小渲染

在普通页面中通过仓库派发任务

store.dispatcher.dispatch({
type;'changeName',
params:'hello'
})

2.redux

Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers)。

核心概念:

  • actions

  • store

  • reducers

    Redux三个原则

    1.单一数据源

    2.state是只读的

    3.使用纯函数修改state

安装

npm i redux --save

(3)基本语法

流程:

引入createStore

定义初始状态

定义纯函数

定义仓库

示例代码:

①引入createStore

/src/store/index.js

// 引入createStore
import { createStore } from 'redux'

②定义初始状态

const initalState = {
    name: '1',
    age: 2
}

③定义纯函数

state 上一次修改完成后的状态

action是组件dispatch的动作

reducer一定要返回一个新的state,否则就检测不到state的变化

function reducer(state = initalState, action) {
    switch (action.type) {
        case 'changeName':
            return{
                ...state,
                name:action.params
            }
        case 'changeAge':
            return{
                ...state,
                age:action.params
            }
        default:
            return state;//一定要返回
    }
}

④创建仓库

// 创建仓库
const store = createStore(reducer)
export default store;

⑤页面组件使用状态

//引入仓库
import store from '../storee/index'

使用仓库中的数据

<p>仓库中的name{store.getState().name}</p>

⑥改变状态

changeName(){
        console.log(store)
        store.dispatch({
            type:'changeName',
            params:'小芳'
        })
    }

⑦使用订阅者实现数据变化,页面就变化

 componentDidMount(){
        // 仓库数据变化,想要重新渲染页面,就要添加订阅者
        this.unsubscribe = store.subscribe(()=>{
            this.setState({})
        })
    }
    componentWillUnmount(){
        this.unsubscribe();//在销毁之前,取消订阅
    }

十四、ui库

1.elemet-react

npm i element-react --save

主题包

npm install element-theme-default --save

2.Ant Design

官网:https://ant.design/index-cn

(1)安装

npm install antd --save

(2)引入

/src/index.js

import 'antd/dist/antd.css'

(3)使用

antdimport {Button} from ''
<Botton type="text">按钮</Button>

十五、音乐案例

1.接口准备

2.安装依赖

npm i 

3.运行项目

npm app.js

4.发起网络请求

jquery、fetch、axios

(1)axios

安装

npm i axios --save

引入

import axios from 'axios'

使用:

axios.get('目标地址').then(res=>{})

配置代理

①package.json

 "proxy":"目标域名地址:端口号"

重启react项目,就可以进行代理转发

在package.json中添加proxy后,当访问一个react项目不存在的路由地址时,会自动转发到proxy对应的目标域名地址上

弊端:只能配置一个,需要寻找一遍路由地址再转发

②使用插件

http-proxy-middleware

安装

npm i http-proxy-middleware --save

在/src下创建一个setupProxy.js文件【文件名不能写错】

const proxy = require('http-proxy-middleware');
module.exports = function(app){
    app.use(proxy.createProxyMiddleware(
        "/关键词",{
            target:'目标域名地址',
            changeOrigin:true,
            pathRewrite:{
                "^/关键词":''
            }
        }
    ))
}

重启项目就可以进行代理的转发

常见错误

1.Warning:The tag is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter

在React中组件名称需要大写

2.Warning: Failed prop type: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.

表单元素设置value属性之后,默认就是只读属性,如果想要让他变成可以修改的控件,需要设置onChange事件,让这个控件变成受控组件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值