Для чого потрібна труба в rxJS


103

Я думаю, що я маю базову концепцію, але є деякі незрозумілі ситуації

Отже, загалом я використовую спостережуване:

observable.subscribe(x => {

})

Якщо я хочу відфільтрувати дані, я можу використовувати це:

import { first, last, map, reduce, find, skipWhile } from 'rxjs/operators';
observable.pipe(
    map(x => {return x}),
    first()
    ).subscribe(x => {

})

Я також можу зробити це:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';

observable.map(x => {return x}).first().subscribe(x => {

})

Тож мої запитання:

  1. Яка різниця?
  2. Якщо різниці немає, чому функціональна труба існує?
  3. Чому ці функції потребують різного імпорту?

1
Я збирався сказати, що це для нестандартних операторів, але я навіть не знаю, чи це правильно. Чи pipe()дозволяє вам передавати створені вами оператори?
zero298

Відповіді:


69

Оператори "pipable" (колишні "дозволені") є поточним та рекомендованим способом використання операторів, починаючи з RxJS 5.5.

Настійно рекомендую прочитати офіційну документацію https://rxjs.dev/guide/v6/pipeable-operators

Основна відмінність полягає в тому, що легше створювати власні оператори і що це зручніше для обробки дерев, не змінюючи якийсь глобальний Observableоб'єкт, який може спричинити зіткнення, якщо дві різні сторони хочуть створити оператор з однаковим ім'ям.

Використання окремого importоператора для кожного оператора 'rxjs/add/operator/first'було способом зробити менші пакети додатків. Імпортуючи лише потрібні оператори замість усієї бібліотеки RxJS, ви можете значно зменшити загальний розмір пакета. Однак компілятор не може знати, чи імпортували ви, 'rxjs/add/operator/first'тому що він вам справді потрібен у вашому коді, або ви просто забули його видалити під час рефакторингу коду. Це одна з переваг використання піпабельних операторів, де невикористаний імпорт ігнорується автоматично.


1
Щодо вашого підтвердження unused imports are ignored automatically, на даний час IDE мають плагіни, які видаляють невикористаний імпорт.
silvanasono

Не всі використовують ці IDE чи ці плагіни, багато людей використовують базовий текстовий редактор. Можливо, більшу частину часу ми не можемо покластися на твердження, що всі в команді використовують той самий IDE / набір плагінів / текстовий редактор, що і ми.
Адам Фарина,

3
@AdamFaryna, звичайно, деякі команди також можуть писати код на папері, але навіщо це робити, якщо вони мають сучасні інструменти? Використання текстового редактора, особливо без важливих плагінів, схоже на написання коду на папері. Ви можете це зробити, але чому це зробить будь-яка пристойна команда / розробник
Denes Papp

Редактор коду @DenesPapp не має значення, оскільки люди можуть використовувати його продуктивно. Крім цього, це лише особисті уподобання. Ваша аналогія з написанням коду на папері неточна, ви не можете виконати код на папері, але код, написаний у будь-якому текстовому редакторі, може бути виконаний.
Адам Фарина

1
@perymimon Ви можете, але вам доведеться встановити rxjs-compatпакет github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…
Мартін,

16

Трубний метод

Відповідно до оригінальної документації

оператор pipable полягає в тому, що функція приймає спостережувані як вхідні дані, і вона повертає інше спостережуване. попереднє спостережуване залишається незміненим.

pipe(...fns: UnaryFunction<any, any>[]): UnaryFunction<any, any>

Оригінальна публікація

Що означає труба?

Це означає, що будь-які оператори, які ви раніше використовували в екземплярі Observable, доступні як чисті функції rxjs/operators. Це робить складання композиції операторів або повторне використання операторів стає по-справжньому простим, без необхідності вдаватися до всілякої гімнастики програмування, де вам доведеться створити власне спостережуване розширення Observable, а потім переписати ліфт, щоб зробити власну власну річ.

const { Observable } = require('rxjs/Rx')
const { filter, map, reduce,  } = require('rxjs/operators')
const { pipe } = require('rxjs/Rx')

const filterOutWithEvens = filter(x => x % 2)
const doubleByValue = x => map(value => value * x);
const sumValue = reduce((acc, next) => acc + next, 0);
const source$ = Observable.range(0, 10)

source$.pipe(
  filterOutWithEvens, 
  doubleByValue(2), 
  sumValue)
  .subscribe(console.log); // 50

@VladKuts змінює коди та задані атрибути. Вибачте за незручності.
Chanaka Weerasinghe

Дякую, я навіть не підозрював, що можу зберігати оператори, що підтримують конвеєр, як посилання на функції та використовувати їх у виклику pipe (). Це набагато чистіше, ніж завжди робити це в режимі онлайн.
Олексій.

9

Хорошим підсумком, який я придумав, є:

Він відокремлює потокові операції (картографування, фільтрування, зменшення ...) від основної функціональності (підписка, конвеєр). Проводячи трубопровідні операції замість ланцюжка, це не забруднює прототип Observable, полегшуючи тремтіння дерев.

Див. Https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#why

