官方文档:React 官方中文文档
虚拟DOM以及React挂载DOM的方式
- 利用JavaScript脚本操作DOM的行为是性能损耗的大头,故产生虚拟DOM的概念。初始化在内存中得到相应的虚拟DOM树,然后将该结果一次性作用于真实DOM,逻辑层的变动导致部分视图层的改变这渲染逻辑也是经过虚拟DOM加上diff得到一次性结果然后作用于真实DOM,在这里Vue和React都是一样的。
- 可以简单理解:react模块旨在完成虚拟dom的相关逻辑,react-dom模块旨在完成真实DOM和diff的处理
// mount container
rootReactDOM.createRoot(document.querySelector('#root'))
// create VDOM
const data = React.createElement('div', {}, 'data')
// VDOM -> true DOM
root.render(data)
JSX
上述ReactDOM生成虚拟DOM的方式,实际编写代码不方便,于是出现了类似JSX来简化视图层的代码编写(Vue中利用Template模板来完成相同视图层的逻辑构建)
- JSX的识别要么在jsx后缀文件书写相关代码逻辑,要么利用babel工具来进行代码转义..
const data = (
<div>
content<span>something...</span>
</div>
)
root.render(data)
- 模板语法:
{}
,Vue是是{{}}
- 注释:
{/* */}
- 内容可以变量、函数、JSX嵌套 ······
- JSX注意事项
- 严格单标签(
<Tab />
) - 标签名小写
- 唯一根节点 ->
<></>
或React.Fragment
- 部分HTML属性名变动:
class -> className
;for -> htmlFor
······ - 多单词属性用驼峰方式书写,dataset除外
类组件
- 组件的概念也就是模块的拆分、抽象和复用,和Vue组件一致,React的组件编写有两种,类组件和函数组件,实际编写代码,React18函数式组件编写更为推崇,但是理解上以类组件会更好。
- 具体类组件的编写,其实就是在面向对象编程,给你一个已知类React.Component,写具体逻辑时,遵照所继承的类的一些规则来扩展业务代码。
简单的父子通信
// A component
class A extends React.Component {
constructor(props) {
super(props)
this.props = props
}
render {
this.props.getData('data')
return (
<div>A component {this.props.info} </div>
)
}
}
// B component
const getData = (data) => {
// result -> data
}
const data = <div>content <A info='something' getData={getData} /> </div>
props相关事项
- 传入组件的参数可以用扩展运算符来传入
const data = {
info: 'something,
getData: (data) => {},
}
const data = <div><A {...data} /></div>
- 单独属性值为true
- 类组件的默认值
// A component
class A extends React.Component {
static defaultProps = {
info: 'default info',
}
constructor(props) {
super(props)
this.props = props
}
render {
this.props.getData('data')
return (
<div>A component {this.props.info} </div>
)
}
}
响应式变量
前端框架必须具体的一个能力就是响应式变量的构造,以及内部实现的动态渲染,react的响应式变量就是构造类的一个私有变量state,再利用其规定指定的方法setState方法来实现响应式
class A extends React.Component {
state {
a: 1,
}
static defaultProps = {
info: 'default info',
}
constructor(props) {
super(props)
this.props = props
}
render {
this.props.getData('data')
return (
<div>A component {this.props.info} </div>
)
}
handleMethod() {
this.setState({ a: 2 })
}
}
如果响应式的值为数组呢?
- react里面的响应式的值为不可变数据集合,比较简单的情况可以利用剩余参数来解决该问题。
class A extends React.Component {
state ={
a: [1, 2, 3]
}
···
handleMethod() {
this.setState({ a: [...a, 4] })
}
}
- 深拷贝
(TODO: 应该有更好的解决方式)
批处理
响应式处理,内部会一次性收集当前变化的量,进入一个处理队列,最终得到一个最终态来进行渲染,避免状态改变多次渲染的情况。
- React18之前,在一些函数执行时机下,不会进行批处理,React18之后解决了这个问题。具体有:Promise、setTimeout、原生事件下
- React提供了防止批处理的函数
class A extends React.Component {
state ={
a: 1
}
···
handleMethod() {
ReactDOM.flushSync(() => {
this.setState({ a: this.state.a + 1 })
this.setState({ a: this.state.a + 1 })
})
// result: run twice render method...
}
render {
console.info('react render.')
return (
<div>A component {this.props.info} </div>
)
}
}
- setState是一个一个异步函数,其第二个参数可以传入回调函数,以便写状态改变之后的相关业务逻辑
- 如果传入setState的值是对象,那么会在响应式内部所收集变化的量里面进行覆盖,如果需要实时根据最新变化的量做逻辑处理,则需要传入一个参数为state的回调函数
class A extends React.Component {
state ={
a: 1
}
···
handleMethod() {
// 1. cover state
this.setState({ a: this.state.a + 1 })
this.setState({ a: this.state.a + 1 })
// result: a -> 2
// 2. real time state
this.setState((state) => { a: this.state.a + 1 })
this.setState((state) => { a: this.state.a + 1 })
// result: a -> 3
}
}
PureComponent shouldComponentUpdate
下面代码,因响应式变量变化,所以会执行一次render渲染
class A extends React.Component {
state ={
a: 1
}
···
handleMethod() {
this.setState({ a: 1 })
}
render {
console.info('react render.')
return (
<div>A component {this.props.info} </div>
)
}
}
上面情况其实响应式的结果都是a变为1,所以从渲染结果上看其实可以优化掉该种情况的渲染,react提供的渲染优化方式有两种:
- PureComponent优化组件
class A extends React.PureComponent {
···
}
- shouldComponentUpdate生命周期函数
class A extends React.Component {
state ={
a: 1
}
···
shouldComponentUpdate = (nextProps, nextState) => {
if (nextState.a === this.state.a)
return false // not render
return true // run render
}
render {
console.info('react render.')
return (
<div>A component {this.props.info} </div>
)
}
}
Refs
框架只是对渲染dom的前置操作做了一层封装,比如先构造虚拟dom或是渲染时机的优化等,但有些时候需要在业务代码里面直接获取dom进行操作,比如input标签的自动聚焦(focus)
- 变量
// A component
class A extends React.Component {
inputRef = React.createRef()
// this.inputRef.current // -> Input DOM
render {
return (
<div>
A component
<input ref={inputRef} />
</div>
)
}
}
- 回调函数
// A component
class A extends React.Component {
inputRef = (dom) => {
dom.focus()
}
// this.inputRef.current // -> Input DOM
render {
return (
<div>
A component
<input ref={inputRef} />
</div>
)
}
}
- 除了可以得到DOM,也可以得到类组件的实例对象
// A component
class A extends React.Component {
···
}
// B component
class B extends React.Component {
refData = React.createRef()
render() {
return <div>content <A ref={refData} /> </div>
}
}
生命周期
组件模式:RenderProps
class A extends React.Component {
state = {
x: 0,
y: 0,
}
constructor(props) {
super(props)
this.props = props
}
componentDidMount = () => {
document.addEventListener('mousemove', this.move)
}
componentWillUnMount = () => {
document.removeEventListener('mousemove', this.move)
}
move = (e) => {
this.setState({
x: e.pageX,
y: e.pageY,
})
}
render() {
return (
<React.Fragment>
{ this.props.render(this.state.x, this.state.y)}
</React.Fragment>
)
}
}
class B extends React.Component {
render() {
return <A render={(x, y) => {
<div>{x}, {y}</div>
}} />
}
}
组件模式:HOC
参数为组件,返回新组件
function hocFun(WithComponent) {
return class extends React.Component {
state = {
x: 0,
y: 0,
}
componentDidMount = () => {
document.addEventListener('mousemove', this.move)
}
componentWillUnMount = () => {
document.removeEventListener('mousemove', this.move)
}
move = (e) => {
this.setState({
x: e.pageX,
y: e.pageY,
})
}
render() {
return <WithComponent {...this.state} />
}
}
}
class A extends React.Component {
render() {
return (
<div>
{this.props.x}, {this.props.y}
</div>
)
}
}
const C = hocFun(A)
class B extends React.Component {
render() {
return <C />
}
}
Context通信
const ContextData = React.createContext()
class A extends React.Component {
state = {
info: 'something'
}
render() {
return (
A
<ContextData.Provider value={this.state.info}>
<B />
</ContextData.Provider>
)
}
}
class B extends React.Component {
render() {
return (
B<C />
)
}
}
class C extends React.Component {
static contextType = ContextData
componentDidMount = () => {
console.lof(this.context)
}
render() {
return (
C
<ContextData.Consumer>{ value => value }</ContextData.Consumer>
)
}
}
// render -> ABCsomething
评论区