react基础--1

2022-09-08 18:31:40 浏览数 (1)

render函数如何执行

要调用render肯定要实例化类组件,可是代码中并没有实例化类组件的代码

原因在与,当写入组件标签时,react帮你实例化了类组件

执行 ReactDOM.render发生了什么?

1.react解析组件标签,找到了组件

2.发现组件是函数定义的,随后调用该函数,将返回的虚拟DOM转换为真实DOM,随后展示在页面中

3.发现组件是类定义的随后,new出该类的实例,并通过该实例调用原型上的render方法,将render返回的虚拟DOM转换为真实DOM,随后呈现在页面中。

组件三大核心属性

state

代码语言:javascript复制
class Weather extends React.Component {
    constructor(props) {
        super(props)
        // 通过构造器初始化state
        this.state = {
            isHot: true
        }
    }
    render() {
        return (
            <h1>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
        )
    }
}

基于上面代码实现标题点击动态切换天气状态

组件三大核心属性

state

代码语言:javascript复制
class Weather extends React.Component {
    constructor(props) {
        super(props)
        // 通过构造器初始化state
        this.state = {
            isHot: true
        }
    }
    render() {
        return (
            <h1>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
        )
    }
}

基于上面代码实现标题点击动态切换天气状态

类方法中的this

代码语言:javascript复制
...
  render() {
        return (
            <h1 onClick={this.changeWeather}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
        )
  }
  changeWeather() {
      console.log(this.state.isHot)
  }
...

上述代码在标题被点击的时候,抛出异常 Cannot read property 'state' of undefined 不能在undefine上读取state属性

在changeWeather打印this,却为undefined

代码语言:javascript复制
  changeWeather() {
      console.log(this) // undefined
  }

只有通过类组件实例区调用changWeather时,changeWeather中的this才是类组件实例

既然出行这种情况,那就说明changeWeather不是类组件实例调用的

复习一下类的相关以解释这个问题

代码语言:javascript复制
class Person {
    study() {
        console.log(this)
    }
}
const p1 = new Person()
p1.study() // this执行 Person 实例
const x = p1.study;
x() // 不是类实例调用 this应该指向window,但由于类中的方法会自动开启严格模式,所以this指向undefined

解决类组件的方法的this指向问题

第一种通过bind

代码语言:javascript复制
class Weather extends React.Component {
    constructor(props) {
        super(props)
        // 通过构造器初始化state
        this.state = {
            isHot: true
        }
        // 根据原型对象上的changeWeather 在当前实例生成一个changeWeather 并绑定this
        this.changeWeather = this.changeWeather.bind(this) 
    }
    render() {
        return (
            <h1 onClick={this.changeWeather}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
        )
    }
    changeWeather() {
      console.log(this) // Weather 实例
    }
}

第二种直接将方法定义成类成员,配合箭头函数解决this问题

代码语言:javascript复制
class Weather extends React.Component {
    constructor(props) {
        super(props)
        // 通过构造器初始化state
        this.state = {
            isHot: true
        }
       
    }
    render() {
        return (
            <h1 onClick={this.changeWeather}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
        )
    }

    changeWeather = function() {
        console.log(this) // this 为  undefined
    }

    changeWeather = () =>{
      // 箭头函数没有自己的this ,this为外层this
      console.log(this) // Weather 实例
    }
}

setState

this问题解决了接下来实现 标题点击动态改变天气 这里要通过setState对state进行修改

代码语言:javascript复制
 changeWeather() {
      console.log(this) // Weather 实例
      this.setState({
          isHot: !this.state.isHot
      })
}

state的简写方式

1.在构造器中初始化state改为在类中初始化state

代码语言:javascript复制
constructor(){
    this.state = {isHot: false}
}

改为

代码语言:javascript复制
class Test {
    state = { isHot: false }
}

三大属性之props

代码语言:javascript复制
class Person extends React.Component {
   render() {
       return (
        <span>姓名:{this.props.name}</span>
        <span>性别:{this.props.sex}</span>
       )
   }
}

ReactDOM.render(<Person  name="小王" sex="男"/>)

