37行代码构建无状态组件通讯工具-让恼人的Vuex和Redux滚蛋吧!

状态管理的现状

不少前端开发者认为,VuexRedux是用来解决组件间状态通讯问题的,因此大部分人仅仅是用于达到状态共享的目的。可是一般Redux是用于解决工程性问题的,用于分离业务与视图,让结构更加清晰,从而达到易于维护的目的。也就是 Flux(这里我以前翻译的Flux深度解读)架构所解决的问题。可是绝大多数时候,你们只是想解决的问题是组件嵌套过深的时候,如何将子组件的状态直接传递给父组件。那么此时Vuex也好Redux也好,对于咱们的诉求就过于繁琐。每次通讯后,咱们还须要清理掉Store中的状态。更加恼人的是,咱们该如何选择哪些状态应该放入Store,那些状态应该放在组建内的state一直困扰着你们,甚至于社区也是没有一个定论。所以不少年轻前端工程师所开发的项目,状态管理极其混乱。以致于不久后就难以维护。前端

无状态组件间通讯的由来

针对以上诉求,咱们能不能开发一个简单的组件间通讯工具来解决目前前端状态管理的痛点呢?所以我实现了一个无状态组件通讯工具,这也就是这篇文章的由来。react

无状态,也就是它并不关注数据内容,它只是起到一个管道的做用,在组件间创建管道,组件能够经过该管道向管道另外一头的组件说:“hello world!This is your message。”。git

巧用设计模式

设计模式,你们都很熟悉,现代前端框架已经使用很是多的设计模式,你们都能耳熟能详的就是观察者模式装饰器模式,以及发布订阅模式(一种将观察者和通知者融合的设计模式)。github

设计模式,是用于解决特定问题而被你们公认为最佳实践的模式。通常最被你们熟知的为23种设计模式 - 这里是我用ES2015实现的面向对象方式的设计模式例子面试

那么咱们该如何利用设计模式解决咱们的问题呢?上代码:redux

const listener = {}; // 用于保存订阅者
    
    // 注册订阅者
    function subscribe (event, handle) {
      // 订阅者订阅的信息
      if (typeof event !== 'string') {
        throw new Error('event must be String!');
      }
      // 订阅者的callback函数
      if (typeof handle !== 'function') {
        throw new Error('handle must be function!');
      }
      // 将订阅者添加到订阅者容器中保存起来
      if (!listener[event]) {
        listener[event] = [];
        listener[event].push(handle);
      } else {
        var index = listener[event].indexOf(handle);
        if (index < 0) {
          listener[event].push(handle);
        }
      }
      // 返回用于取消订阅的接口,这里是一个高阶函数
      return function unSubscribe() {
        var index = listener[event].indexOf(handle);
        if (index > -1) {
          listener[event].splice(index, 1);
        }
      }
    }
    // 为通知者提供的发起通知的接口
    function dispatch (event, payload) {
      if (listener[event]) {
        listener[event].forEach(function serviceFunc(handle) {
          handle(payload);
        })
      } else {
        throw new Error('No subscriber be registried for serviceName!');
      }
    }
    
    export {
      subscribe,
      dispatch
    }
复制代码

这里主要使用了一下几种JS语言经常使用的设计模式以及技术知识点:小程序

  • 沙盒模式 在以前一篇文章如何构建一个不到100行的小程序端mini版本redux 中介绍了如何经过沙盒模式构建一个mini小程序版的redux。若是对于沙盒模式还不了解能够参看这篇文章,这里用沙盒模式对用于存储订阅者的变量进行封装和保护。
  • 发布订阅模式 发布(dispatch)订阅(subscribe)模式是一种混合模式,它包含了观察者模式和通知者模式。
  • 高阶函数 这是JS一种常见的知识点,在面试的时候常常会有面试官提问这个技术,可是真正用于实战的并很少,大多都是构建基础架构的高级工程师才有机会使用。

