react组件使用结构赋值语法时是怎么解析的?
function A () {
return <h1>aaa</h1>
}
ReactDom.render(<A {...{a:1}} />, document.getElementById('id'))
balel转义后
"use strict";
function A() {
return React.createElement("h1", null, "aaa");
}
ReactDom.render(React.createElement(A, {
a: 1
}), document.getElementById('id'));
什么是jsx?
jsx是js和html的混合语法,是javascript的一种语法拓展。
什么是纯函数?
- 对入参没有修改
- 不影响除当前作用域以外的变量。
- 没有副作用
- 同一个输入永远得到同一个输出,也就是无状态。
改变this指向三种方式
- this.add.bind
- {() => {this.add()}}
- add = () => {}
ref
ref-> reffer 引用的意思,通过什么来引用这个元素(react组件/原生节点)
有三种方式使用,string/ function/ React.createRef,前两种将被废弃
非受控组件和受控组件
非受控组件数据存储在dom上,受控组件数据存储在react中。
比如input,如果设置了value为this.state.value = 'xxx',同时就必须有onChange事件通过修改state来改变input的值,否则就会修改不掉。如果是受控组件,可以设置默认值defaultValue,不能设置value,这样的话。数据就存储在input节点上。
react的父类的抽象
将setState、props这类属性和方法抽象在一个父基类里面,子类继承的时候就可以调用。
元素事件的监听
React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行。
setState是异步的吗?
源码解析
不是,源码中是根据当前阶段是否为创建或者更新组件时期将isBatchingUpdates变量设置为true。意味着这个节点可能会有很多setState的操作,react将这些操作存储在一个数组队列中。等当前生命周期或者交互事件(可能更新组件)执行完了,再将isBatchingUpdates设为false。此时再调用setState就是同步的了。所以,setState为异步只是react为了减少dom渲染频率而做的一些控制让我们看起来是异步的而已。
react中事务的概念
和面向切面编程的概念很接近,都是在做一件事情前后都做些什么,来达到分离无关代码的目的。在setstate中就是做了isBatchingUpdates状态的控制。
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
*
为什么不把请求放在componentwillmount或者constructor中?
- react16.0以后,因为fiber机制,componentWillMount可能会被执行多次。
- 其实是可以放在comstructor中发请求,只是因为代码规范统一再加上官方推荐而都放在componentDidMount上。
新版生命周期和旧版本有什么区别?
去掉了componentWillMount、componentWillReceiveProps
和componentWillUpdate,新增了getDrivedStateFromProps和getSnapshotBeforeUpdate
getDrivedStateFromProps的作用是?
子组件在 render 中有的使用内部state,有的使用props,不统一,现在统一使用state,这个方法return值作为state。而不需要通过setstate来重新渲染。
getSnapshotBeforeUpdate的作用是?
获取组件更新前的快照,比如上方数据视图增多,页面自动滚动问题处理。
propTypes
propTypes是用来校验传给组件的数据是否合法,state数据是自己定义的,所以不需要校验。
import React from 'react';
import PropTypes from 'prop-types';
class MyComponent extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired
}
render() {
// ... do things with the props
}
}
context
为了解决多层级组件props传递的问题,不过官方不建议使用,因为在老的Context中由上而下的“触发链”有可能被shouldComponentUpdate打断。如果父组件和爷爷组件context同名属性,爷爷的属性会就近原则被父组件覆盖。
新版本的React.createContext实现思路
pureComponent
帮我们封装了shouldComponentUpdate方法,进行浅比较(深比较浪费性能,不划算)。如果前后数据不相同才会return true进行组件的重新渲染。一个注意点是下面这种写法会造成每次渲染的时候都生成一个新的函数(每次都通过bind调用生成)。这样会影响浅比较判断。
render () {
<button onCLick={this.onClick.bind(this)}></button>
}
所以还是使用
onClick = () => {}
render () {
<button onCLick={this.onClick.}></button>
}
React.memo就是将函数组件放在类组件的render函数中返回渲染。
function memo (func) {
return class A extends React.pureComponent {
render () {
return <Func {...this.props} />
// return func(this.props)
}
}
}
高阶组件
高阶组件就是一个函数,接收一个组件,返回一个组件。高阶组件的作用就是为了代码的复用。
高阶函数的来历,为什么叫高阶?是因为其他语言比如java里面不能将函数作为其他函数的参数,也不能将函数作为返回值。所以js中称作为参数的函数为告捷函数。
这里有个重要的点:需要被增强的组件里的生命周期函数不会被覆盖掉,因为这是两个不同的组件,而且,被增强的组件是最为子组件进行渲染,因为父组件会等待子组件渲染完毕才会进入componentDidMount,所以可以正确计时(想要计算子组件渲染所花费时间)。
import React from 'react';
import ReactDOM from 'react-dom';
class A extends React.Component {
static defaultProps = { // 这个属性是放在类上的,相当于 A.a = 11111,同时可以继承给子元素。
a:11111
}
xxx = 1 // 这样是表示实例上的属性。this.xxx = 1
constructor (props) {
super(props)
this.iptRef = React.createRef()
this.state = {
num: 0
}
}
componentDidMount() {
console.log('99999')
}
componentWillMount() {
console.log('123456')
}
render () {
return (
<div>
{this.state.num}
</div>
)
}
}
class B extends React.Component {
componentDidMount() {
console.log('111199')
}
componentWillMount() {
console.log('222256')
}
render () {
return <A {...this.props} />
}
}
ReactDOM.render(<B /> , document.getElementById('root'));
高阶组件存在的问题:多层嵌套,思路比较绕,容易出bug。(hooks解决了)
import React from 'react';
export default function (Component) {
//从属性对象中接收到了一个val属性,存放着英文名,调用接口取得中文名,然后作为value属性传给了Component
return class extends React.Component {
constructor() {
super();
this.state = {value: ''};
}
componentDidMount() {
fetch('http://localhost:3000/translation.json').then(response=>response.json()).then(result=>{
debugger;
this.setState({value:result[this.props.val]});
})
//{"zhangsan":"张三","lisi":"李四"}
}
render() {
return <Component {...this.props} value={this.state.value}/>
}
}
}
import React from 'react';
export default function (Component, name) {
return class extends React.Component {
constructor() {
super();
this.state = {val: ''};
}
componentDidMount() {
this.setState({
val: localStorage.getItem(name)
});
}
render() {
return <Component {...this.props} val={this.state.val}/>
}
}
}
import React, { Component } from 'react'
import withLocal from './withLocal';
import withAjax from './withAjax';
class UserNameInput extends Component {
render() {
return (
<input defaultValue={this.props.value}/>
)
}
}
//高阶组件的多层嵌套也是hooks解决的问题之一
let UserNameInputWithAjax=withAjax(UserNameInput);
let UserNameInputWithLocal=withLocal(UserNameInputWithAjax,'username');
export default UserNameInputWithLocal;
render props
render props
是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。render prop 是一个用于告知组件需要渲染什么内容的函数 prop。目的是为了做代码的抽象。将公共代码抽象到父组件,然后将父组件中的数据传到子组件中。高阶组件是一样的逻辑。