批量传递props

代码语言:javascript复制
const p = { name:'小王', sex: '男' }
ReactDOM.render(<Person {...p}/>)

需要注意的是展开运算符不能展开对象,这里之所以这样写是React帮我们进行处理

对props进行限制

1.全局引入 prop-typs.js

在代码中使用

代码语言:javascript复制
class Person extends React.Component {
    render () {
        const { name,age,sex } = this.props
        
    }
    
}

Person.propTypes = {
    name: PropTypes.string,
    sex: PropTypes.string.isRequired,
    speak:PropType.func //函数类型
}
// 设置默认值
Person.defaultProps = {
    sex: 'test',
    name: '张三'
}

props的简写形式

props是只读的,只允许读不允许改 1.限制规则简写

代码语言:javascript复制
class Person extends React.Component {
    static propTypes = {
        name: PropTypes.string,
        sex: PropTypes.string.isRequired,
        speak:PropType.func //函数类型
    }
   static  defaultProps = {
        sex: 'test',
        name: '张三'
    }
    render () {
        const { name,age,sex } = this.props
        
    }
    
}

三大属性之refs

1.字符串的refs

代码语言:javascript复制
render() {
    return (
        <div ref="node1"></div>
        <button onClick={this.showData}></button>
    )
}
showData = ()=> {
    console.log(this.refs.node1)
}

2.回调形式的refs

代码语言:javascript复制
render() {
    return (
        <div ref={(currentNode)=>{ this.input1 = currentNode }}></div>
        <button onClick={this.showData}></button>
    )
}
showData = ()=> {
   console.log(this.input1)
}

3.createRef

代码语言:javascript复制
class Demo extends React.Component {
    myRef = React.createRef();
    render() {
    return (
        <div ref={this.myRef}></div>
        <button onClick={this.showData}></button>
        )
    }
    showData = ()=> {
    console.log(this.myRef)
    }
}

类组件的构造器

构造器是否接受props,是否传递给super,取决与,是否希望在构造器中通过this访问props

事件绑定

react将所有原生事件进行了重写,on后面的事件名的第一个首字母要大写 如 onclick 在 react 中 为 onClick

事件绑定注意

代码语言:javascript复制
...
 render() {
    return (
        <h1 onClick={test()}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
    )
  }
...

function test () {
    console.log("hello world")
}

上述代码会导致页面一加载就调用test,其根本原因是,调用render时,发现将test函数的返回值赋给了onClick,所以函数在页面加载时就执行了,接着将undefined作为onClick的回调

纠正

代码语言:javascript复制
...
 render() {
    return (
        <h1 onClick={test}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
    )
  }
...

function test () {
    console.log("hello world")
}

可以看到去掉括号后就正常了,可如果函数需要参数怎么办,这个时候肯定要加小括号

改成如下即可

代码语言:javascript复制
...
 render() {
     let arr = [12,3]
    return (
        <h1 onClick={(data)=> { test(data) }}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
    )
  }
...

function test (data) {
    console.log("hello world",data)
}

函数柯里化:通过函调用继续返回函数的形式,实现多次接收参数最后统一处理的函数编码方式

react生命周期

旧版生命周期

1.初始化阶段:由于ReactDOM.render() 触发-- 初次渲染

1.constructor()

2.componentWillMount()

3.render()

4.componentDidMount()

2.更新阶段:由组件内部this.setState() 或父组件更新render触发

1.shouldComponentUpdate() // 决定组件是否更新

2.componentWillUpdate()

3.render()

4.componentDidUpdate()

3.卸载组件 由ReactDOM.unmountComponentAtNode()触发

1.componentWillUnmount()

新版生命周期

1.初始化阶段:由于ReactDOM.render() 触发-- 初次渲染

1.constructor()

2.getDerivedStateFromProps()

3.render()

4.componentDidMount()

2.更新阶段:由组件内部this.setState() 或父组件更新render触发

1.getDerivedStateFromProps()

2.shouldComponentUpdate() // 决定组件是否更新

3.render()

4.getSnapshotBeforeUpdate() // 马上要更新了获取快照

