1 React 介绍
1.1 react
用于构建用户界面的 JavaScript 库。
react核心文件:react.development.js
react用于操作dom的扩展文件,需要依赖react核心文件:react-dom.development.js
ReactDOM.render() // 第一个参数:写入的内容,第二个参数:写入的位置
// 不允许放置到 html 和 body 标签中,会报错
1.2 虚拟DOM元素
① React.createElement()
创建虚拟的DOM元素:JSX React.createElement()
// createElement 返回的是一个虚拟的DOM对象
// 第一个参数:元素名字,第二个参数:配置属性,第三个参数:元素的内容
// h1 可以称为元素变量
const h1 = React.createElement("h1",{
id:"my",
className:"c1" // 会编译为class
},"你过来啊") // 虚拟对象中的 type h1,props是一个对象,存放属性;props中有个children为内容
React.render(h1,document.querySelector("div"))
// 第三个参数可以是数组
const span = React.createElement("span",{
id:"one",
key:"xixixi" // 可以提升 diff 算法的执行效率
},["你过来","我是一个span"])
② JSX
1.3 JSX 基本语法规范
1.3.1 JSX 元素变量或将其放置到外部
JSX 是 javascript xml,jsx是 javascript 的一个扩展,浏览器不支持,需下载 babel.min.js,支持JSX,可以将type为 text/babel 的 script 标签内出现的 JSX 转换为浏览器可识别元素。
// jsx 简化了 React.createElement 的语法结构
// jsx 是一个虚拟DOM对象,执行速度快,安全
// jsx 一般以<开头,<>当中如果是HTML标签,那么将作为HTML来处理;如果不是则会作为组件来处理
// {}:会将{}中的内容作为js语句执行
ReactDOM.render(<h1>叫爸爸</h1>,document.querySelector("div"));
*有且只能有一个根元素
// <><div></div></> // 只是一个包裹标签
// 如果内容不多建议加()
ReactDOM.render((
<React.Fragment>
<div></div>
<div></div>
</React.Fragment>
),document.querySelector("div"))
// 如果内容较多,建议声明一个元素变量,值为虚拟DOM元素
const myDiv = (
<div>
<div>1</div>
<p>2</p>
<h1>1</h1>
<h2>2</h2>
</div>
)
ReactDOM.render(myDiv,document.querySelector("#root"))
1.3.2 {} 用法
// 1、输出变量值 <%=%>
let userName = "laotie";
ReactDOM.render((
<div>
<p>{userName}</p>
<p>{"abcdefg"}</p>
</div>
),document.querySelector("#root"));
// 2、与属性结合使用。
let imgSrc = "https://www.baidu.com/img/flexible/logo/pc/result.png";
ReactDOM.render((
<div>
<img src="https://www.baidu.com/img/flexible/logo/pc/result.png"/>
<img src={"https://www.baidu.com/img/flexible/logo/pc/result.png"} />
<img src={imgSrc}/>
</div>
),document.querySelector("#root"))
// 3、写表达式. 简写形式(语法糖)
let num = 1;
let str = "师法魔级超";
let myDiv = <div>my</div>
let youDiv = <div>you</div>
ReactDOM.render((
<div>
<p>{num===1?"成功":"失败"}</p>
<p>{str.split("").reverse().join("")}</p>
{
// num===1?myDiv:youDiv
}
{
/**
* adsf
*/
}
{/* num===1 && youDiv */}
{ num===1 && myDiv}
{ num!==1 && youDiv}
</div>
),document.querySelector("#root"));
1.3.3 className 和 style
<div>
<p className="haha" style={{background:"red"}}>1</p>
</div>
1.4 关于事件
注意:
如果 render 出现多次,最后一次会将前面的覆盖。
react 当中的事件与原生事件的不同:
1.react 当中的事件需要使用小驼峰命名法
2.取消默认事件不支持 return false,只支持 preventDefault()
给元素添加事件,可以给一个普通函数,可以给一个箭头函数,可以先定义好一个函数将函数名写在{}里,
可以定义好一个返回值是函数的函数,在{}里调用该函数,可以传参,只是会有一个事件对象。
let fn = function(){
console.log(3333333333);
}
let fn1 = function(){
return function () {
console.log(444444)
}
}
<div>
<button onClick={function () {
console.log(111111111)
}}>一</button>
<button onClick={()=>{
console.log(222222222)
}}>二</button>
<button onClick={fn}>三</button>
<button onClick={fn1}>四</button>
<a href="http://www.baidu.com" onClick={function (e) {
alert("百度")
e.preventDefault();// 取消默认事件
}}>百度</a>
</div>
// 小案例 显示与隐藏
let isShow = true;
let myDiv = (
<div style={{
width:"300px",
height:"300px",
background:"cyan",
display:isShow?"block":"none"
}}>
</div>
)
function main(){
ReactDOM.render((
<div>
<button onClick={()=>{
isShow = !isShow;
main();
}}>显示与隐藏</button>
{
isShow && myDiv
}
</div>
),document.querySelector("#root"));
}
// function main(){
// ReactDOM.render((
// <div>
// <button onClick={()=>{
// isShow = !isShow;
// main();
// }}>显示与隐藏</button>
// <div style={{
// width:"300px",
// height:"300px",
// background:"cyan",
// display:isShow?"block":"none"
// }}>
// </div>
// </div>
// ),document.querySelector("#root"));
// }
main();
1.5 关于数组
react 当中是可以直接将数组展开的。元素是 react 中的最小单位,key 是一个字符串,可以提升执行速度。
let bookArr = ["天龙八部","啦啦啦","嘻嘻嘻"];
ReactDOM.render((
<div>
{
bookArr.map((v,index)=>{
// return "《"+v+"》"
return (
<p key={index}>《{v}》</p>
)
})
}
</div>
),document.querySelector("#root"));
1.6 **函数组件
组件: 是对 html 元素的扩展,首字母以大写开头
模板: 由 html ,组件来组成的
组件分为两种:1、函数式组件(无状态组件)通过函数声明的组件 2、类组件(状态组件)通过类声明的组件。
状态指的是数据。
使用组件时可以直接闭合
定义组件的要求:
1、首字母大写
2、必须要有返回值,一般情况下需要返回JSX
3、有且只能有一个根元素
4、函数名就是组件名
5、返回的数据即是要渲染的内容(渲染的内容即为模板 template)
1、函数式组件
// 复合组件 由多个组件组成的组件称为复合组件
// 组件传参 **重点**
1、如何传(从上到下):通过属性向下传参 // 单向数据流
组件参数不允许直接修改的(5)
修改属性是通过组件函数(6、7)props
2、如何接收
3、类型限制 proptypes
引入 prop-types.js
给组件增加一个静态属性 propTypes 限制:age:PropTypes.number
设置默认值:组件名.defaultProps
2、状态组件,需要继承React.Component:
类名即为组件名,需要有一个 render 方法,必须有返回值
state 状态即数据
声明状态组件
**修改状态的方法:this.setState({},()=>{状态修改完毕之后会执行该回调}),异步修改,修改状态之后会重新执行render函数,重新渲染
生命周期:钩子函数 componentDidMount
1.7 事件
事件函数:
解决 this 为 undefined 的方案:
1、需要用.bind 绑定 this,可以传参,使用频率较高的话不建议使用(传参的情况比较多)
2、构造器当中直接.bind绑定this,当不需要额外传参,且使用频率较高时建议使用
3、直接写成箭头函数
4、直接将函数定义为箭头函数,传值不方便
1.8 乞丐版切换和优化版切换
// 乞丐版切换
<script type="text/babel">
class App extends React.Component{
constructor(props){
super(props)
this.state = ({
index:0
})
}
changeIndex(index){
this.setState({
index
})
}
render(){
const {index} = this.state;
return(
<div className="tag">
<button onClick={this.changeIndex.bind(this,0)} className={index===0?"active":null}>娱乐</button>
<button onClick={this.changeIndex.bind(this,1)} className={index===1?"active":null}>体育</button>
<button onClick={this.changeIndex.bind(this,2)} className={index===2?"active":null}>军事</button>
<div style={{display:index===0?"block":"none"}}>娱乐新闻</div>
<div style={{display:index===1?"block":"none"}}>体育新闻</div>
<div style={{display:index===2?"block":"none"}}>军事新闻</div>
</div>
)
}
}
ReactDOM.render((
<App></App>
),document.querySelector("#root"));
</script>
// 优化版
<script type="text/babel">
class App extends React.Component{
constructor(props){
super(props)
this.state = ({
index:0
}),
this.newsInfo = [
{
id:1,
type:"娱乐",
newsList:[
{
id:4,
newsTitle:"娱乐一号"
},
{
id:5,
newsTitle:"娱乐二号"
}
]
},
{
id:2,
type:"体育",
newsList:[
{
id:6,
newsTitle:"体育一号"
},
{
id:7,
newsTitle:"体育二号"
}
]
},
{
id:3,
type:"军事",
newsList:[
{
id:8,
newsTitle:"军事一号"
},
{
id:9,
newsTitle:"军事二号"
}
]
}
]
}
changeIndex(index){
this.setState({
index
})
}
render(){
const {index} = this.state;
return(
<div className="tag">
{
this.newsInfo.map((item,i)=>(
<button onClick={this.changeIndex.bind(this,i)} className={this.state.index===i?"active":null}>{item.type}</button>
))
}
{
this.newsInfo.map((item,i)=>(
<div key={item.id} style={{display:index===i?"block":"none"}}>
{
item.newsList.map(info=>(
<p key={info.id}>{info.newsTitle}</p>
))
}
</div>
))
}
</div>
)
}
}
ReactDOM.render((
<App></App>
),document.querySelector("#root"));
</script>
1.9 通过axios调用数据
<script type="text/babel">
axios.interceptors.response.use(res=>{
return res.data;
})
React.Component.prototype.$axios = axios;
class App extends React.Component{
constructor(props){
super(props)
this.state = ({
index:0,
newsInfo:[]
})
}
changeIndex(index){
this.setState({
index
})
}
render(){
return(
<div className="tag">
{
this.state.newsInfo.map((item,i)=>(
<button onClick={this.changeIndex.bind(this,i)} className={this.state.index===i?"active":null}>{item.type}</button>
))
}
{
this.state.newsInfo.map((item,i)=>(
<div key={item.id} style={{display:this.state.index===i?"block":"none"}}>
{
item.newsList.map(info=>(
<p key={info.id}>{info.newsTitle}</p>
))
}
</div>
))
}
</div>
)
}
componentDidMount(){
this.$axios.get("./data.json")
.then(newsInfo=>{
this.setState({
newsInfo
})
})
}
}
ReactDOM.render((
<App></App>
),document.querySelector("#root"));
</script>
1.10 类组件
1.10.1 类组件传参
子组件接收的属性不可以直接修改;
可以通过向子组件传递函数,通过该函数进行修改,修改的其实是父组件的数据。
1.10.2 类组件的属性限制
props:属性,不允许直接修改,用于传递数据与接收数据;
state:状态,是可以修改的,在本组件中使用
子组件设置属性限制:
给类本身添加个属性propTypes,相当于类中添加静态属性。
defaultProps是默认属性值
1.11 ref
函数组件用于渲染较多,类组件有自己的状态,通常用于需要写一些逻辑的场景。
ref: 可以通过其获得DOM元素以及组件的实例
① ref 是一个属性,值为字符串
refs 是一个对象,对象里的内容是所有拥有ref属性的元素,对象的属性为ref的属性值
class App extends React.Component{
render(){
return (
<div>
<div ref="one">one</div>
<div ref="two">two</div>
<input type="text" ref="userName"/>
<button onClick={()=>{
// refs:是一个对象,对象里的内容是所有拥有ref属性的元素
console.log(this.refs.one.innerHTML)
console.log(this.refs.two.innerHTML)
console.log(this.refs.userName.value)
this.refs.userName.value = "XIXI";
this.refs.one.style.background = "red";
}}>点我</button>
</div>
)
}
}
② ref 是一个属性,值为箭头函数
箭头函数会在加载时执行,接收的参数即是DOM元素
class App extends React.Component{
render(){
return (
<div>
<div ref={el=>{
this.one = el;
console.log(el); // el 为<div></div>
}}></div>
<input ref={el=>this.userName=el} type="text"/> // el 为input元素
<button onClick={()=>{
this.one.innerHTML = "ABCDEFG"
// console.log(this.one);
console.log(this.userName.value);
this.userName.value = "efg";
}}>点我</button>
</div>
)
}
}
③ 推荐使用
构造器当中设置属性,值为React.createRef(),会返回一个对象,对象中拥有属性 current,current的值就是拥有 ref 属性且值为该实例属性的元素。
// 3、ref的值是通过React.createRef()生成的。
class App extends React.Component{
constructor(props) {
super(props);
this.userName = React.createRef();// 会返回一个对象,对象当中拥有属性current
}
render(){
return (
<div>
<div ref={this.userName}>123</div>
<button onClick={()=>{
console.log(this.userName.current.innerHTML)
}}>点我</button>
</div>
)
}
}
特点:
如果通过ref多次赋值,那么得到的是最后使用ref的元素,
如果ref与组件结合使用,那么得到的是该组件的实例
class Child extends React.Component{
constructor() {
super();
this.state = {
a:1,
b:2
};
this.age = React.createRef();
}
render(){
return (
<div>{this.state.a}|{this.state.b}
<input type="text" ref={this.age}/>
</div>
)
}
}
class App extends React.Component{
constructor(props) {
super(props);
this.userName = React.createRef();// 会返回一个对象,对象当中拥有属性current
this.arr = [1,2,3];
this.arrRef = React.createRef();
this.child = React.createRef();
}
render(){
return (
<div>
<Child ref={this.child}></Child>
<div ref={this.userName}>123</div>
<div ref={this.userName}>456</div>
<div ref={this.userName}>789</div>
{
this.arr.map(item=>(
<p ref={this.arrRef} key={item}>{item}</p>
))
}
<button onClick={()=>{
// console.log(this.userName.current.innerHTML) // 789
// console.log(this.arrRef.current) // 所在的p元素
console.log(this.child.current.age.current.value) // ref的值为this.age的input元素
}}>点我</button>
</div>
)
}
}
1.12 受控组件
受控组件: 表单相对应的组件,受 react 数据状态的控制。通过 value 属性
ref 属性是 非受控组件
class App extends React.Component{
constructor() {
super();
this.state = {
userName:"laozhang",
passWord:"",
home:"3",
sex:"1",
hobby:["1","3"]
}
}
changeHandler(e){
// console.log(e.target.value)
// console.log(e.target.name)
let value = e.target.value;
if(e.target.type === "checkbox"){
const isHas = this.state[e.target.name].includes(e.target.value);
if(isHas){
value = this.state[e.target.name].filter(v=>v !== e.target.value)
}else{
value = [
...this.state[e.target.name],
e.target.value
]
}
}
this.setState({
[e.target.name]:value
})
// if(e.target.type === "checkbox"){
// const isHas = this.state[e.target.name].includes(e.target.value);
// if(isHas){
// this.setState({
// [e.target.name]:this.state.hobby.filter(v=>v !== e.target.value)
// })
// }else{
// this.setState({
// [e.target.name]:[
// ...this.state.hobby,
// e.target.value
// ]
// })
// }
//
// }else{
// this.setState({
// [e.target.name]:e.target.value
// })
// }
}
submitHandler(e){
console.log("userName",this.state.userName);
console.log("passWord",this.state.passWord);
console.log("home",this.state.home);
console.log("hobby",this.state.hobby)
e.preventDefault();
}
render(){
return (
<form onSubmit={this.submitHandler.bind(this)} action="http://www.baidu.com">
{/*<p>用户名:<input type="text" defaultValue={this.state.userName}/></p>*/}
<p>用户名:<input name="userName" onChange={this.changeHandler.bind(this)} type="text" value={this.state.userName}/></p>
<p>密码:<input
name="passWord"
onChange={this.changeHandler.bind(this)}
type="password"
value={this.state.passWord}/></p>
<p>家乡:
<select name="home" onChange={this.changeHandler.bind(this)}
value={this.state.home} name="home">
<option value={"1"}>南白滩</option>
<option value={"2"}>北白滩</option>
<option value={"3"}>湖南</option>
<option value={"4"}>湖北</option>
</select>
</p>
<p>性别:
<input onChange={this.changeHandler.bind(this)} name="sex" checked={this.state.sex==="1"} type="radio" value="1"/>男
<input onChange={this.changeHandler.bind(this)} name="sex" checked={this.state.sex==="2"} type="radio" value="2"/>女
</p>
<p>
爱好:
<input name="hobby" onChange={this.changeHandler.bind(this)} checked={this.state.hobby.includes("1")} type="checkbox" value="1" />学习
<input name="hobby" onChange={this.changeHandler.bind(this)} checked={this.state.hobby.includes("2")} type="checkbox" value="2" />游戏
<input name="hobby" onChange={this.changeHandler.bind(this)} checked={this.state.hobby.includes("3")} type="checkbox" value="3" />电影
<input name="hobby" onChange={this.changeHandler.bind(this)} checked={this.state.hobby.includes("4")} type="checkbox" value="4" />睡觉
</p>
<p><input type="submit" value="提交" /></p>
</form>
)
}
}
2 生命周期
2.1 旧图
生命周期: 组件从创建到销毁的过程。在生命周期当中所暴露出来的函数称为钩子函数 。
钩子函数: 并不是人为触发的,执行到某一个阶段的时候自动触发,只要声明了函数到指定阶段自动会执行。
生命周期分为三个阶段:
① 挂载阶段 mounting
1、2、4在生命周期中只会执行一次
1. constructor() // 构造器
2. componentWillMount() // 挂载之前 在后续某个版本中可能会被废弃掉,去除警告加前缀:UNSAFE
3. render() // 指定渲染的内容
4. componentDidMount() // 界面内容渲染完成
2 中可以编写同步代码
② 更新阶段 updating
- 更新属性
1. componentWillReceiveProps(nextProps) // 当属性发生变化时 UNSAFE_
2. shouldComponentUpdate(nextProps) // 根据返回值决定是否允许更改界面
3. componentWillUpdate(nextProps) // 更新之前
4. render() // 指定渲染的数据
5. componentDidUpdate(prevProps) // 更新完成
class Child extends React.Component{
constructor() {
super();
this.userName = React.createRef();
}
// 更新 nextProps:是即交要更新的值
UNSAFE_componentWillReceiveProps(nextProps){
console.log("1、componentWillReceiveProps");
console.log(this.props.age,nextProps.age) // 12 13
}
// 必须要返回一个布尔值。true允许修改,false不允许更改
shouldComponentUpdate(nextProps){
console.log("2、shouldComponentUpdate");
if(nextProps.age >15){
return false
}
console.log(this.props.age,nextProps.age) // 12 13
return true;
}
UNSAFE_componentWillUpdate(nextProps){
console.log("3、componentWillUpdate");
console.log(this.props.age,nextProps.age) // 12(更新之前的值) 13(即将更改的值)
}
UNSAFE_componentWillMount(){
console.log("UNSAFE_componentWillMount")
}
render(){
console.log("4、render",this.props.age) // 初始渲染12,状态发生改变时会变化
return (
<div>
我还是一个孩子,今年{this.props.age}岁了
<div ref={this.userName} >123123123123</div>
</div>
)
}
componentDidMount(){
console.log("componentDidMount")
}
componentDidUpdate(prevProps){
console.log("5、componentDidUpdate")
// this.props.age:更新以后的值 prevProps:更新之前的值
console.log(this.props.age,prevProps.age,this.userName.current.innerHTML)
}
}
class App extends React.Component{
constructor() {
super();
this.state = {
age:12
}
}
render(){
return (
<div>
App
<Child age={this.state.age}></Child>
<button onClick = {()=>{
this.setState({
age:this.state.age+1
})
}}>修改age</button>
</div>
)
}
}
- 更新状态
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render()
4. componentDidUpdate()
class Child extends React.Component{
constructor() {
super();
this.state = {
num:0
}
this.userName = "你好";
}
// componentWillReceiveProps(){
// console.log("componentWillReceiveProps")
// }
shouldComponentUpdate(nextProps,nextState){
console.log("1、shouldComponentUpdate")
console.log(this.state.num,nextState.num)
return true;
}
UNSAFE_componentWillUpdate(nextProps,nextState){
console.log("2、componentWillUpdate");
console.log(this.state.num,nextState.num)
}
render(){
console.log("3、render")
return (
<div>
<button onClick={()=>{
this.userName = "我也好";
// 当你的数据没有存放在this.state当中。
this.forceUpdate();// 强制更新,会跳过shouldComponentUpdate
// this.setState({
// num:this.state.num+1
// })
}}>{this.userName}|{this.state.num}</button>
</div>
)
}
componentDidUpdate(prevProps,prevState){
console.log("4、componentDidUpdate")
console.log(this.state.num,prevState.num);// 当前值 更新之后的值
}
}
class App extends React.Component{
render(){
return (
<div>
<Child></Child>
</div>
)
}
}
在事件中强制更新的话只会执行componentWillUpdate()、render()、componentDidUpdate()
③ 销毁阶段 unmounting
componentWillUnmount() // 销毁
class Child extends React.Component{
constructor() {
super();
this.state = {
num:0
}
}
render(){
return (
<div>我还是一个孩子,今年{this.state.num}岁了</div>
)
}
componentDidMount(){
this.timer = setInterval(()=>{
console.log("111111111")
this.setState({
num:this.state.num+1
},function () {
console.log(this.state.num);
})
},1000)
}
componentWillUnmount(){
console.log("componentWillUnmount");
clearInterval(this.timer);
} // 虽说被销毁了,状态没有了会报错,但是还有些东西存在,在完成挂载时设置个多次定时,当销毁后还会触发该定时器,所以在定时器中添加第二个回调可以将结束的值获取到。
}
class App extends React.Component{
constructor() {
super();
this.state = {
isShow:true
}
}
render(){
return (
<div>
<button onClick={()=>{
this.setState({
isShow:!this.state.isShow
})
}}>显示与隐藏</button>
{
this.state.isShow && <Child/>
}
</div>
)
}
}