浅记React

    技术2025-04-20  10

    一、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事件,让这个控件变成受控组件

    Processed: 0.008, SQL: 9