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