react相关知识点
事件
事件绑定
// 第一种
this.handle = this.handle.bind(this)
// <p onClick={this.handle}>aaa</p>
handle(){}
// 第二种
// <p onClick={this.handle}>aaa</p>
handle = () => {}
event
clickHandler = (event) => {
event.preventDefault() // 阻止默认行为
event.stopPropagation() // 阻止冒泡
console.log('target', event.target) // 指向当前元素,即当前元素触发
console.log('current target', event.currentTarget) // 指向当前元素,假象!!!
// 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件
console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent
console.log('event.__proto__.constructor', event.__proto__.constructor)
// 原生 event 如下。其 __proto__.constructor 是 MouseEvent
console.log('nativeEvent', event.nativeEvent)
console.log('nativeEvent target', event.nativeEvent.target) // 指向当前元素,即当前元素触发
console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!
// 1. event 是 SyntheticEvent ,模拟出来 DOM 事件所有能力
// 2. event.nativeEvent 是原生事件对象
// 3. 所有的事件,都被挂载到 document 上
// 4. 和 DOM 事件不一样,和 Vue 事件也不一样
}
合成事件
- 为什么要合成事件机制?
1.更好的兼容性和跨平台
2.载到document,减少内存消耗,避免频繁解绑
3.方便事件的统一管理
性能优化
shouldComponentUpdate()
- 1.默认父组件更新,子组件也会更新
- 2.所以使用shouldComponentUpdate,来避免部分子组件不需要更新而跟着父组件更新了
- 3.shouldComponentUpdate需要的时候才用
- 4.shouldComponentUpdate默认返回true
shouldComponentUpdate(nextProps,nextState){
if(nextProps.count !== this.props.count){
return true; // 可以渲染
}
return false; // 不可以渲染
}
PureComponent
- 1.PureComponent中,shouldComponentUpdate实现了浅比较
- 2.在class组件中使用
React.memo
- 1.相当于函数组件中的PureComponent
组件
函数组件
- 1.纯函数,输入props,输出jsx
- 2.没有实例,没有生命周期,没有state
- 3.不能扩展
function List(props){
const { list } = this.props;
return (
list.map(item => {
return <div key={item.id}>{item.name}</div>
})
)
}
非受控组件
- 1.不受state控制,只初始化state,通过ref方式获取改变的值
- 2.ref ,defaultValue defaultChecked, 手动操作dom元素
constructor(){
state = {
name: 'aaa'
}
this.nameRef = React.createRef()
}
handle(){
const ele = this.nameRef.current; // 获取 ref
console.log(ele.value)
}
<input ref={this.nameRef} defaultValue={this.state.name} />
- 3.非受控组件使用场景:
- 1.必须手动操作dom元素,setstate实现不了
- 2.文件上传
- 3.富文本编辑器
受控组件vs非受控组件
- 1.优先使用受控组件
- 2.必须操作dom时,再使用非受控组件
异步组件
- import() React.lazy React.Suspense
// React.lazy
const Demo = React.lazy(() => import('./App.js'))
// React.Suspense
<React.Suspense fallback={loading}>
<Demo />
</React.Suspense>
高阶组件
// 基本用法
import React from 'react'
// 高阶组件
const withMouse = (Component) => {
class withMouseComponent extends React.Component {
constructor(props) {
super(props)
this.state = { x: 0, y: 0 }
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
{/* 1. 透传所有 props 2. 增加 mouse 属性 */}
<Component {...this.props} mouse={this.state}/>
</div>
)
}
}
return withMouseComponent
}
const App = (props) => {
const a = props.a
const { x, y } = props.mouse // 接收 mouse 属性
return (
<div style={{ height: '500px' }}>
<h1>The mouse position is ({x}, {y})</h1>
<p>{a}</p>
</div>
)
}
export default withMouse(App) // 返回高阶函数
Render Props
import React from 'react'
import PropTypes from 'prop-types'
class Mouse extends React.Component {
constructor(props) {
super(props)
this.state = { x: 0, y: 0 }
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
{/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
{this.props.render(this.state)}
</div>
)
}
}
Mouse.propTypes = {
render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数
}
const App = (props) => (
<div style={{ height: '500px' }}>
<p>{props.a}</p>
<Mouse render={
/* render 是一个函数组件 */
({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
}/>
</div>
)
/**
* 即,定义了 Mouse 组件,只有获取 x y 的能力。
* 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。
*/
export default App
介绍一下虚拟DOM
React组件如何通讯
jsx本质是什么
Context是什么,有何用途
多层组件传递属性,用context
import React from 'react'
// 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')
// 底层组件 - 函数是组件
function ThemeLink (props) {
// const theme = this.context // 会报错。函数式组件没有实例,即没有 this
// 函数式组件可以使用 Consumer
return <ThemeContext.Consumer>
{ value => <p>link's theme is {value}</p> }
</ThemeContext.Consumer>
}
// 底层组件 - class 组件
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
render() {
const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
return <div>
<p>button's theme is {theme}</p>
</div>
}
}
ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
<ThemeLink />
</div>
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
render() {
return <ThemeContext.Provider value={this.state.theme}>
<Toolbar />
<hr/>
<button onClick={this.changeTheme}>change theme</button>
</ThemeContext.Provider>
}
changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light'
})
}
}
export default App
渲染列表为何用key
- 必须用key,且不能是index和random
- diff算法中通过tag和key来判断,是否是sameNode
- 减少渲染次数,提升渲染性能
jsx本质
1.React.createElement 即h函数,返回vnode
2.第一个参数,可能是组件,也可能是html tag
3.组件名,首字母必须大写
Portals
- 例如做一个Modal组件,需要把modal节点放在body第一层,使用createPortal
// index.js
<Modal>MODAL</Modal>
// Modal.js
import React from 'react'
import ReactDom from 'react-dom';
class Demo React.Component {
render(){
return ReactDom.createPortal(
<div className="modal">this.props.childen</div>,
document.body // dom 节点
)
}
}
- Portal使用场景:
1.overflow:hidden
2.父组件:z-index太小
3.fixed需要放在body第一层
什么是纯函数
什么是受控组件
何时使用异步组件
多个组件有公共逻辑,如何抽离
redux如何进行异步请求
react-router如何配置懒加载
PureComponent有何区别
react和vue区别
react事件和dom事件区别
生命周期
描述redux单项数据流
setState是同步还是异步
不可变值
- 1.不能直接修改state,使用setState
// 数组 this.setState({ count: this.state.count.concat(100) })
// 对象
this.setState({
obj:{…this.state.obj,{a:10}}
})
+ 2.可能是异步更新:
```js
state = {
count: 0
}
// 第一种 ,同步
this.setState({
count: this.state.count+1
})
console.log(this.state.count) // 0
// 第二种,异步,回调函数
this.setState({
count: this.state.count+1
},() => {
console.log(this.state.count) // 1
})
// 第三种,在setTimeout中是,同步,不需要回调函数
setTimeout(() => {
this.setState({
count: this.state.count+1
})
console.log(this.state.count) // 1
},0)
// 第四种,自定义的dom事件,setState是同步
componentDidMount(){
document.body.addEventListener('click',() => {
this.setState({
count: this.state.count+1
})
console.log(this.state.count) // 1
})
}
- 3.可能会被合并:
// 传入对象,会被合并。执行3次,结果还是1 this.setState({ count:this.state.count+1 }) this.setState({ count:this.state.count+1 }) this.setState({ count:this.state.count+1 })
// 传入函数,不会被合并。执行结果是3
this.setState((prevState,props) => {
return {
count:prevState + 1
}
})
this.setState((prevState,props) => {
return {
count:prevState + 1
}
})
this.setState((prevState,props) => {
return {
count:prevState + 1
}
})
```
setstate到底是异步还是同步
1.setstate无所谓是同步还是异步
2.看是否能命中batchUpdate机制
3.判断isBatchingUpdates
哪些不能名字batchupdate机制
1.setTimeout
2.自定义的dom事件
vdom和diff
只比较同一层级,不跨级比较
tag不相同,则直接删除重建,不再深度比较
tag和key相同,则一样
基于react设计一个todolist(组件结构,redux state 数据结构)
jsx如何渲染页面
setstate之后如何更新页面
react源码解析
react 里如何做动态加载
- React.lazy ,另外通过 webpack 的动态加载:import() 和 ensure.require