尼采般地抒情

尼采般地抒情

尼采般地抒情

音乐盒

站点信息

文章总数目: 321
已运行时间: 1782

官方文档:React 官方中文文档

虚拟DOM以及React挂载DOM的方式

  1. 利用JavaScript脚本操作DOM的行为是性能损耗的大头,故产生虚拟DOM的概念。初始化在内存中得到相应的虚拟DOM树,然后将该结果一次性作用于真实DOM,逻辑层的变动导致部分视图层的改变这渲染逻辑也是经过虚拟DOM加上diff得到一次性结果然后作用于真实DOM,在这里Vue和React都是一样的。
  2. 可以简单理解: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模板来完成相同视图层的逻辑构建)

  1. JSX的识别要么在jsx后缀文件书写相关代码逻辑,要么利用babel工具来进行代码转义..
const data = (
  <div>
    content<span>something...</span>
  </div>
)
root.render(data)
  1. 模板语法:{},Vue是是{{}}
    1. 注释:{/* */}
    2. 内容可以变量、函数、JSX嵌套 ······
  1. JSX注意事项
    1. 严格单标签(<Tab />
    2. 标签名小写
    3. 唯一根节点 -> <></>React.Fragment
    4. 部分HTML属性名变动:class -> className; for -> htmlFor ······
    5. 多单词属性用驼峰方式书写,dataset除外

类组件

  1. 组件的概念也就是模块的拆分、抽象和复用,和Vue组件一致,React的组件编写有两种,类组件函数组件,实际编写代码,React18函数式组件编写更为推崇,但是理解上以类组件会更好。
  2. 具体类组件的编写,其实就是在面向对象编程,给你一个已知类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相关事项

  1. 传入组件的参数可以用扩展运算符来传入
const data = {
  info: 'something,
    getData: (data) => {},
  }
const data = <div><A {...data} /></div>
  1. 单独属性值为true
  2. 类组件的默认值
// 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 })
}
}

如果响应式的值为数组呢?

  1. react里面的响应式的值为不可变数据集合,比较简单的情况可以利用剩余参数来解决该问题。
class A extends React.Component {
  state ={
    a: [1, 2, 3]
  }
  ···
  handleMethod() {
    this.setState({ a: [...a, 4] })
  }
}
  1. 深拷贝

(TODO: 应该有更好的解决方式)

批处理

响应式处理,内部会一次性收集当前变化的量,进入一个处理队列,最终得到一个最终态来进行渲染,避免状态改变多次渲染的情况。

  1. React18之前,在一些函数执行时机下,不会进行批处理,React18之后解决了这个问题。具体有:Promise、setTimeout、原生事件下
  2. 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>
  )
}
}
  1. setState是一个一个异步函数,其第二个参数可以传入回调函数,以便写状态改变之后的相关业务逻辑
  2. 如果传入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提供的渲染优化方式有两种:

  1. PureComponent优化组件
class A extends React.PureComponent {
  ···
}
  1. 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)

  1. 变量
// A component
class A extends React.Component {
  inputRef = React.createRef()
  // this.inputRef.current // -> Input DOM
  render {
  return (
    <div>
      A component
      <input ref={inputRef} />
    </div>
  )
}
}
  1. 回调函数
// 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>
  )
}
}
  1. 除了可以得到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

评论区

什么都不舍弃,什么也改变不了