以上,咱们利用沙盒模式,发布订阅模式实现了一个基本的无状态组件间通讯工具。那么咱们如何使用它呢?设计模式

使用无状态工具实现组件间数据通讯

下面是咱们要实现的一个例子:前端框架

组件结构是 爷爷包含儿子,儿子包含孙子,儿子和孙子能够和爷爷直接对话。前端工程师

在根组件(爷爷组件)注册订阅者用来订阅儿子和孙子发来的信息:

import Son from './Son';
    
    import { subscribe } from './utils';
    
    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          messageFromSon: '',
          messageFromGrandson: ''
        }
        // 在这里订阅了儿子的会话和孙子的会话,记得bind(this)这样才能访问组件的上下文
        this.listenSonHandle = this.listenSonHandle.bind(this);
        this.listenGrandsonHandle = this.listenGrandsonHandle.bind(this);
        // 咱们须要保留订阅会话,在不须要的时候取消注册
        this.listenHandle = [
          subscribe('son', this.listenSonHandle),
          subscribe('grandson', this.listenGrandsonHandle)
        ]
      }
    
      listenSonHandle(payload) {
        this.setState({
          messageFromSon: payload
        });
      }
    
      listenGrandsonHandle(payload) {
        this.setState({
          messageFromGrandson: payload
        })
      }
    
      componentWillUnmount() {
        this.listenHandle.forEach((unSubscribe) => {
          unSubscribe();
        })
      }
    
      render() {
        return (
          <div style={{background: 'red'}}> <Son /> <div> 儿子来电:{this.state.messageFromSon} </div> <div> 孙子来电:{this.state.messageFromGrandson} </div> </div>
        );
      }
    }
复制代码

儿子组件须要和爷爷组件直接对话,那么就须要和爷爷组件创建相同的通讯管道:

import React from 'react';
    import { dispatch } from './utils';
    import Grandson from './Grandson';
    
    export default class Son extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          message: ''
        }
      }
    
      render() {
        return <div style={{background: 'green'}}> 这里是儿子: <input value={this.state.message} onChange={(e) => { this.setState({ message: e.target.value }) }}></input> <button onClick={() => { // 利用通知者接口,向爷爷组件发送信息 dispatch('son', this.state.message); }}>告诉老子</button> <Grandson/> </div>
      }
    }
复制代码

孙子组件想要向爷爷组件发送信息,若是不使用redux的话就要一层一层的传递props。先告诉爸爸,而后爸爸告诉爷爷,可是有了咱们如今构建的无状态组件通讯工具。就不须要那么麻烦了:

import React from 'react';
    import { dispatch } from './utils';
    
    export default class Son extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          message: ''
        }
      }
    
      render() {
        return <div style={{background: 'yellow'}}> 这里是孙子: <input value={this.state.message} onChange={(e) => { this.setState({ message: e.target.value }) }}></input> <button onClick={() => { dispatch('grandson', this.state.message); }}>告诉爷爷</button> </div>
      }
    }
复制代码

例子源码

甚至咱们能够很容易再剥离出一层业务层,实现业务与视图的隔离。起到和Vuex,Redux一样的目的。

最后

因为设计模式是语言无关的,所以这个utils/index.js下的代码是能够用于任何前端框架的。

这就是设计模式的强大之处。是否是大家能够扔掉那恼人的Vuex和Redux了呢?

广告:咱们团队招人,途家网,地点在国家会议中心,咱们的团队很年前,才组件1年多。有不少机会。

别人面试造火箭,进去拧螺丝。咱们面试拧螺丝,进来造火箭。咱们招不起能面试造火箭的人(T_T不知道这么说老大会不会打我),因此只能招得起面试能拧螺丝的。可是咱们有不少造火箭的需求。

若是你想寻求刺激,就来加入咱们吧

yahuil_1@tujia.com

相关文章
相关标签/搜索