zhoujin4515 / Blog

1 stars 0 forks source link

react class 简单实现 #16

Open zhoujin4515 opened 3 years ago

zhoujin4515 commented 3 years ago

react class 简单实现

我们的html结构

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
  </head>
  <body>
    <div id="root">
      <div class='wrapper'>
      </div>
    </div>
  </body>
</html>

创建一个可复用的点赞按钮组件

  1. 先创建一个通过字符串生成dom节点的函数
    // 通过字符串生成dom元素
    function domFormStr(str) {
    let dom = document.createElement('div')
    dom.innerHTML = str || ''
    return dom
    }
  2. 创建一个点赞按钮的组件

    class niubiButton {
    constructor() {
    this.state = {
      // state保存状态
      isLiked: false
    }
    }
    
    // 更新state
    setState(state) {
    const oldEl = this.el
    this.state = state
    this.el = this.render()
    // 实例有当状态变化回调函数时,更新dom
    this.onStateChange && this.onStateChange(oldEl, this.el)
    }
    
    // dom节点的点击回调,更新state
    changeNiubiText() {
    this.setState({
      isLiked: !this.state.isLiked
    })
    }
    
    // 生成对象的dom节点,给dom节点绑定点击事件
    render() {
    this.el = domFormStr(`
      <button class='niubi-btn'>
        <span class='niubi-text'>${this.state.isLiked ? '牛逼' : '一般'}</span>
        <span>?</span>
      </button>
    `)
    this.el.addEventListener('click', ()=> {
      this.changeNiubiText()
    })
    return this.el
    }
    }
  3. 使用该类创建一个点赞按钮,并挂载到document中
    // 生成实例
    const niubiButton = new niubiButton()
    // 挂载实例dom
    wrapper.appendChild(niubiButton1.render())
    // 创建实例dom click 回调函数
    niubiButton.onStateChange = function(oldEl, newEl ) {
    // 这个是粗暴的重置,diff算法就是优化这个步骤性能的
    wrapper.insertBefore(newEl, oldEl) // 插入新的元素
    wrapper.removeChild(oldEl) // 移除旧的元素
    }

上面这个方式创建的点赞组件只需要引入 niubiButton 类,生成一个实例,挂载到页面中就可以使用了,不需要手动操作dom,复用功能比较完善。但是有一个小问题,当需要创建一个新对象的时候,比如一个收藏按钮,我们需要再写一次setState方法。可以把它抽出来

抽象出 Component 类

  1. 创建 Component 类

    class Component {
    constructor(props = {}) {
    this.props = props
    }
    
    setState(state) {
    const oldEl = this.el
    this.state = state
    this.el = this.renderDOM()
    
    this.onStateChange && this.onStateChange(oldEl, this.el)
    }
    
    renderDOM() {
    this.el = domFormStr(this.render())
    this.onClick && this.el.addEventListener('click', this.onClick.bind(this), false)
    return this.el
    }
    }

    2.挂载方法

    const mount = (wrapper, component) => {
    wrapper.appendChild(component.renderDOM())
    component.onStateChange = (oldEl, newEl) => {
    wrapper.insertBefore(newEl, oldEl)
    wrapper.removeChild(oldEl)
    }
    }

    3.继承Component类,创建一个点赞按钮组件

class LikeButton extends Component { constructor(props) { super(props) this.state = { isLiked: false } }

onClick() { this.setState( { isLiked: !this.state.isLiked } ) }

render() { return `

`

} }

4. 使用组件
```javascript
const islikeButton = new LikeButton({
  word: 'hello'
})

mount(wrapper, islikeButton)
zhoujin4515 commented 3 years ago

如果我们需要写另外一个组件,只需要像上面那样,简单地继承一下 Component 类就好了:

class RedBlueButton extends Component {
    constructor (props) {
      super(props)
      this.state = {
        color: 'red'
      }
    }

    onClick () {
      this.setState({
        color: 'blue'
      })
    }

    render () {
      return `
        <div style='color: ${this.state.color};'>${this.state.color}</div>
      `
    }
}

const redBlueButton = new RedBlueButton()

mount(wrapper, redBlueButton)