一文教会你用redux实现computed计算属性

目录
  • 前言
  • 原理分析:
  • 总结

前言

什么是computed计算属性?它会根据所依赖的数据动态显示新的计算结果, 该计算结果会被缓存起来。如果是Vue开发者,对这个功能并不陌生,而且很常用。对于React开发者,如果用过mobx,那其实也不陌生,一个装饰器就生效了。那如果是Redux呢??(沉默中。。。)有了,reselect嘛,哈哈。啪,骗子,这是假的计算属性,它要手动提供全部依赖,每个依赖都是一个函数回调确定依赖值,每次写这么多代码是有多想敲坏我的机械键盘(嘶吼)。

这么说,redux和计算属性无缘?也不能这么说,办法总比困难多。虽然redux是单向数据流,无法做响应式操作,不过,我们可以创造出一个监听对象

import { Store } from 'redux';

const collector = [];

class ObjectDeps {
  protected readonly deps: string[];
  protected readonly name: string;
  protected readonly store: Store;
  protected snapshot: any;

  constructor(store: Store, name: string, deps: string[] = []) {
    this.store = store;
    this.name = name;
    this.deps = deps;
    collector.push(this);
  }

  proxy(currentState) {
    if (state === null || typeof state != 'object') return state;

    const proxyData = Array.isArray(state) : [] : {};
    const currentDeps = this.deps.slice();
    const keys = Object.keys(currentState);

    for (let i = keys.length; i-- > 0; ) {
      const key = keys[i]!;

      Object.defineProperty(proxyData, key, {
        enumerable: true,
        get: () => {
          if (visited) {
            return new ObjectDeps(
              this.store,
              this.name,
              currentDeps.slice(),
            ).proxy(currentState)[key];
          }

          visited = true;
          this.deps.push(key);
          return this.proxy((this.snapshot = currentState[key]));
        },
      });
    }
  }
}

朴实无华,没有基于ES6的Proxy,因为兼容性不好。既然是前端的应用,自然是要照顾到ES5的环境的,因此选择defineProerty是个不错的方案。

有了监听驱动,那监听岂不是易如反掌?

// 假设user里的对象为:{ firstName: 'lady', lastName: 'gaga' }
const userState = store.getState()['user'];

function computedFullName() {
  const proxy = new ObjectDeps(store, 'user').proxy(userState);
  return proxy.firstName + '-' + proxy.lastName;
}

const fullname = computedFullName();

现在我们看看collector里收集到多少个依赖

console.log(collector); // [ ObjectDeps, ObjectDeps ]

不错,两条依赖,第一条的deps链为['user', 'firstName'],第二条为['user', 'lastName']。

原理分析:

  • 每次创建proxy时,构造函数均会执行collector.push(this)向采集器加入自己。
  • proxy访问firstName时,其实访问的是getter,getter中有一条this.deps.push(key)立即收集依赖,并返回下一级的proxy值。以此类推,即使是proxy.a.b.c.d这种深度操作也来者不拒,因为每次访问下一级都能收集依赖并合并到deps数组中。
  • proxy访问lastName时,由于proxy实例其实已经被firstName占用了(通过visited变量判断),所以getter逻辑中会直接返回一个新的ObjectDeps实例,此时lastName已经和我们看到的proxy变量没有任何关系了。

自动收集依赖已经实现了,我们试一下如何缓存属性

class ObjectDeps {
  protected snapshot: any;

  proxy() {...}

  isDirty() {
    return this.snapshot !== this.getSnapshot();
  }

  protected getSnapshot() {
    const deps = this.deps;
    let snapshot = this.store.getState();

    for (let i = 0; i < deps.length; ++i) {
      if (snapshot == null || typeof snapshot !== 'object') {
        break;
      }
      snapshot = snapshot[deps[i]!];
    }

    return snapshot;
  }
}

通过isDirty()的判断,即再次获得deps下的最新值和旧值做对比,便可以知道这个依赖是否为脏值。这一步便是缓存的关键。

