eventbus使用_EventBus状态巴士

状态管理

?其实各类框架都是以组件为基础的(?web-component也不例外)。而组件的柯里化其实我个人觉得是因人而异的, 一些人可能会以业务(?)为基准, 而有些人会以功能(?)为基准。所以, 不好总体说些什么。但是有一个事情,是无论你的基准是什么都也许或大概率会碰到的。那就是组件之间以及跨组件状态共享、事件共享。

小提醒?

本文的所以示例代码均可在??传送门[1]内查看。简易版的多框架cli。请勿吐槽?

?其实我本来想偷个懒直接都写SFC的,奈何我这简易版的架子有点问题, vue的组件编译成功但是展示不出来。你问我为啥不知道在script中写renderjsx, 你就有点不懂我的初衷了。
所有的props都均使用JS原生类型(React的也不使用propTypes)

/* components/componentName/props.js */export default {  propsName: PropType.oneOfType(JsOriginType) // 样例?}

我们常用的`Vue`[2]组件之间参数互传有下面几种:

  • props
  • customer event
  • slot slotScope
  • vuex
  • eventBus
  • inject provider

今天我们主要讨论的是eventBus?, 并会结合类似的react例子来进行比较,如果你看完本文有其他框架比如angularsvelet等的例子的话,欢迎pr?。

正文

我们以一个评论条目作为拆分主体进行分析并渗透代码。先看具体模样(?有点丑哈,不要介意; 也可能刻意分的很细, 只是为了迎合?体)

预览

f8bd2c3e321bc31eb6175acd215df5b8.png
eventBus

概述

组件Comment主要由CommentAction和其他自有代码组成,其中CommentActionIcon组成。

Comment  - CommentAction    - Icon  - other code

其中, 在CommentAction组件的每个Icon点击的时候, 都会触发事件并反映给最终调用Comment组件的父组件, 触发当前的action事件。

常规思路
  • Vue
  Icon(click -> emit('click'))   =>   CommentAction(recieve('click') -> emit('CommentAction', type))   =>   Comment(recieve('CommentAction', type) => emit('action', type))  =>  Parent(recieve('action', type))
  • React
  Icon(click -> props.onClick())   =>   CommentAction(onClick(props.CommentAction(type))   =>   Comment(CommentAction(props.action(type)))  =>  Parent(action(type))

我们不难看出,为了在最上层的父组件触发action函数, 整个调用链路相当的长, 而且在书写过程中也会存在大量的冗余代码。(?一些看官大人?坐不住了, 不是可以上vuex或者redux么; 又有一些看官大人?说, vueslot不仅减少了参数的传递, 还让组件减少了大部分的事件链路传递, reactrenderProps不香么; 远方又传来声音?, 都是渣渣, 我写在一起,什么父子, 都是一体。)
emmmm~ 远方的声音忽略。至于所说的vuexredux并不适用于UI级别的组件,我们倾向于不和业务过度耦合(什么耦不耦合, 你还不是写的都是业务相关的代码, 你这么较真, 那我还真就是了怎么滴?)。slotrenderProps确实可以实现减少调用链路的情况, 但是我们需要重复书写一部分代码在每次调用的地方(你会不会用啊,说的都是些什么(σ`д′)σ, 你行你来你来?)。当然mixins也是一种解决方式, 但既然已经被抛弃了, 我们就只提一下。
今天, 我们用EventBus的方式来实现调用链路缩短,共享深嵌套组件事件状态分发。
其中React部分使用useReducer来实现, 官方有这么一句话`使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 `[3]

Code
  • `comment.vue`[4]
  
... v-if='!$slots.action' v-model='showAction' :CommentEventBus='CommentEventBus' /> ...
import props from './props'import Icon from '../Icon/icon.vue'import CommentAction from './CommentAction/action.vue'import Vue from 'vue'export default { props, name: 'Comment', components: { Icon, CommentAction }, data() { return { CommentEventBus: null, showAction: false } }, created() { // 创建状态bus监听事件 const CommentEventBus = new Vue() this.CommentEventBus = CommentEventBus CommentEventBus.$on('commentAction', this.commentAction) CommentEventBus.$on('commentActionVisible', this.changeActionVisible) }, methods: { commentAction(type) { this.$emit('action', type) }, changeActionVisible() { this.showAction = !this.showAction } }}
  • `vm.$on(event, callback)`[5]: 使用$on可以在选定时机开始监听实例上的自定义事件, 并在需要时使用$emit来触发。(又有些人坐不住了, 你这个代码用provider+inject不是也?么, 我的os?)。其实说白一点就是利用一个实例来作为props桥接不同深度的子组件和最上层父组件之间的事件链路。
  • `CommentAction/action.vue`[6]
  
class='action' @mouseleave='changeActionVisible' @mouseenter='changeActionVisible' > name='zan' @click='action("zan")' > v-show='value' v-for='iconItem in actionArray' :key='iconItem.name' :name='iconItem.name' @click='action(iconItem.name)' > {{ iconItem.slot }}
...export default { ... methods: { action(type) { this.CommentEventBus.$emit('commentAction', type) }, changeActionVisible() { this.CommentEventBus.$emit('commentActionVisible') } }}
  • `comment.jsx`[7]
import React, {  useState,  useReducer,  useCallback} from 'react'import './index.less'import Icon from '../Icon/icon.jsx'import CommentAction from './CommentAction/action.jsx'// 使用reducer来代替eventBusconst initialState = {  showAction: false}const Comment = ({  comment = {},  commentAction,  children}) => {  function reducer(state, action) {    switch(action.type) {      case 'commentActionVisible':         return { showAction: !state.showAction }      case 'commentAction':        commentAction(action.payload.type)        return state    }  }  const [ state, dispatch ] = useReducer(reducer, initialState)  return (    
... { !children ? value={ state.showAction } CommentEventBus={ dispatch } /> : <> { children } > } ...
)}export default Comment
  • useReducer + dispatch: 其实这个组合拳写redux的时候就有渗透, 但是在hook出来之后, 我们可以在局部组件内营造小范围状态共享, 无需接入总的store tree。这使得代码和项目业务本身的耦合度就大幅度的降低。而一路透传dispatch也让我们的组件无需透传各种参数, 代码线路和逻辑也会更明了。
  • `CommentAction/action.jsx`[8]
import React, { useCallback } from 'react'...const CommentAction = ({  value,  CommentEventBus}) => {  const changeActionVisible = useCallback(() => {    CommentEventBus({      type: 'commentActionVisible'    })  }, [])  const action = useCallback(type => {    CommentEventBus({      type: 'commentAction',      payload: {        type      }    })  }, [])  return (    
className='action' onMouseLeave={ changeActionVisible } onMouseEnter={ changeActionVisible } > name='zan' onClick={ () => action('zan') } > { value && <> { actionArray.map(iconItem => ( key={ iconItem.name } name={ iconItem.name } onClick={ () => action(iconItem.name) } > { iconItem.slot } )) } > }
)}export default CommentAction

结语

当然, EventBus?的用法不仅仅于此, 但举一反三大家还是可以的✿✿ヽ(°▽°)ノ✿。至于React的替代方案当然也不止useReduce一种, 大家也可以选择使用useContext这种的方案以及等等。毕竟实现一个事情方法是千千万万的。以上~

参考资料

[1]

??传送门: https://github.com/innocces/eventBus

[2]

Vue: https://cn.vuejs.org/

[3]

使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数: https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer

[4]

comment.vue: https://github.com/innocces/eventBus/blob/master/src/component/Comment/comment.vue#L56

[5]

vm.$on(event, callback): https://cn.vuejs.org/v2/api/#vm-on

[6]

CommentAction/action.vue: https://github.com/innocces/eventBus/blob/master/src/component/Comment/CommentAction/action.vue#L57

[7]

comment.jsx: https://github.com/innocces/eventBus/blob/master/src/component/Comment/comment.jsx#L23

[8]

CommentAction/action.jsx: https://github.com/innocces/eventBus/blob/master/src/component/Comment/CommentAction/action.jsx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值