AnnVoV / blog

24 stars 2 forks source link

读《探索Vue高阶组件》 #11

Open AnnVoV opened 6 years ago

AnnVoV commented 6 years ago

HOC 组件的特点

React 中的高阶函数(High Order Component)

比如我们要把一个ajax获取数据的功能放到一个高阶组件里

// APILoader.js
// 首先高阶函数的意思就是通过一个函数,传入一个函数,得到另一个函数So
import api from 'component/api'
import React from 'react'

function AjaxLoader() {
  // 首先这里要返回个函数
  return function(MyComponent) {
    class APILoader extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          ajaxData: {}
        }
      }
      componentDidMount() {
        // 异步加载数据
        api.then((data)=> {
          this.setState({
            ajaxData: data
          })
        })
      }
      render() {
        return (
          <MyComponent data={this.state.ajaxData}/>
        )
      }
    }
    return APILoader
  }
}
export default AjaxLoader;
// BookLoader.js
import APILoader from './APILoader'
import React from 'react'
class Book extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    const {name, author} = this.props;
    return (
      <section>
        <div>{name}</div>
        <div>{author}</div>
      </section>
    )
  }
}
export default APILoader()(Book)

mixin 与 HOC 对比

![IMAGE](quiver-image-url/1B82D7CF4D0F1E8308C7CB963525AC1F.jpg =652x309)

在mixin中,组件和mixin之间是相互感知的。基于在多个mixin同时作用于一个组件上时,mixin之间也可能相互影响。这个就像复杂的多角恋关系。

在HOC中,组件对包裹自己的HOC毫不知情,只知道接受自己想要的props,可以单纯地作自己组件份内的事情。要给组件附加的特定逻辑(包括props、methods、computed等),都在HOC中实现和管理。HOC更像是一个暗恋者,默默地为组件做了一些事情,组件却不知道(也没必要知道)。

Vue中如何实现HOC

ps: 关于slot 的部分如何解决,我还没有搞明白。。。 vue 中一般我们都用Mixin,那在Vue中如何实现HOC组件呢?(vue中hoc的实现依赖于render函数,先回顾下render函数)

render函数中的Vnode数据对象

  {
    // v-bind: class
    'class': {
      foo: true,
      bar: false
    },
    // v-bind: style
    style: {
      color: 'red',
      fontSize: '14px'
    },
    // 正常的HTML特性
    attrs: {
      id: 'foo'
    },
    // 组件props
    props: {
      myProp: 'bar'
    },
    // DOM属性
    domProps: {
      innerHTML: 'baz'
    },
    // 事件监听基于on
    on: {
      click: this.clickHandler
    }
    // 自定义指令
    directives: [
      {
        name: 'my-custom-directive',
        value: '2',
        expression: '1 + 1',
        arg: 'foo',
        modifiers: {
          bar: true
        }
      }
    ]
    // 作用域插槽
    // { name: props => VNode | Array<VNode> }
    scopedSlots: {
      default:props => createElement('span', props.text)
    },
    // 如果组件是其他组件的子组件,需为插槽指定名称
    slot: 'name-of-slot',
    // 其他特殊顶层属性
    key: 'myKey',
    ref: 'myRef'
  }

hoc组件

import Ajax from 'api/ajax';
const hoc = (component) => {
  // component 上的props
  const inheritedProps = component.props || [];
  return {
    render(createElement) {
      return createElement(component, {
        props: {
          ...inheritedProps,
          data: this.fetchedData
        }
      })
    },
    // hoc 的props和component保持一致
    props: [...inheritedProps],
    data() {
      return {
        fetchedData: {}
      }
    },
    mounted() {
      Ajax.get('/user/getUserInfo')
        .then((data) => {
          this.fetchedData = data;
        });
    }
  }
}
export default hoc;

baseComponent组件

<template>
    <div>
        <span>name:{{data}}</span>
        <button>click</button>
    </div>
</template>

<script>
    export default {
        props: ['url', 'data'],
        mounted() {
            console.log('来自baseComp的mounted');
        }
    }
</script>

app.js

import hoc from './hoc';
import BaseComp from './baseComp'

export default {
  ...
  components: {
    'enhance-comp': hoc(BaseComp)
  }
}

深入阅读资料:

http://hcysun.me/2018/01/05/%E6%8E%A2%E7%B4%A2Vue%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6/