现在你相信reselect是骗子了吧,明明可以自动依赖,非要多写几行代码增加心智负担?拜托,不是每个人都需要KPI压力的。

老师,我想直接在项目中使用上这个什么computed属性,应该去哪里找现成的呢?废话,当然是去山东找蓝翔。看看蓝翔大法:

import { defineModel, useComputed } from 'foca';

export const userModel = defineModel('user', {
  initialState: {
    firstName: 'lady',
    lastName: 'gaga',
  },
  computed: {
    // 清爽
    fullName() {
      return this.state.firstName + '-' + this.state.lastName;
    },
  },
});

// App.tsx
const App: FC = () => {
  const fullName = useComputed(userModel.fullName);

  return <div>{fullName}</div>;
};

嗯?刚刚发生了什么,好像看到dva飞过去?飞你个头,是哥写的React状态管理库foca,基于redux和react-redux,刚才的computed解析就是从里面摘抄的(具体实现逻辑请看这里)。虽然是个软广告,不过redux也算是支持computed了,各位大佬就不要天天喷redux这个不好那个不好了行吧,二次封装才是真爱。

人生苦短,手握神器,少写代码,早点下班最要紧:https://github.com/foca-js/foca

总结

到此这篇关于用redux实现computed计算属性的文章就介绍到这了,更多相关redux实现computed计算属性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解如何优雅地在React项目中使用Redux

    前言 或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处,本文不会安利大家使用Redux 概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在React项目中使用Redux react-thunk 中间件,作用:支持异步action 目录结构 Tips:与Redux无关的目录已省略 |--src |-- store Redux目录

  • 详解react、redux、react-redux之间的关系

    本文介绍了react.redux.react-redux之间的关系,分享给大家,也给自己留个笔记,具体如下: React 一些小型项目,只使用 React 完全够用了,数据管理使用props.state即可,那什么时候需要引入Redux呢? 当渲染一个组件的数据是通过props从父组件中获取时,通常情况下是 A --> B,但随着业务复杂度的增加,有可能是这样的:A --> B --> C --> D --> E,E需要的数据需要从A那里通过props传递过来,以及对应的 E

  • 简单介绍react redux的中间件的使用

    用过react的同学都知道在redux的存在,redux就是一种前端用来存储数据的仓库,并对改仓库进行增删改查操作的一种框架,它不仅仅适用于react,也使用于其他前端框架.研究过redux源码的人都觉得该源码很精妙,而本博文就针对redux中对中间件的处理进行介绍. 在讲redux中间件之前,先用两张图来大致介绍一下redux的基本原理: 图中就是redux的基本流程,这里就不细说. 一般在react中不仅仅利用redux,还利用到react-redux: react-redux这里也不细说.

  • 浅谈redux以及react-redux简单实现

    写在前头 redux 简介 随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态). 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等. 管理不断变化的 state 非常困难.如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,

  • React/Redux应用使用Async/Await的方法

    Async/Await是尚未正式公布的ES7标准新特性.简而言之,就是让你以同步方法的思维编写异步代码.对于前端,异步任务代码的编写经历了 callback 到现在流行的 Promise ,最终会进化为 Async/Await .虽然这个特性尚未正式发布,但是利用babel polyfill我们已经可以在应用中使用它了. 现在假设一个简单的React/Redux应用,我将引入 Async/Await 到其代码. Actions 此例子中有一个创建新文章的 Action ,传统方法是利用 Prom

  • 一文教会你用redux实现computed计算属性

    目录 前言 原理分析: 总结 前言 什么是computed计算属性?它会根据所依赖的数据动态显示新的计算结果, 该计算结果会被缓存起来.如果是Vue开发者,对这个功能并不陌生,而且很常用.对于React开发者,如果用过mobx,那其实也不陌生,一个装饰器就生效了.那如果是Redux呢??(沉默中...)有了,reselect嘛,哈哈.啪,骗子,这是假的计算属性,它要手动提供全部依赖,每个依赖都是一个函数回调确定依赖值,每次写这么多代码是有多想敲坏我的机械键盘(嘶吼). 这么说,redux和计算属

  • Vue computed计算属性的使用方法

    computed computed:相当于method,返回function内return的值赋值在html的DOM上.但是多个{{}}使用了computed,computed内的function也只执行一次.仅当function内涉及到Vue实例绑定的data的值的改变,function才会从新执行,并修改DOM上的内容. computed和method的对比 <div id="example"> {{ message.split('').reverse().join('

  • 深入理解Vue Computed计算属性原理

    Computed 计算属性是 Vue 中常用的一个功能,但你理解它是怎么工作的吗? 拿官网简单的例子来看一下: <div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm =

  • Vue computed 计算属性代码实例

    什么是计算属性??? 1.在computed中,可以定义一些属性,这些属性叫做[计算属性] 2.计算属性的本质是一个方法,不过一般是将他们的名称直接当做属性使用,不会当方法调用 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="../lib/vue-2.4.0.js"></scri

  • Vue中computed计算属性和data数据获取方式

    目录 computed计算属性和data数据获取 解决方法一 解决方法二 computed计算属性取对象的值,第一次报错undefined 报错和打印值 解决方案 computed计算属性和data数据获取 获取到数据(对象.数组),截取一部分显示到页面中,用computed计算属性来实现截取数据然后直接输出到页面. <div class="detailBox"> <h1 class="detailHead">{{ActiveData.tit

  • Vue的computed计算属性你了解吗

    目录 computed计算属性 1.什么是计算属性 2.为什么要用计算属性 3.compute.methods和watch三者的区别 4.案例:遍历数组对象的时候进行监视 总结 computed计算属性 1.什么是计算属性 计算属性 本质是方法,只是在使用这些 计算属性 的时候,把他们的名称直接当作 属性 来使用,并不会把 计算属性 当作方法去调用,不需要加小括号 ()调用. 2.为什么要用计算属性 当你需要一个属性是需要经过一些计算的,比如你要一个discounted折扣后的钱属性,现在有pr

  • Vue computed计算属性详细讲解

    目录 一.计算属性computed 1.1.什么是计算属性computed 1.2.复杂数据的处理-computed 1.3.计算属性的缓存 1.4.计算属性computed的setter和getter 一.计算属性computed 1.1.什么是计算属性computed computed 是基于它的依赖缓存,只有在它的相关依赖发生改变时才会进行更新.官方文档是这样说的:对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性 1.2.复杂数据的处理-computed 拼接字符串.分数是否及格.m

  • Vue中computed(计算属性)和watch(监听属性)的用法及区别说明

    目录 计算属性computed 侦听属性watch 计算属性computed 支持缓存,只有依赖数据发生改变,才会重新进行计算 不支持异步,当computed内有异步操作时无效,无法监听数据的变化 computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed 如果computed属性属性值是函数,

  • 详解Vue的computed(计算属性)使用实例之TodoList

    最近倒腾了一会vue,有点迷惑其中methods与computed这两个属性的区别,所以试着写了TodoList这个demo,(好土掩面逃~); 1. methods methods类似react中组件的方法,不同的是vue采用的与html绑定事件. 给个例子 /*html*/ <input type="button" value="点击" v-on:click='handlClick' id="app"> /*js*/ var ap

  • Vue 计算属性 computed

    目录 1.基础例子 2.计算属性缓存 vs 方法 3.计算属性的 setter 前言: 一般情况下属性都是放到data中的,但是有些属性可能是需要经过一些逻辑计算后才能得出来,那么我们可以把这类属性变成计算属性. 比如以下: <div id="example"> {{ message.split('').reverse().join('') }} </div> 在这个地方,模板不再是简单的声明式逻辑.你必须看一段时间才能意识到,这里是想要显示变量 message

随机推荐