Проблеми з виправленими операторами для ланцюгових точок:

Будь-яка бібліотека, яка імпортує оператор виправлення, збільшить Observable.prototype для всіх споживачів цієї бібліотеки, створюючи сліпі залежності. Якщо бібліотека скасовує їх використання, вони несвідомо ламають усіх інших. З конвеєрами ви повинні імпортувати потрібні оператори в кожен файл, в якому ви їх використовуєте.

Оператори, виправлені безпосередньо на прототипі, не піддаються "струшуванню дерев" такими інструментами, як зведення та веб-пакет. Оператори, що піддаються конвеєруванню, будуть такими, як просто функції, втягнуті з модулів безпосередньо.

Невикористані оператори, які імпортуються в додатках, не можуть бути надійно виявлені за допомогою будь-якого інструменту збірки або правила збитків. Це означає, що ви можете імпортувати сканування, але припиніть його використовувати, і воно все ще додається до вихідного пакета. Якщо ви не використовуєте його, використовуючи трубопровідні оператори, правило lint може підібрати його вам.

Функціональний склад чудовий. Створення власних користувацьких операторів стає набагато простіше, і тепер вони працюють і виглядають так само, як і всі інші оператори з rxjs. Вам більше не потрібно розширювати спостережуваний або перевизначити підйомник.


8

Яка різниця? Як ви бачите у своєму прикладі, головна відмінність полягає в покращенні читабельності вихідного коду. У вашому прикладі є лише дві функції, але уявіть, чи існує десяток функцій? то піде як

function1().function2().function3().function4()

це справді стає потворним і важким для читання, особливо коли ви заповнюєте всередині функцій. Крім того, деякі редактори, такі як код Visual Studio, не дозволяють довжину більше 140 рядків. але якщо це буде наступним чином.

Observable.pipe(
function1(),
function2(),
function3(),
function4()
)

Це різко покращує читабельність.

Якщо різниці немає, чому функціональна труба існує? Призначення функції PIPE () полягає в об'єднанні всіх функцій, які приймають і повертають спостережувані. Спочатку потрібно спостережуване, а потім це спостережуване використовується у всій функції pipe () кожною функцією, яка використовується всередині нього.

Перша функція приймає спостережуване, обробляє його, модифікує його значення і переходить до наступної функції, потім наступна функція бере вихідний спостережуваний першої функції, обробляє його і переходить до наступної функції, потім вона продовжується, поки всі функції всередині функції pipe () використовуйте це спостережуване, нарешті, у вас є оброблене спостережуване. В кінці ви можете виконати спостережливу функцію subscribe (), щоб витягти з неї значення. Пам'ятайте, значення в оригіналі спостережуваних не змінюються. !! 

Чому ці функції потребують різного імпорту? Імпорт залежить від того, де функція вказана в пакеті rxjs. Це проходить так. Всі модулі зберігаються в папці node_modules в Angular. імпортувати {class} з "модуля";

Візьмемо наступний код як приклад. Я щойно писав це у стекбліці. Отже, ніщо автоматично не генерується і не копіюється звідкись ще. Я не бачу сенсу копіювати те, що зазначено в документації rxjs, коли ви також можете це прочитати. Я припускаю, що ви задали це питання тут, тому що не розуміли документації. 

  • Є конвеєрні, спостережувані, класи класів карт, імпортовані з відповідних модулів. 
  • В тілі класу я використовував функцію Pipe (), як показано в коді. 
  • Функція Of () повертає спостережуване, яке випускає цифри послідовно, коли воно передплачене.

  • На спостережну ще не підписані.

  • Коли ви використовували його, подобається Observable.pipe (), функція pipe () використовує даний Observable як вхід.

  • Перша функція map () використовує функцію Observable, обробляє її, повертає оброблений Observable назад у функцію pipe (),

  • тоді цей оброблений Observable надається наступній функції, якщо вона є,

  • і це триває так, поки всі функції не обробляють Observable,

  • в кінці того, що Observable повертається функцією pipe () до змінної, у наступному прикладі її obs.

Зараз справа в Observable полягає в тому, що поки спостерігач не підписався, він не видає жодного значення. Тож я використав функцію subscribe (), щоб підписатись на цей Observable, і як тільки я його підписав. Функція () починає випускати значення, потім вони обробляються через функцію pipe (), і ви отримуєте кінцевий результат в кінці, наприклад 1 береться з функції (), 1 додається 1 у функцію map () і повернувся назад. Ви можете отримати це значення як аргумент усередині функції Subscribe (function ( argument ) {}).

Якщо ви хочете надрукувати його, тоді використовує як

subscribe( function (argument) {
    console.log(argument)
   } 
)
    import { Component, OnInit } from '@angular/core';
    import { pipe } from 'rxjs';
    import { Observable, of } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent implements OnInit  {
    
      obs = of(1,2,3).pipe(
      map(x => x + 1),
      ); 
    
      constructor() { }
    
      ngOnInit(){  
        this.obs.subscribe(value => console.log(value))
      }
    }

https://stackblitz.com/edit/angular-ivy-plifkg

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.