一、类式组件
1、继承
class Person {
constructor(name,age){
this.name = name
this.age = age
}
}
class Student extends Person{
constructor(name,age,grade){
/*
按理来说是这么写的,继承父类的属性
this.name = name
this.age = age
但比较麻烦,如果父类写一千条,那这里岂不是也要写一千条吗,所以用super
*/
super(name.age)
//加上自己的属性
this.grade = grade
}
}
子类写构造方法时,必须要加上super,因为你要写新的属性,必须要把父类的属性加上去
2、this指向
class Person {
constructor(name,age){
this.name = name
this.age = age
}
study(){
//study方法放在了哪里? -类的原型对象上,供实例使用
//通过Person实例调用study时,study中this就是Person实例
console.log(this);
}
}
const p1 = new Person('tom',18)
p1.study() //通过实例调用study方法。this指向是Person实例(Person{....})
const x = p1.study //将方法直接赋给x,相当于给地址给了它
x() //相当于直接调用study方法,此时this指向为window,但方法里开启了严格模式,所以为undefind
3、改变this指向
class Weather extends React.Component{
constructor(props){
super(props)
this.state = {isHot: false}
//这里将this的指向绑定了,详细讲就是将此处的this(调用的实例对象),绑定到了实例对象上面,比如zs调用这个方法,那么zs的this就(前面的this)永远绑定到了zs的这个对象上面(括号里的this)身上,就是将this永远指向自己;换言之就是将this指向的值永远指向括号里面的对象
this.chageWeather = this.changeWeather.bind(this)
}
render(){
const {isHot} = this.state
//因为前面用bind将this的指向变为了实例对象,所以此处将方法赋值给它的同时,也将this指向的地方赋值给了它
render <h1 onClick = {this.changeWeather}>今天天气很{isHot? '炎热' : '凉爽'} </h1> }
changeWeather(){
//所以此处this的指向为实例对象
console.log(this)
}
}
ReactDOM.render(<Weather/>.document.getElementById('test'))
4、ref的使用
class Demo extends React.Component{
const {input} = this.input
showData = ()=>{
alert(input.value)
}
render(){
return(
//c这个参数为当前input节点。即input本身
<input ref = {c=>{this.input = c}} type = "text" placeholder = "点击按钮提示数据"/>
<button onClick = {this.showData}>点击</button>
)
}
}
ref一般用来联系多个节点,如button点击按扭输出input节点的文本框内容。如果本节点调用本节点的值,那么只就不需要用到ref,ref尽量少用,如:
class Demo extends React.Component{
showData = ()=>{
//event.target就是标签自身节点,即标签本身
alert(event.target.value)
}
render(){
return(
<input onBlur = {this.showData} type = "text" placeholder = "失去焦点提示数据">
)
}
}
5、受控组件和非受控组件
非受控组件(实现表单提交,提示框提示input中输入的账号和密码)
class Demo extends React.Component{
handleSubmit = (event) => {
event.preventDefault() //阻止表单提交
const { input1,input2 } = this
//$为转义字符
alert(`你输入的用户名为: ${input1.value} 你输入的密码为: ${input2.value}`)
}
render(){
return(
<form onSubmit = { this.handleSubmit }>
用户名: <input ref = { c => this.input1 = c } type = "text" name = "username"/>
密码: <input ref = { c => this.input2 = c } type = "text" name = "password"/>
<button>登录</button>
</form>
)
}
}
受控组件
class Demo extends React.Components{
state = {
username: '',
password: ''
}
saveUsername = () => {
this.setstate({ username: event.target.value })
}
savePassword = () => {
this.setstate({ password: event.target.value })
}
handleSubmit = () => {
alert(this.state.username+this.state.password)
}
render(){
return(
<form onSubmit = { this.handleSubmit }>
用户名: <input onChange = { this.saveUsername } type = "text" name = "username"/>
密码: <input onChange = { this.savePassword } type = "text" name = "password"/>
<button>登录</button>
</form>
)
}
}
非受控组件就是当你点击按钮才能获得输入框的值;受控组件它是实时更新的,它值就一直在那里,直接取就行了
6、高阶函数的柯里化
函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
为了避免上面例子要创建两个函数的尴尬,这里要用到高阶函数的柯里化,只创建一个函数就满足上述需求
class Demo extends React.Components{
state = {
username: '',
password: ''
}
saveFormData = (val) => {
return(
(event) => {
//疑点,这里的val如果不加个[],就相当与"val"
this.setState([val]: event.target.value)
}
)
}
handleSubmit = () => {
alert(this.state.username+this.state.password)
}
render(){
return(
<form onSubmit = { this.handleSubmit }>
//这里返回的是一个函数的执行结果,所以上面要用函数的柯里化让它返回的是一个函数,将state的值进行event.target更改
用户名: <input onChange = { this.saveFormData('username') } type = "text" name = "username"/>
密码: <input onChange = { this.saveFormData('password') } type = "text" name = "password"/>
<button>登录</button>
</form>
)
}
}
疑点解答
这里的中括号obj[myHero]相当于将2.myHero的值'hero'作为属性名传递给obj。所以第5行的代码就相当于console.log(obj.hero)。其中1.hero和2.hero的必须相等,即为同一个字符串
let obj = {};
obj.hero= '亚索'; //1.hero
let myHero = 'hero'; //myHero变量 2.hero
console.log(obj.myHero);//undefined,访问不到对应的属性
console.log(obj[myHero]);//亚索
7、生命周期简介
字体会在1秒内慢慢消失掉
class Life extends React.Component{
state = { opacity:1 }
death = () =>{
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//组件挂载完毕之后调用这个钩子,只会执行一次
componentDidMount(){
this.timer = setInterval(() => {
let { opacity } = this.state
opacity -=0.1
if(opacity <= 0)opacity = 1
this.setState(opacity)
},200)
}
//组件将要卸载会调用此钩子
componentWillUnmount(){
clearInterval(this.timer)
}
//组件内的内容每次更新就会调用render,因此如果将定时器放在render里面,会陷入到死循环当中
render(){
console.log('render');
return(
<div>
<h2 style = {{opacity:this.state.opacity}}React复习中</h2>
<button onClick = {this.death}>不活了</button>
</div>
)
}
}
新知识
1、a:a 可以写为a,如let b = 2; let a = {b:b}, 可写为let a = {b}
react的diffing算法:
2、每次重新渲染会和之前的数据作比较,用map遍历时最好不要用当中的index值作为key值,因为如果在原数组当中新增一个数据,下标为0,那么就会重新生成虚拟dom,从而引发效率问题,所以最好用id作为key的唯一标识
3、解构赋值:const a = this.b.a =>
const {a} = this.b =>
const {b:{a}} = this =>
将a的名字换成c(重命名)
const {b:{a:c}} = this
4、输出error信息时要用error.message
8、setState
setState更新状态的2种写法
setState(stateChange, [callback])-----对象式的setState
stateChange为状态改变对象(该对象可以体现出状态的更改)
callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用
setState(updater, [callback])-----函数式的setState
updater为返回stateChange对象的函数
updater可以接收到state和props
callback是可选的回调函数,它在状态更新、界面也更新hou(render调用后)才被调用
总结:1、对象式的seState是函数式的setState的简写方式(语法糖)
2、使用原则:
(1).如果新状态不依赖于原状态 ===>使用对象方式
(2).如果新状态依赖于原状态===>使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取
class Demo extends Component{
state = {count: 0}
add = () =>{
const {count} = this.state
this.setState({count:count+1})
console.log(state)
}
reder(){
return (
<div>
<h1>当前求和为: {this.state.count}</h1>
<button Onclick = {this.add}>点我+1</button>
</div>
)
}
}
点击一下按钮后,标签页面展示的是1,但console.log输出的是0,这是因为它是异步进行的
可以改为:
this.setState((state)=>({count: state.count+1}))
这里的参数除了state还可以传props,这里只传了一个参
9、lazyLoad懒加载
import React,{ Component,lazy } from 'react'
import { NavLink,Route } from 'react-router-dom'
/*
不能用import Home from './Home'
import About from './About'
这种方式引入路由组件,因为这样会直接把路由组件数据加载进去,起不到点一下,加载一下的效果
*/
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))
上述方式引入懒加载后,必须要在注册路由那里加上一些代码,当网比较慢时,可以给一些加载中的数据或者页面提醒客户正在加载中
<Suspense fallback = {<h1>正在加载中....<h1>}>
<Route path = "/about" component = {About}/>
<Route path = "/home" component = {Home}/>
</Suspense>
10、路由
路由的基本使用
index.js中要用一个大的路由器BrowserRouter包裹着
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root'))
About和Home子组件要展示的内容
About.jsx
import React, { Component } from 'react'
export default class index extends Component {
render() {
return (
<h3>我是About的内容</h3>
)
}
}
Home.jsx
import React, { Component } from 'react'
export default class index extends Component {
render() {
return (
<h3>我是Home的内容</h3>
)
}
}
App.jsx
import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
render() {
return (
<div>
//相当于一个超链接,点击之后会跳转到to后面的地址
<Link to = "/about">About</Link><br/>
<Link to = "/home">Home</Link>
//跳转之后的地址,其中地址名要与上面的一一对应
<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
</div>
)
}
}
高亮效果NavLink
高亮效果就是点击一个按钮,那个按钮持续性的保持那个颜色
将Link替换成NavLink
<div>
<NavLink to = "/about">About</NavLink><br/>
<NavLink to = "/home">Home</NavLink>
<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
</div>
并且可以给他加属性名,每点击一下就给它加一下属性名
<div>
<NavLink activeClassName = "yss" to = "/about">About</NavLink><br/>
<NavLink activeClassName = "yss" to = "/home">Home</NavLink>
<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
</div>
随后可以根据这个属性名加一些样式
封装NavLink组件
封装NavLink组件可以将
About
简写成
About
import React, { Component } from 'react'
export default class MyNavLink extends Component {
render() {
return (
<NavLink activeClassName = "atguigu" className = "list-group-item" {...this.props}>
)
}
}
引入封装好的MyNavLink组件
<MyNavLink to = "/about" a = {1}, b = {2}>About</MyNavLink>
<MyNavLink to = "/home">Home</MyNavLink>
按照正常情况来讲在子组件中间加一段文字内容,不会进行展示的,但是NavLink标签里面有个children属性,可以用来接收About和Home并将其进行展示,起到<span>你好</span>的作用
Switch的使用
<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
<Route path = "/home" component = {Test}></Route>
此时页面会展示三条数据,也就是路由匹配到了home的路由还会往下继续进行匹配,这样就影响了效率,所以此时为了避免这种情况,需要用到switch
import{Route,Switch} from 'react-router-dom'
<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
<Route path = "/home" component = {Test}></Route>
加上switch后,当匹配到Home之后就不会继续往下找Test了
解决样式丢失问题
<MyNavLink to = "/atguigu/home">Home</MyNavLink>
<MyNavLink to = "/atguigu/home">Home</MyNavLink>
<Switch>
<Route path = "/atguigu/about" component = { About }/>
<Route path = "/atguigu/home" component = { Home }/>
</Switch>
当加了加了个地址之后,可能会出现样式丢失问题,这是应为,你加了个/atguigu,导致你请求的bootsrap路径错了
3种解决方法
1用HashRouter,只读取井号前面的路径
import React from 'react'
import React from 'react-dom'
import { HashRouter } from 'react-router-dom'
import App from './App'
ReactDOM.render(
<HashRouter>
</App>
</HashRouter>
)
2.加绝对路径 ‘%PUBLIC_URL%’
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
//加绝对路径
<link rel = 'icon' href = '%PUBLIC_URL%/favicon.ico'>
<link rel = 'style' href = '%PUBLIC_URL%/css/bootstrap'>
</head>
<body>
<div id="root"></div>
</body>
</html>
3.去除路径css前面的点(.),这样他就会只找css文件下的文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
//加绝对路径
<link rel = 'icon' href = '/favicon.ico'>
<link rel = 'style' href = '/css/bootstrap'>
</head>
<body>
<div id="root"></div>
</body>
</html>
路由的模糊匹配和严格匹配
模糊匹配
路由默认是模糊匹配,可以比喻为发送地址为商人,接收请求为客户,比如,
//商人
<NavLink to = "/home/a/b">Home</NavLink>
//客户
<Route path = "/home" component = {Home}></Route>
这样写的话可以正常显示,因为客人要的地址商人全都有,但是顺序不能变,比如,
//商人
<NavLink to = "/a/home/b">Home</NavLink>
//客户
<Route path = "/home" component = {Home}></Route>
这样就会报错,且商人只能多给,不能少给,比如,
//商人
<NavLink to = "/home">Home</NavLink>
//客户
<Route path = "/home/a/b" component = {Home}></Route>
也会报错,因为我要的东西你全都没有
严格匹配
如果想要地址匹配必须一模一样,这时可以用exact
//商人
<NavLink to = "/home">Home</NavLink>
//客户
<Route exact path = "/home/a/b" component = {Home}></Route>
Redirect重定向的使用
就是每一个路由都匹配不上的时候,那么就听从Redirect的发落
比如:
import { NavLink, Route, Redirect } from 'react-router-dom'
<NavLink to = "/about">About</NavLink><br/>
<NavLink to = "/home/a/b">Home</NavLink>
<Route path = "/about" component = {About}></Route>
<Route exact path = "/home" component = {Home}></Route>
<Redirect to = "/about"/>
当页面初始加载时,会进行重定向,起到一个默认选项的作用,会重定向到Redirect指定的地址
二级路由
爷组件App
import React, { Component } from 'react'
import { NavLink, Route, Redirect } from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
render() {
return (
<div>
<NavLink to = "/about">About</NavLink><br/>
<NavLink to = "/home">Home</NavLink>
<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
<Redirect to = "/about"/>
</div>
)
}
}
一级路由———父组件About
import React, { Component } from 'react'
export default class About extends Component {
render() {
return (
<h3>我是About的内容</h3>
)
}
}
一级路由———父组件Home
import React, { Component } from 'react'
import Message from './Message/index'
import News from './News/index'
import { NavLink, Route } from 'react-router-dom'
export default class About extends Component {
render() {
return (
<>
<NavLink to = "/home/message">Message</NavLink>
<NavLink to = "/home/news">News</NavLink>
<Route path = "/home/message" component = {Message}/>
<Route path = "/home/news" component = {News}/>
<h3>我是Home的内容</h3>
</>
)
}
}
二级路由———子组件Message
import React, { Component } from 'react'
export default class Message extends Component {
render() {
return (
<h2>我是Message的内容</h2>
)
}
}
二级路由———子组件News
import React, { Component } from 'react'
export default class News extends Component {
render() {
return (
<h2>我是News的内容</h2>
)
}
}
在当前章节知识点中唯一需要注意的一点是,父组件Home用路由给子组件传递地址时需要在地址名前加上App的一级路由名:/home/message和/home/news前面的home,因为它在匹配时会拿着这个地址名去一级路由那里去进行匹配地址,匹配完毕后再来二级路由这里,所以本章节知识点当中不可用严格模式匹配。