构建用户界面的JavaScript库,主要用于构建UI界面
1、声明式
的设计
2、高效,采用虚拟DOM
来实现DOM的渲染,最大限度的减少DOM的操作。
3、灵活,跟其他库灵活搭配使用。
4、JSX
,俗称JS里面写HTML,JavaScript语法的扩展。
5、组件化,模块化
。代码容易复用,2016年之前大型项目非常喜欢react
单向数据流。没有实现数据的双向绑定。数据-》视图-》事件-》数据
cnpm install -g create-react-app
create-react-app 01reactapp(项目名称可以自定义)
macOS不行就npm init react-app my-app
npm init -f
接着安装依赖
npm install formidable --save
-f表示force的意思,不加这个,npm会让你输入一堆信息,比如name、version之类,如果只是做做实验小demo,直接-f,npm帮你初始化package.json,并填充各种默认值,省事。
–save表示将安装的包加入依赖列表的意思,可以看下package.json 里的dependencies字段。后面再运行 npm install,就会把所有依赖安装下来。如果不加–save,什么都不会安装。
顺便如果速度慢可以设置为淘宝 npm 镜像
npm config set registry https://registry.npm.taobao.org
//实现页面时刻的显示
function clock(){
let time = new Date().toLocaleTimeString()
let element = (
<div>
<h1>现在的时间是{time} </h1>
<h2>这是副标题</h2>
</div>
)
let root = document.querySelector('#root');
//getbyid...也可以
ReactDOM.render(element,root)
}
clock()
setInterval(clock,1000)
函数式渲染
function Clock(props){
return (
<div>
<h1>现在的时间是{props.date.toLocaleTimeString()} </h1>
<h2>这是函数式组件开发</h2>
</div>
)
}
function run(){
ReactDOM.render(
<Clock date={new Date()} />,
document.querySelector('#root')
)
}
setInterval(run,1000)
优点:
1、JSX执行更快,编译为JavaScript代码时进行优化
2、类型更安全,编译过程如果出错就不能编译,及时发现错误
3、JSX编写模板更加简单快速。(不要跟VUE比)
注意:
1、JSX必须要有根节点。
2、正常的普通HTML元素要小写
。如果是大写,默认认为是组件
。
1、由HTML元素构成
2、中间如果需要插入变量用{}
3、{}中间可以使用表达式
4、{}中间表达式中可以使用JSX对象
5、属性和html内容一样都是用{}来插入内容
import React from 'react';
import ReactDOM from 'react-dom';
import './App.css'
/* ./App.css
.bgRed{
background-color: red;
}
*/
let time = new Date().toLocaleTimeString()
let str = '当前时间是:'
let element = (
<div>
<h1>helloworld</h1>
<h2>{str+time}</h2>
</div>
)
console.log(element)
let man = '发热';
let element2 = (
<div>
<h1>今天是否隔离</h1>
<h2>{man=="发热"?<button>隔离</button>:"躺床上"}</h2>
</div>
)
//let man = '发热';
let element4 = (
<div>
<span>横着躺</span>
<span>竖着躺</span>
</div>
)
man = '正常'
let element3 = (
<div>
<h1>今天是否隔离</h1>
<h2>{man=="发热"?<button>隔离</button>:element4}</h2>
</div>
)
let color = 'bgRed'
let logo = 'https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png'
//HTML的样式类名要写className,因为class在js当中是关键词
let element5 = (
<div className={color}>
<img src={logo} />
红色的背景颜色
</div>
)
ReactDOM.render(
element5,
document.getElementById('root')
)
import React from 'react';
import ReactDOM from 'react-dom';
import './04style.css';
//JSX中设置style
let exampleStyle = {
background:"skyblue",
borderBottom:"4px solid red",
'background-image':"url(https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png)"
}
let element = (
<div>
<h1 style={exampleStyle}>helloworld</h1>
</div>
)
let classStr = "redBg"
let element2 = (
<div>
<h1 className={"abc "+classStr}>helloworld</h1>
</div>
)
let classStr2 = ['abc2','redBg2'].join(" ")
let element3 = (
<div>
{/* 只能这样写注释 */}
{/* 这里实现了name和style一起装入,同时style还有个导入文件来渲染 */}
<h1 className={classStr2} style={exampleStyle}>helloworld</h1>
</div>
)
ReactDOM.render(
element3,
document.querySelector('#root')
)
helloworld
helloworld
helloworld
let classStr2 = ['abc2','redBg2']
let element3 = (
<div>
{/* 这里写注释 */}
<h1 className={classStr2}>helloworld</h1>
</div>
)
返回的DOM为helloworld
如果要合并的话就用 ['abc2','redBg2'].join(' ')
返回abc2 redBg2
,然后再接受类选择器的渲染:
import './04style.css';
就行.abc2{
width: 200px;
height: 200px;
}
.redBg2{
background: orangered;
}
import React from 'react';
import ReactDOM from 'react-dom';
import './04style.css';
//函数式组件
function Childcom(props){
console.log(props)
let title = <h2>我是副标题</h2>
let weather = props.weather
//条件判断
let isGo = weather=='下雨' ?"不出门":"出门"
return (
<div>
<h1>函数式组件helloworld</h1>
{title}
<div>
是否出门?
<span>{isGo}</span>
</div>
</div>
)
}
//类组件定义
class HelloWorld extends React.Component{
render(){
console.log(this)
return (
<div>
<h1>类组件定义HELLOWORLD</h1>
<h1>hello:{this.props.name}</h1>
<Childcom weather={this.props.weather} />
</div>
)
}
}
// ReactDOM.render(
// ,
// document.querySelector('#root')
// )
ReactDOM.render(
<HelloWorld name="xx" weather="下雨" />,
document.querySelector('#root')
)
,
)直接返回DOM,不用render但是内部可以有{}相关的渲染,️state事件!!!React.Component
渲染render
,render内部渲染是获取props和state相关的操作,然后返回渲染后的DOM。class XxxXxx extends React.Component{
render(){
return (
<div>
<h1>类组件定义HELLOWORLD</h1>
<h1>hello:{this.props.name}</h1>
<Childcom weather={this.props.weather} />
</div>
)
}
}
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component{
constructor(props){
super(props)
//状态(数据)--》view
//构造函数初始化数据,将需要改变的数据初始化到state中
this.state = {
time:new Date().toLocaleTimeString()
}
//console.log(this.state.time)
}
render(){
//console.log("这是渲染函数")
//this.state.time = new Date().toLocaleTimeString();
return (
<div>
<h1>当前时间:{this.state.time}</h1>
</div>
)
}
//生命周期函数,组件渲染完成时的函数
componentDidMount(){
setInterval(()=>{
console.log(this.state.time)
//this.state.time = new Date().toLocaleTimeString(); //错误的方式
//切勿直接修改state数据,直接state重新渲染内容,需要使用setState
//通过this.setState修改完数据后,并不会立即修改DOM里面的内容,react会在,
//这个函数内容所有设置状态改变后,统一对比虚拟DOM对象,然后在统一修改,提升性能。
//小程序也是也是借鉴REACT状态管理操作
this.setState({
time:new Date().toLocaleTimeString()
})
},1000)
}
}
ReactDOM.render(
<Clock />,
document.querySelector('#root')
)
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time:new Date().toLocalTimeString()
}
//这里校验下有没有渲染成功
console.log(this.state.time)
}
render(){
return(
<div><h1>当前时间:{this.state.time}</h1></div>
)
}
}
ReactDOM.render(
<Clock />,
document.querySelector('#root')
)
setInterval(()=>{
ReactDOM.render(
<Clock />,
document.querySelector('#root')
)
console.log(new Date())
},1000)
在这里将会显示的是,第一次render的时间,和后面表示进入setInterval
计时器的时间,但是没有显示再次渲染的时间,因为在外部反复渲染同一个组件是不需要再次初始化的,只有内部state的事件导致数据改变了才重新渲染
。
解决方式:用我们的componentDidMount(){}来进行渲染后的后续操作
componentDidMount()
,渲染之后的操作都要用this.setState
哦~componentDidMount(){
setInterval(()=>{
console.log(this.state.time)
//this.state.time = new Date().toLocaleTimeString(); //错误的方式
//切勿直接修改state数据,直接state重新渲染内容,需要使用setState
//通过this.setState修改完数据后,并不会立即修改DOM里面的内容,react会在,
//这个函数内容所有设置状态改变后,统一对比虚拟DOM对象,然后在统一修改,提升性能。
//小程序也是也是借鉴REACT状态管理操作
this.setState({
time:new Date().toLocaleTimeString()
})
},1000)
}
import React from 'react';
import ReactDOM from 'react-dom';
import './Tab.css'
class Tab extends React.Component{
constructor(props){
super(props)
//设置状态、数据
this.state = {
c1:'content active',
c2:"content"
}
this.clickEvent = this.clickEvent.bind(this)
}
clickEvent(e){
console.log('clickEvent')
console.log(e.target.dataset.index)
//target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。
let index = e.target.dataset.index;
console.log(this)
if(index=='1'){
this.setState({
c1:'content active'
})
this.setState({
c2:"content"
})
}else{
this.setState({
c1:'content',
c2:"content active"
})
}
}
render(){
return (
<div>
<button data-index="1" onClick={this.clickEvent}>内容一</button>
<button data-index="2" onClick={this.clickEvent}>内容二</button>
<div className={this.state.c1}>
<h1>内容1</h1>
</div>
<div className={this.state.c2}>
<h1>内容2</h1>
</div>
</div>
)
}
}
ReactDOM.render(
<Tab />,
document.querySelector('#root')
)
this.clickEvent = this.clickEvent.bind(this)
的子元素数据传递import React from 'react';
import ReactDOM from 'react-dom';
import './01props.css'
//在父元素中使用state去控制子元素props的从而达到父元素数据传递给子元素
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
isActive:true
}
this.changeShow = this.changeShow.bind(this)
}
render(){
return (
<div>
<button onClick={this.changeShow}>控制子元素显示</button>
<ChildCom isActive={this.state.isActive} />
</div>
)
}
changeShow(){
this.setState({
isActive:!this.state.isActive
})
}
}
class ChildCom extends React.Component{
constructor(props){
super(props)
}
render(){
let strClass = null;
if(this.props.isActive){
strClass = ' active'
}else{
strClass = ""
}
strClass = this.props.isActive?" active":"";
return (
<div className={"content"+strClass}>
<h1>我是子元素</h1>
</div>
)
}
}
ReactDOM.render(
<ParentCom />,
document.querySelector("#root")
)
本节完成的是:按子元素渲染的按钮就可以改变父元素的数据。
,来传入父元素的setChildData
,然后通过childCom来修改传入数据,返回到父元素的render里面后又重新渲染。
import React from 'react';
import ReactDOM from 'react-dom'
//子传父
class ParentCom extends React.Component{
//构造函数 继承React.Component的props
constructor(props){
//props
super(props)
//state
this.state = {
childData:null //一开始是没有的
}
}
//state
setChildData = (data)=>{
this.setState({
childData:data
})
}
render(){
return(
<div>
{/* this.state.childData 才能获取数据 */}
<h1>子元素传递给父元素的数据{this.state.childData}</h1>
{/* 一开始是没有的,因为父元素的childData为null,但是后面用子元素给父元素 */}
<ChildCom setChildData = {this.setChildData}/>
</div>
)
}
}
class ChildCom extends React.Component{
constructor(props){
super(props)
//state
this.state = {
msg:"helloworld"
}
}
//state
//用箭头函数绑定当前childcom类的this
sendData = () => {
console.log(this.state.msg)//测试绑定是否成功
console.log(this.props)//测试子元素是否拿到父元素的this.setChildData
this.props.setChildData(this.state.msg)
}
//修改数据
setChildData = (data) =>{
this.setState({
childData:data
})
}
render(){
return(
<div>
{/* 点击就可以传递数据 */}
<button onClick={this.sendData}>传递helloworld给父元素</button>
{/* 或者可以用以下方式 */}
<button onClick={()=>this.props.setChildData(this.state.msg)}>传递helloworld给父元素</button>
</div>
)
}
}
ReactDOM.render(
<ParentCom />,
// document.getElementById('#root')
document.querySelector('#root')
)
import React from 'react';
import ReactDOM from 'react-dom';
class ParentCom extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div >
<form action="http://www.baidu.com">
<div className="child" >
<h1>helloworld</h1>
<button onClick={this.parentEvent}>提交</button>
</div>
</form>
{/* 使用ES6箭头函数传递多个参数 */}
<button onClick={(e)=>{this.parentEvent1('msg:helloworld',e)}}>提交</button>
{/* //不使用ES6箭头函数传递多个参数的方式 */}
<button onClick={function(e){this.parentEvent1('不使用es6,msg:helloworld',e)}.bind(this)}>提交</button>
</div>
)
}
parentEvent=(e)=>{
console.log(e.preventDefault)
e.preventDefault()
//e.preventDefault()
//return false; //但是react不行
}
parentEvent1 = (msg,e)=>{
console.log(msg)
console.log(e)
}
}
ReactDOM.render(
<ParentCom></ParentCom>,
document.querySelector("#root")
)
以上是原生阻止默认行为的方式,可以直接返回return false
;
React中,阻止默认必须e.preventDefault()
;
{/* 使用ES6箭头函数传递多个参数 */}
<button onClick={(e)=>{this.parentEvent1('msg:helloworld',e)}}>提交</button>
{/* //不使用ES6箭头函数(匿名函数)传递多个参数的方式,就要进行bind(this)的this绑定 */}
<button onClick={function(e){this.parentEvent1('不使用es6,msg:helloworld',e)}.bind(this)}>提交</button>
.bind(this)是绑定的时候使用的
.call是调用的时候使用的
.apply也是调用时候使用的
条件运算
然后返回要渲染的JSX对象import React from 'react';
import ReactDOM from 'react-dom';
function UserGreet(props){
return (<h1>欢迎登陆</h1>)
}
function UserLogin(props){
return (<h1>请先登录</h1>)
}
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
isLogin:true
}
}
render(){
if(this.state.isLogin){
return (<UserGreet></UserGreet>)
}else{
return (<UserLogin></UserLogin>)
}
}
}
ReactDOM.render(
<ParentCom></ParentCom>,
document.querySelector('#root')
)
render(){
let element = null;
if(this.state.isLogin){
element = <UserGreet></UserGreet>;
}else{
element = (<UserLogin></UserLogin>);
}
return (
<div>
<h1>这是头部</h1>
{element}
<h1>这是三元运算符的操作</h1>
{this.state.isLogin?<UserGreet></UserGreet>:<UserLogin></UserLogin>}
<h1>这是尾部</h1>
</div>
)
}
import React from 'react';
import ReactDOM from 'react-dom';
let arr = ["小明","小黑","小白"]
let arrHtml = [<li>小明</li>,<li>小黑</li>,<li>小白</li>];
class Welcome extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<ul>
{arrHtml}
</ul>
</div>
)
}
}
ReactDOM.render(
<Welcome></Welcome>,
document.querySelector('#root')
)
import React from 'react';
import ReactDOM from 'react-dom';
class Welcome extends React.Component{
constructor(props){
super(props)
this.state = {
list:[
{
title:"第一节 React事件",
content:"事件内容"
},
{
title:"第二节 React数据传递",
content:"数据传递内容",
},
{
title:"第三节 条件渲染",
content:"条件渲染内容",
}
]
}
}
render(){
let listArr = [];
for(let i=0;i<this.state.list.length;i++){
let item = (
<li>
<h3>{this.state.list[i].title}</h3>
<p>{this.state.list[i].content}</p>
</li>
)
listArr.push(item)
}
return (
<div>
<h1>
今天课程内容
</h1>
<ul>
{listArr}
<li>
<h3>这是标题</h3>
<p>内容</p>
</li>
</ul>
</div>
)
}
}
ReactDOM.render(
<Welcome></Welcome>,
document.querySelector('#root')
)
function ListItem(props){
return (
<li>
<h3>{props.index+1}:listItem:{props.data.title}</h3>
<p>{props.data.content}</p>
</li>
)
}
//动态的就用类
class ListItem2 extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<li onClick={(event)=>{this.clickEvent(
this.props.index,
this.props.data.title,
event
)}}>
<h3>{this.props.index+1}:listItem:{this.props.data.title}</h3>
<p>{this.props.data.content}</p>
</li>
)
}
clickEvent=(index,title,event)=>{
alert((index+1)+"-"+title)
}
}
this.state.list.map((item,index)
的值,因为要传值且是对类内部的操作所以要用箭头函数绑定。class Welcome extends React.Component{
constructor(props){
super(props)
this.state = {
list:[
{
title:"第一节 React事件",
content:"事件内容"
},
{
title:"第二节 React数据传递",
content:"数据传递内容",
},
{
title:"第三节 条件渲染",
content:"条件渲染内容",
}
]
}
}
render(){
let listArr = this.state.list.map((item,index)=>{
return (
<ListItem2 key={index} data={item} index={index}></ListItem2>
)
})
return (
<div>
<h1>
今天课程内容
</h1>
<ul>
{listArr}
<li>
<h3>这是标题</h3>
<p>内容</p>
</li>
</ul>
<h1>复杂没有用组件完成列表</h1>
<ul>
{
this.state.list.map((item,index)=>{
return (
<li key={index} onClick={(event)=>{this.clickFn(index,item.title,event)}}>
<h3>{index+1}-复杂-{item.title}</h3>
<p>{item.content}</p>
</li>
)
})
}
</ul>
</div>
)
}
clickFn=(index,title,event)=>{
alert((index+1)+"-clickFn-"+title)
}
}
ReactDOM.render(
<Welcome></Welcome>,
document.querySelector('#root')
)
//要先 npm install axios
//然后 node spider.js 来运行
let axios = require('axios');
(async function(){
let httpUrl = 'https://c.m.163.com/ug/api/wuhan/app/index/feiyan-data-list?t='
let res = await axios.get(httpUrl+new Date().getTime())
console.log(res.data)
})()
这里获取的json数据格式如下
"data":{"list":[{"name":"酉阳县","province":"重庆","confirm":1,"suspect":null,"heal":null,"dead":null,"page":1},.......
定义数据对象:
因为是对于每个省份的相关数据的操作,所以定义一个省份对象,包含data里面的数据类型。取数据并加工
然后进行forEach循环,内部用箭头函数中的括号值获取省份对象的对应位置的值,然后进行对象判空的初始化,不空就对对象进行数据操作。定义到list数组,并插入数据
然后通过循环中的key挨个push json中的对象到list数组里,再进行sort排序(注意函数内部设的参数的值a
this.props.list.map((item,index)=〉{return()}
的渲染import React from 'react';
import ReactDOM from 'react-dom';
import jsonData from './feiyan.json'
import './06style.css'
/* './06style.css'
ul,li{
list-style: none;
margin: 0;
padding: 5px 5px;
}
li{
display: flex;
}
span{
flex: 1;
}
li:nth-child(odd){
background-color: #efefef;
}
*/
console.log(jsonData)
//省份对象 统计相关数据
let provincesObj = {
// "广东省":{
// confirm:0,
// suspect:0,
// heal:0,
// deal:0,
// }
}
//对列表进行forEach循环
jsonData.data.list.forEach((item,i) => {
//判断province有没有值
if(provincesObj[item.province]==undefined){
provincesObj[item.province] = {
confirm:0,
heal:0,
dead:0,
}
}
//判断item是不是none
item.confirm = item.confirm?item.confirm:0;
item.heal = item.heal?item.heal:0;
item.dead = item.dead?item.dead:0;
provincesObj[item.province] = {
confirm:provincesObj[item.province].confirm+item.confirm,
heal:provincesObj[item.province].heal+item.heal,
dead:provincesObj[item.province].dead+item.dead
}
});
//得到list
let provinceList = []
for (const key in provincesObj) {
provincesObj[key].province = key;
provinceList.push(provincesObj[key])
}
console.log(provincesObj)
console.log(provinceList)
//从大到小排序
let provinceListSort = provinceList.sort((a,b)=>{
if(a.confirm<b.confirm){
return 1;
}else{
return -1;
}
})
console.log(provinceListSort)
class Bili extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<h1>中国病例</h1>
<ul>
<li>
<span>地区</span>
<span>确诊</span>
<span>死亡</span>
<span>治愈</span>
</li>
{
this.props.list.map((item,index)=>{
return (
<li>
<span>{item.province}</span>
<span>{item.confirm}</span>
<span>{item.dead}</span>
<span>{item.heal}</span>
</li>
)
})
}
</ul>
</div>
)
}
}
ReactDOM.render(
<Bili list={provinceListSort}></Bili>,
document.querySelector('#root')
)
生命周期即是组件从实例化到渲染到最终从页面中销毁,整个过程就是生命周期,在这生命周期中,我们有许多可以调用的事件,也俗称为钩子函数
生命周期的3个状态:
生命周期中的钩子函数(方法,事件)
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
class ComLife extends Component{
constructor(props){
super(props)//调用继承Component的构造函数
this.state = {
msg:"hello world"
}
console.log("constructor构造函数")
}
componentWillMount(){
console.log("componentWillMount组件将要渲染")
}
componentDidMount(){
console.log("componentDidMount组件渲染完毕")
}
componentWillReceiveProps(){
console.log("componentWillReceiveProps组件将要接收新的state和props")
}
shouldComponentUpdate(){
//如果希望更新,就返回为真,不希望更新就返回为false
console.log("进行判断是否要更新内容")
console.log(this.state.msg)
if(this.state.msg==="hello world"){
console.log(true)
return true
}else{
console.log(false)
return false
}
}
componentWillUpdate(){
console.log('componentWillUpdate组件将要更新')
}
componentDidUpdate(){
console.log('componentDidUpdate组件更新完毕')
}
componentWillUnmount(){
console.log('componentWillUnmount移除')
}
render(){
console.log('render渲染函数')
return (
<div>
<h1>{this.state.msg}</h1>
<button onClick={this.changeMsg}>组件更新</button>
</div>
)
}
changeMsg=()=>{
this.setState({
msg:"hello 老陈"
})
}
}
class ParentCom extends Component{
constructor(props){
super(props)
this.state = {
isShow:true
}
}
render(){
if(this.state.isShow){
return (
<div>
<button onClick={this.removeCom}>移除comlife</button>
<ComLife></ComLife>
</div>
)
}else{
return <h1>将comlife已移除</h1>
}
}
removeCom=()=>{
this.setState({
isShow:false
})
}
}
ReactDOM.render(
<ParentCom></ParentCom>,
document.querySelector('#root')
)
上述代码逻辑:
首先定义一个ParentCom类组件,进行原始页面的渲染,同时要为了验证移除和更新功能:
移除:
通过点击事件设置
state中isShow
的值,因为state改变了所以parent要重新渲染,然后就不用子组件ComLife了,子组件执行componentWillUnmount移除
操作。
更新操作是写到子组件里面的,点击事件进行changeMsg操作,,
进行更新前的虚拟DOM比较shouldComponentUpdate()
,如果返回的true就然后进行更新,
然后componentWillUpdate
组件将要更新
render
渲染函数
componentDidUpdate
组件更新完毕
效果如下:
constructor构造函数
index.js:17 componentWillMount组件将要渲染
index.js:48 render渲染函数
componentDidMount组件渲染完毕
componentWillUnmount移除
进行判断是否要更新内容
index.js:28 hello world
index.js:30 true
index.js:39 componentWillUpdate组件将要更新
index.js:48 render渲染函数
index.js:42 componentDidUpdate组件更新完毕
下面来看这段另外一个实现点击记次数的代码:
首先定义Button类组件,构建页面框架:带有onClick监听事件改变this.state值的Button和传state.data进行后面段落渲染的Content。然后就是生命周期的一些操作,详见上面的实现方式。
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {data: 0};
this.setNewNumber = this.setNewNumber.bind(this);
}
setNewNumber() {
this.setState({data: this.state.data + 1})
}
render() {
return (
<div>
<button onClick = {this.setNewNumber}>INCREMENT</button>
<Content myNumber = {this.state.data}></Content>
</div>
);
}
}
class Content extends React.Component {
componentWillMount() {
console.log('Component WILL MOUNT!')
}
componentDidMount() {
console.log('Component DID MOUNT!')
}
componentWillReceiveProps(newProps) {
console.log('Component WILL RECEIVE PROPS!')
}
shouldComponentUpdate(newProps, newState) {
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log('Component WILL UPDATE!');
}
componentDidUpdate(prevProps, prevState) {
console.log('Component DID UPDATE!')
}
componentWillUnmount() {
console.log('Component WILL UNMOUNT!')
}
render() {
return (
<div>
<h3>{this.props.myNumber}</h3>
</div>
);
}
}
ReactDOM.render(
<div>
<Button />
</div>,
document.getElementById('example')
);
下拉列表框的表单操作
。event.preventDefault();
阻止浏览器的默认表单提交操作。class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择您最喜欢的网站
<select value={this.state.value} onChange={this.handleChange}>
<option value="gg">Google</option>
<option value="rn">Runoob</option>
<option value="tb">Taobao</option>
<option value="fb">Facebook</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
ReactDOM.render(
<FlavorForm />,
document.getElementById('example')
);
handleInputChange(event){}
来根据 event.target.name来进行setState操作。class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
是否离开:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
访客数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
import React from 'react';
import axios from 'axios';
import bannerImg from '../assets/img/banner.jpg'
class NewsCom extends React.Component{
constructor(props){
super(props)
this.state = {
datalist:[]
}
}
async componentWillMount(){
let res = await axios.get('http://localhost:8080/api/news');
//console.log(res.data)
let data = JSON.parse(res.data.data[0].content)
console.log(data.sub_raw_datas)
this.setState({
datalist:data.sub_raw_datas
})
}
render(){
console.log(this.state.datalist);
return (
<div className="contentItem new">
<div className="banner">
<img alt="banner" src={bannerImg} />
<h1>疫情追踪</h1>
</div>
<div className="newContent">
<div className="line"></div>
<div className="newsList">
{
this.state.datalist.map((item,index)=>{
if(item.raw_data.event_image){
return (
<div className="newsListItem" key={index}>
<div className="time">{item.raw_data.showtime_string}</div>
<div className="desc">
{item.raw_data.desc}
</div>
<div className="img">
<img src={item.raw_data.event_image.url} alt="img" />
</div>
</div>
)
}else{
return (
<div className="newsListItem" key={index}>
<div className="time">{item.raw_data.showtime_string}</div>
<div className="desc">
{item.raw_data.desc}
</div>
</div>
)
}
})
}
</div>
</div>
</div>
)
}
}
export default NewsCom;
server是这样的:
const express = require('express');
const axios = require('axios')
const app = express()
app.get('/',(req,res)=>{
res.send("返回抗疫数据的api服务器")
})
app.get('/api/newsdata',async (req,res)=>{
//解决ajax跨域问题
res.append("Access-Control-Allow-Origin","*")
res.append("Access-Control-Allow-content-type","*")
//请求头条数据
let result = await axios.get('https://i.snssdk.com/forum/home/v1/info/?activeWidget=1&forum_id=1656784762444839')
let data = result.data;
res.send(data)
})
app.get('/api/news',async (req,res)=>{
//解决ajax跨域问题
res.append("Access-Control-Allow-Origin","*")
res.append("Access-Control-Allow-content-type","*")
//请求头条数据
let httpUrl = 'https://i.snssdk.com/api/feed/forum_flow/v1/?activeWidget=1&query_id=1656810113086509&tab_id=1656810113086525&category=forum_flow_subject&is_preview=0&stream_api_version=82&aid=13&offset=0&count=20'
let result = await axios.get(httpUrl)
let data = result.data;
res.send(data)
})
app.listen(8080,()=>{
console.log("server Start:")
console.log("http://localhost:8080")
console.log("http://localhost:8080/api/newsdata")
})
组件中的HTML内容直接全部插入
class ParentCom extends React.Component{
render(){
console.log(this.props)
return (
<div>
<h1>组件插槽</h1>
{this.props.children}
</div>
)
}
}
组件中根据HTML内容的不同,插入的位置不同。
以下实现了:父组件在子组件的创建中设置子组件的props,子组件可以根据props来进行相关运算。
import React from 'react';
import ReactDOM from 'react-dom';
class ParentCom extends React.Component{
render(){
console.log(this.props)
return (
<div>
<h1>组件插槽</h1>
{this.props.children}
<ChildCom>
<h1 data-position="header">这是放置到头部的内容</h1>
<h1 data-position="main">这是放置到主要的内容</h1>
<h1 data-position="footer">这是放置到尾部的内容</h1>
</ChildCom>
</div>
)
}
}
class ChildCom extends React.Component{
render(){
let headerCom,mainCom,footerCom;
this.props.children.forEach((item,index)=>{
if(item.props['data-position']==='header'){
headerCom = item
}else if(item.props['data-position']==='main'){
mainCom = item
}else{
footerCom = item
}
})
return (
<div>
<div className="header">
{headerCom}
</div>
<div className="main">
{mainCom}
</div>
<div className="footer">
{footerCom}
</div>
</div>
)
}
}
class RootCom extends React.Component{
constructor(props){
super(props)
//console.log(props)
this.state = {
arr:[1,2,3]
}
}
render(){
return (
<ParentCom>
<h2 data-name="a" data-index={this.state.arr[0]}>子组件1</h2>
<h2 data-name="b" data-index={this.state.arr[1]}>子组件2</h2>
<h2 data-name="c" data-index={this.state.arr[2]}>子组件3</h2>
</ParentCom>
)
}
}
ReactDOM.render(
<RootCom></RootCom>
,
document.querySelector("#root")
)
安装:
npm install react-router-dom -save
以下为路由案例
import React from 'react';
//hash模式
//import {HashRouter as Router,Link,Route} from 'react-router-dom'
//history模式/后端匹配使用
import {BrowserRouter as Router,Link,Route} from 'react-router-dom'
function Home(){
return (
<div>
<h1>admini首页</h1>
</div>
)
}
function Me(){
return (
<div>
<h1>admin个人中心</h1>
</div>
)
}
function Product(){
return (
<div>
<h1>admin产品页面</h1>
</div>
)
}
class App extends React.Component{
render(){
return (
<div id="app">
{/* 所有页面普通内容 */}
<Router>
<Route path="/" exact component={()=>(<div>首页</div>)}></Route>
<Route path="/me" component={()=>(<div>me</div>)}></Route>
<Route path="/product" component={()=>(<div>product</div>)}></Route>
</Router>
<Router>
{/*
Home
Product
个人中心
*/}
<Route path="/admin/" exact component={Home}></Route>
<Route path="/admin/product" component={Product}></Route>
<Route path="/admin/me" exact component={Me}></Route>
</Router>
</div>
)
}
}
export default App
一般而言,react路由会匹配所有匹配到的路由组件,exact能够使得路由的匹配更严格一些。exact的值为bool型,为true是表示严格匹配,要求路径与location.pathname必须完全匹配
,且会比较匹配过的路由是否和正匹配的路由一致,为false时为正常匹配。
如在exact为true时,’/link’与’/’是不匹配的,但是在false的情况下它们又是匹配的。
<Route path='/' component={App} />
<Route path='/Home' component={Home} />
<Route path='/About' component={About} />
//这种情况下,如果点击Home,匹配路由path='/Home',那么会把App也会展示出来。
<Route exact path='/' component={App} />
<Route path='/Home' component={Home} />
<Route path='/About' component={About} />
//这种情况,如果点击Home,匹配路由path='/Home',那么App就不会展示出来
class App extends React.Component{
render(){
let meObj = {
pathname:"/me",//跳转的路径
search:"?username=admin",//get请求参数
hash:"#abc",//设置的HASH值
state:{msg:'helloworld'}//传入组件的数据
};
return (
<div id="app">
<Router>
<div className="nav">
<Link to="/">Home</Link>
<Link to="/product">Product</Link>
<Link to={ meObj } replace>个人中心</Link>
<Link to="/news/4568789">新闻页</Link>
</div>
<Route path="/" exact component={Home}></Route>
<Route path="/product" component={Product}></Route>
<Route path="/me" exact component={Me}></Route>
<Route path="/news/:id" component={News}></Route>
</Router>
</div>
)
}
}
import React from 'react';
import {BrowserRouter as Router,Route,Link,Redirect,Switch} from 'react-router-dom'
function LoginInfo(props){
//props.loginState = 'success';
//props.loginState = "fail"
console.log(props)
if(props.location.state.loginState === 'success'){
return <Redirect to="/admin"></Redirect>
}else{
return <Redirect to="/login"></Redirect>
}
}
let FormCom = ()=>{
let pathObj = {
pathname:"/logininfo",
state:{
loginState:'success'
}
}
return (
<div>
<h1>表单验证</h1>
<Link to={pathObj}>登录验证后页面</Link>
</div>
)
}
class ChildCom extends React.Component{
render(){
return (
<div>
<button onClick={this.clickEvent}>跳转到首页</button>
</div>
)
}
clickEvent=()=>{
//console.log(this.props)
//this.props.history.push("/",{msg:"这是由ChildCom组件发给首页的数据"})
//this.props.history.replace("/",{msg:"这是由ChildCom组件发给首页的数据"})
//前进
//this.props.history.go(1)
//this.props.history.goForward()
//后退
//this.props.history.go(-1)
//this.props.history.goBack()
}
}
class App extends React.Component{
render(){
return (
<div>
<Router>
<Switch>
<Route path="/:id" exact component={(props)=>{console.log(props);return (<h1>首页</h1>)}}></Route>
<Route path="/form" exact component={FormCom}></Route>
<Route path="/login" exact component={()=>(<h1>登录页</h1>)}></Route>
<Route path="/logininfo" exact component={LoginInfo}></Route>
<Route path="/admin" exact component={()=>(<h1>admin页,登录成功</h1>)}></Route>
<Route path="/abc" exact component={()=>(<h1>abc1页,登录成功</h1>)}></Route>
<Route path="/abc" exact component={()=>(<h1>abc2页,登录成功</h1>)}></Route>
<Route path="/child" component={ChildCom} ></Route>
</Switch>
</Router>
</div>
)
}
}
export default App
JS中的解构
import find from 'lodash/find';
state={
data:[{
id:1,
name:'xiaobe'
},{
id:2,
name:'Sam'
}]
}
fn(){
const{data}=this.state;
constnewData=[...data];
console.log('修改前data',data);
constnewItem=find(newData,{id:1})||{};
console.log('修改前newItem',newItem);
if(Object.keys(newItem).length){
newItem.name='new-xiaobe';
}
console.log('修改后newItem',newItem);
this.setState(
{
data:[...newData],
},
()=>{
console.log('修改后data',data);
},);
}
发现,修改前data里第一条name已经从xiaobe变成了new-xiaobe
[…data]到底干了啥?下面以你提供的数据来解释以下:
为了方便理解,我改造一下data。
const a={
id:1,
name:'xiaobe'
}
const b={
id:2,
name:'Sam'
}
const data=[a,b]
[…data]的流程如下:
const newData=[]//newArray()
newData.push(a)
newData.push(b)
通过上面的代码,我们发现如果直接判断newData===data,结果一定为false,因为newData的内存地址和data的不一致。
但是如果你判断newData[0]===data[0]以及newData[1]===data[1],那结果变为true,子项的内存地址并没有发生变化。
当你修改第一项的name值之后,便会影响到data和newData,因为这个子项被data和newData都引用了。
整个应用状态存储到一个地方上称为store
,里面保存着一个状态树store tree
,组件可以派发(dispatch)行为(action)给store
,而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图
。唯一改变state的方法就是触发action
,action是一个用于描述以发生事件的普通对象
import {createStore} from 'redux'
const store=createStore(fn);
import {createStore} from 'redux'
const store=createStore(fn);
const state=store.getState()
const action={
type:'ADD_TODO',
payload:'redux原理'
}
在上面代码中,Action的名称是ADD_TODO,携带的数据是字符串‘redux原理’,Action描述当前发生的事情,这是改变state的唯一的方式
store.dispatch({
type:'ADD_TODO',
payload:'redux原理'
})
store.dispatch接收一个Action作为参数,将它发送给store通知store来改变state。
```javascript
const reducer =(state,action)=>{
switch(action.type){
case ADD_TODO:
return newstate;
default return state
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux'
//用于通过动作,创建新的state
const reducer = function(state={num:0},action){
console.log(action)
switch(action.type){
case "add":
state.num++;
break;
case 'decrement':
state.num--;
break;
default:
break;
}
return {...state}//相当于对象的COPY
}
//创建仓库
const store = createStore(reducer)
console.log(store)
function add(){
//通过仓库的方法dispatch进行修改数据
store.dispatch({type:"add",content:{id:1,msg:"helloworld"}})
console.log(store.getState())
}
function decrement(){
//通过仓库的方法dispatch进行修改数据
store.dispatch({type:"decrement"})
console.log(store.getState())
}
//函数式计数器
const Counter = function(props){
//console.log(store.getState())
let state = store.getState()
return (
<div>
<h1>计数数量:{state.num}</h1>
<button onClick={add}>计数+1</button>
<button onClick={decrement}>计数-1</button>
</div>
)
}
ReactDOM.render(<Counter></Counter>,document.querySelector("#root"))
store.subscribe(()=>{
ReactDOM.render(<Counter></Counter>,document.querySelector("#root"))
})
下面看一个可以+1和+5的加法器
import React from 'react';
import ReactDom from 'react-dom'
import {createStore} from 'redux'
//Provide 跟store 和视图结合起来
//connect 数据的连接
import {Provider,connect} from 'react-redux'
class Counter extends React.Component{
render(){
//用于计数,通过组件来获取值,组件的props通过store的state来获取的
const value = this.props.value;
//添加事件,将修改事件的数据或者方法传入到props
const onAddClick = this.props.onAddClick;
return (
<div>
<h1>计数的数量:{value}</h1>
<button onClick = {onAddClick}>数字+1</button>
<button onClick = {this.props.onAddClick5}>一次性+5</button>
</div>
)
}
}
const addAction = {
type : 'add'
}
//不要让reducer放很多方法 所以在外面定义一个action的对象
ActionFnObj = {
add:function(state,action){
return state;
},
addNum:function(state,action){
state.num = state.num + action.num;
return state;
}
}
//设置reducer默认值
function reducer(state = {num : 0},action){
//判断action的字段中有无redux,没有就是要进行初始化的意思
if(action.type.indexOf('redux')==-1){
state = ActionFnObj[action.type](state,action);
return {...state};
} else{
return state;
}
//在action对象中找到相关方法 然后设置action.asg的参数
//这样写就可以不用switch了
// switch(action.type){
// case 'add':
// state.num++;
// break;
// default:
// break;
// }
//解构挨个返回新的state
return {...state}
}
//创建一个store
const store = createStore(reducer)
//将store的state数据映射到props
//将状态映射到props的函数
function mapStateToProps(){
//返回props 将state的num传给props的value
return {
value:state.num
}
}
//将state的dispatch映射到组件的props里面 实现了方法共享
//修改state数据的方法 映射到props 默认会传入store里面的dispatch方法
function mapDispatchToprops(){
return {
//放一个props方法 匿名函数或者是箭头函数
onAddClick:(dispatch)=>{
dispatch(addAction);
},
onAddClick5:(dispatch)=>{
dispatch({type:'addNum',num:5});
}
}
}
//将上面的这两个数据仓库的state和修改state的方法关联映射到Counter组件上,形成新的组件
const App = connect(
mapStateToProps,
mapDispatchToprops
)(Counter)
//更新渲染
//Provider 将store的数据传进去 并且做数据的更新
ReactDOM.render(
<Provider store = {store}>
<App></App>
</Provider>,
document.querySelector('#root')
)
的解释如下: