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是 history
、location
、match
点击导航有状态,如高亮,使用可以使用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.没有任何影响,因为保存在历史记录中的对象 会导致路径中的状态 遗留问题