4.componentDidUpdate()

3.卸载组件 由ReactDOM.unmountComponentAtNode()触发

1.componentWillUnmount()

重要的钩子

render: 初始化渲染或更新渲染调用

componentDidMount 开启监听,发送ajax componentWillMount

父子组件通信

消息订阅与发布 PubSubJS

// 子组件

代码语言:javascript复制
import PubSub from 'pub-sub.js'
PubSub.public('loadData',{name:'test'})

componentDidMount(){
 // 订阅消息,当发布改消息时执行函数体
   this.token = PubSub.subscribe('loadData',(msg ,data)=> {

   })
}
componentWillUnmount() {
    // 取消订阅
    PubSub.unsubscribe(this.token)
}

路由

react-router-dom

import { Link, BrowserRouter} <Link to="/about"/> 在HTML代码实现页面跳转

属性:to 要去的路由

**link标签必须被BrowserRouter或HashRouter 包裹 ** 根据不同路由展示页面使用

代码语言:javascript复制
<BrowserRouter>
<Route path="/about" component={About}/>
<Route path="/test" component={Test}/>
</BrowserRouter>

link必须跟route使用同一个BrowserRouter包裹,才能实现路由管理 **最简单的办法是直接将APP用BrowserRouter 包裹 **

路由组件收到的props是 historylocationmatch

点击导航有状态,如高亮,使用可以使用NavLink

NavLink

将Link替换为NavLink

默认情况下NavLink被点击时会添加一个active类名

<NavLink activeClassName="ac">

activeClassName 该导航激活的类名

NavLink的内容会放在其属性里面的children

通过Switch标签将Route标签包裹起来可实现匹配一个路径就不往下匹配了

路由模糊匹配与精确匹配

<Route exact={true/false}>

redirect

重定向 放在Route标签的下方 当所有路由都没有匹配上时,执行Redirect <Redirect to="home"/>

路由传参

1.params参数 路由链接(携带参数):<Link to='/demo/test/tome/18'>详情</Link> 注册路由(声明参数):<Route path='/demo/test/:name/:age' component={Testt}> 接收参数 this.props.match.params 2.search参数 路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'> 注册路由(无需声明直接注册) <Route path='/demo/test' component={Test}> 接收参数:this.props.location.search 备注:获取到的search是urlencoded需要借助querystring解析

3.state参数 路由链接(携带参数)<Link to={ {pathname:'/demo/test', state:{name:'tom',age:30}} }> 注册路由(无需声明直接注册) <Route path='/demo/test' component={Test}> 接收参数:this.props.location.state 备注:该方式刷新也可以保留参数,地址栏看不到

push 与 replace

路由跳转默认为push

开启replace <Link replace={true}/>

编程路由导航

只有路由组件才有下面的api

代码语言:javascript复制
replaceShow = (id,title) => {
    this.props.history.replace(`/home/message/detail${id}/${title}`) // params
    this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`) // search
    this.props.history.replace(`/home/message/detail`, {id,title}) // state
}
pushShow = (id,title) => {
    this.props.history.push(`/home/message/detail${id}/${title}`)
    // goBack() goForward() go()
}

如何使得一般组件也能使用路由组件的api ?

withRouter

代码语言:javascript复制
import { withRouter } from 'react-router-dom'
class Header extends Component{
    render() {
        return (
            <div onClick={()=> replaceShow}></div>
        )
    }
    replaceShow() {
        ...
    }
}

// 注意导出
export default withRouter(Header)

经过上面的操作 Header组件就可以调用路由组件的api了

BrowserRouter与HashRouter

前端路由的操作原理就是点击链接引其浏览器url的变化(通过BOM的历史) 在监听到这个变化,然后在路由变化的时候执行一些操作

1. 动力原理 BrowserRouter使用H5的历史API 2. 没有追踪rie9的以下 HashRouter使用URL的哈希值 。path形式 B的路径没有表现#H 的路径有# 3. 刷新对路由状态参数的影响 B 4.没有任何影响,因为保存在历史记录中的对象 会导致路径中的状态 遗留问题

0 人点赞