首页 > 其他分享 >RxJS 系列 – 实战练习

RxJS 系列 – 实战练习

时间:2022-10-14 19:45:48浏览次数:85  
标签:实战 style const opening 练习 height RxJS cardWrapper document

前言

这篇主要是给一些简单例子, 从中体会 RxJS 在管理上的思路. 

 

Slide Down Effect with Dynamic Content

我在这篇 CSS & JS Effect – FAQ Accordion & Slide Down 讲过如何实现 slide down with dynamic content.

效果大概是这样的

RxJS 的思路步骤

元素 > 事件 > 状态 > 渲染

1. 先把涉及到的元素找出来. 比如上面的 open button, close button, add more button, card 等等

2. 把监听的事件找出来, 比如 click, transitionend 等等

3. 抽象出状态, 然后通过事件改变状态. 比如 Status: 'opening' | 'opened' | 'closing' | 'closed'

opening 表示正要打开, opened 表示已经打开, closing 表示正要关闭, closed 表示已经关了.

依据上面的操作那么就是 closed > opening > opened > closing > closed, 当然依据用户的操作也可以是 closed > opening > closing > closed (在还没有完全打开的时候 user 就点击了关闭)

4. 依据状态对元素进行渲染 (修改 DOM)

RxJS 之管理

从上面几个步骤可以发现它带有 mvvm 的思想, 也有 redux 那种 state management 的味道. 

拆分步骤有几个好处

1. 出现 bug 的时候容易排查定位.

2. 实现的时候可以一步一来.

坏处就是复杂了一些. 所以这其实是一个取舍. 如果你的项目没有遇到 bug 难定位, 不用分段实现 (为了休息, 缓口气) 的话, 其实不用 RxJS 也是 ok 的.

Without RxJS 版本

const openBtn = document.querySelector('.open-btn')!;
const cardWrapper = document.querySelector<HTMLElement>('.card-wrapper')!;
openBtn.addEventListener('click', () => {
  cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
});

const closeBtn = document.querySelector('.close-btn')!;
closeBtn.addEventListener('click', () => {
  if (cardWrapper.style.height === 'auto') {
    cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
    requestAnimationFrame(() => {
      cardWrapper.style.removeProperty('height');
    });
  } else {
    cardWrapper.style.removeProperty('height');
  }
});

cardWrapper.addEventListener('transitionend', () => {
  if (cardWrapper.style.height !== '') {
    cardWrapper.style.height = 'auto';
  }
});

const addMoreBtn = document.querySelector('.add-more-btn')!;
const description = document.querySelector('.description')!;
addMoreBtn.addEventListener('click', () => {
  description.textContent = `${description.textContent}\n${description.textContent}`;
});

简单明了, 就是监听然后操作 DOM, 需要判断的地方直接读取 DOM 当前的状态.

RxJS 版本

先把 element 抓出来

import { fromEvent, map, merge, pairwise, startWith, withLatestFrom } from 'rxjs';

const openBtn = document.querySelector('.open-btn')!;
const closeBtn = document.querySelector('.close-btn')!;
const cardWrapper = document.querySelector<HTMLElement>('.card-wrapper')!;
const description = document.querySelector('.description')!;

监听 event 并转换成 state (状态)

type Status = 'opening' | 'opened' | 'closing' | 'closed';
const opening$ = fromEvent(openBtn, 'click').pipe(map<Event, Status>(() => 'opening')); const closing$ = fromEvent(closeBtn, 'click').pipe(map<Event, Status>(() => 'closing')); const openingOrClosing$ = merge(opening$, closing$);
const transitionend$ = fromEvent(cardWrapper, 'transitionend');
const openedOrClosed$ = transitionend$.pipe( withLatestFrom(openingOrClosing$), map(([_event, openingOrClosing]) => (openingOrClosing === 'opening' ? 'opened' : 'closed')) );
const status$ = merge(openingOrClosing$, openedOrClosed$).pipe( startWith<Status>('closed'), pairwise() );

渲染

status$.subscribe(([prevStatus, currStatus]) => {
  switch (currStatus) {
    case 'opening':
      cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
      break;
    case 'opened':
      cardWrapper.style.height = 'auto';
      break;
    case 'closing':
      {
        if (prevStatus === 'opening') {
          cardWrapper.style.height = '0';
        } else {
          cardWrapper.style.height = `${cardWrapper.scrollHeight}px`;
          requestAnimationFrame(() => {
            cardWrapper.style.height = '0';
          });
        }
      }
      break;
  }
});

这里和 pure JS 最大的不同是, 它不是通过读取 DOM 发现当前是什么状态的, 而是通过 RxJS 把之前的状态缓存了起来.

这样代码就很好理解了.

最后补上

const addMoreBtn = document.querySelector('.add-more-btn')!;
addMoreBtn.addEventListener('click', () => {
  description.textContent = `${description.textContent}\n${description.textContent}`;
});

由于这个很简单所以不需要用 RxJS 来实现.

 

标签:实战,style,const,opening,练习,height,RxJS,cardWrapper,document
From: https://www.cnblogs.com/keatkeat/p/16792773.html

相关文章

  • [Unit testing RxJS] Test error handling with marbles
    const{TestScheduler}=require("rxjs/testing");const{map,take,delay,mapTo,catchError}=require("rxjs/operators");const{concat,from,of}=requ......
  • 运算符练习
    #1.选择工资不在5000到12000的员工的姓名和工资SELECTlast_name,salaryFROMemployees#wheresalarynotbetween5000and12000;WHEREsalary<5000ORsalary......
  • 包装类练习——有一个字符串:“91 27 46 38 50”,请写程序实现最终输出结果是:“27 38 46
    packagepackage2;importjava.util.Arrays;//:有一个字符串:“9127463850”,//请写程序实现最终输出结果是:“2738465091"publicclassTest2{publicst......
  • Python实战—基于KNN算法尾鸢花数据集分类
    KNN模型理论K最近邻分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中......
  • Python实战—自行车租赁数据分析
    本节选取自行车的租赁数据,利用numpy、pandas、matplotlib三个库,数据清洗后,做数据分析,研究时间段与自行车租赁的关系。数据来源  本节以自行车的租赁数据为例,数据来源于网......
  • ​MATLAB实战—最优Copula函数的选择
    Copula函数模型本文讲解Copula函数在实际生活中的应用,Copula函数描述的是变量间的相关性,实际上是一类将联合分布函数与它们各自的边缘分布函数连接在一起的函数,因此也有人将......
  • R实战—相关矩阵可视化
    近几天毕业生相继离校了你们走了,我好继承华水的食堂在这里,祝毕业生一路顺风愿此去前程似锦再相逢依旧如故今天讲相关分析,并将得出的相关矩阵可视化。dvdf相关关系是一种非确......
  • Python实战—单词量评估
    今天,2019年上半年的四六级成绩出来了你过了吗?点击链接进行成绩查询​​http://cet.neea.edu.cn/cet/​​对于四六级的成绩总是几家欢乐几家愁如果这次没过下次一定要加油鸭!......
  • 546 JDBC练习_insert语句和547JDBC练习_update语句
    JDBC练习_insert语句publicstaticvoidmain(String[]args)throwsException{Statementstmt=null;Connectionconn=null;try{......
  • java阶乘的实现(scanner使用练习)
    publicclasstest{publicstaticvoidmain(String[]args){System.out.println("请输入计算x!的x值");Scannerscanner=newScanner(System.i......