Ống dẫn trong rxJS là gì


104

Tôi nghĩ rằng tôi có khái niệm cơ bản, nhưng có một số điều khó hiểu

Vì vậy, nói chung đây là cách tôi sử dụng một cách có thể quan sát được:

observable.subscribe(x => {

})

Nếu tôi muốn lọc dữ liệu, tôi có thể sử dụng cái này:

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

})

Tôi cũng có thể làm điều này:

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

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

})

Vì vậy, câu hỏi của tôi là:

  1. Sự khác biệt là gì?
  2. Nếu không có sự khác biệt, tại sao đường ống chức năng tồn tại?
  3. Tại sao những chức năng đó cần nhập khẩu khác nhau?

1
Tôi đã định nói rằng nó dành cho các toán tử tùy chỉnh, không phải gốc, nhưng tôi thậm chí không biết liệu điều đó có đúng hay không. Có pipe()cho phép bạn chuyển các toán tử mà bạn tạo không?
0298

Câu trả lời:


69

Các toán tử "pipable" (trước đây là "lettable") là cách sử dụng toán tử hiện tại và được khuyến nghị kể từ RxJS 5.5.

Tôi thực sự khuyên bạn nên đọc tài liệu chính thức https://rxjs.dev/guide/v6/pipable-operators

Sự khác biệt chính là việc tạo các toán tử tùy chỉnh dễ dàng hơn và có thể chia cây tốt hơn trong khi không làm thay đổi một số Observableđối tượng toàn cục có thể gây ra xung đột nếu hai bên khác nhau muốn tạo một toán tử cùng tên.

Sử dụng importcâu lệnh riêng biệt cho từng toán tử 'rxjs/add/operator/first'là một cách để tạo các gói ứng dụng nhỏ hơn. Bằng cách chỉ nhập các toán tử bạn cần thay vì toàn bộ thư viện RxJS, bạn có thể giảm đáng kể tổng kích thước gói. Tuy nhiên, trình biên dịch không thể biết liệu bạn đã nhập hay chưa 'rxjs/add/operator/first'vì bạn thực sự cần nó trong mã của mình hoặc bạn quên xóa nó khi cấu trúc lại mã của mình. Đó là một trong những lợi thế của việc sử dụng các toán tử có thể chuyển đổi trong đó các nhập không sử dụng được tự động bỏ qua.


1
Về khẳng định của bạn unused imports are ignored automatically, hiện tại các IDE đều có plugin loại bỏ các nhập không sử dụng.
silvanasono

Không phải ai cũng sử dụng các IDE này hoặc các plugin này, nhiều người sử dụng trình soạn thảo văn bản cơ bản. Có lẽ hầu hết thời gian chúng tôi không thể chuyển tiếp tuyên bố rằng mọi thành viên trong nhóm đang sử dụng cùng một IDE / bộ plugin / trình soạn thảo văn bản như chúng tôi.
Adam Faryna

3
@AdamFaryna chắc chắn, một số nhóm cũng có thể viết mã trên giấy, nhưng tại sao họ lại viết nếu họ có sẵn các công cụ hiện đại? Sử dụng trình soạn thảo văn bản, đặc biệt là không có các plugin quan trọng cũng tương tự như viết mã trên giấy. Bạn có thể làm điều đó nhưng tại sao bất kỳ đội bóng phong nha / nhà phát triển sẽ làm điều đó
Dénes Papp

Trình chỉnh sửa mã @DenesPapp không quan trọng miễn là mọi người có thể sử dụng nó một cách hiệu quả. Ngoài ra, đó chỉ là sở thích cá nhân. Sự tương tự của bạn với việc viết mã trên giấy là không chính xác, bạn không thể thực thi mã trên giấy nhưng mã được viết trong bất kỳ trình soạn thảo văn bản nào có thể được thực thi.
Adam Faryna

1
@perymimon Bạn có thể nhưng bạn phải cài đặt rxjs-compatgói github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…
martin

16

Phương pháp ống

Theo tài liệu gốc

toán tử có thể phân phối là hàm đó nhận các có thể quan sát làm đầu vào và nó trả về một khả năng quan sát có thể quan sát khác.

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

Bài gốc

Ống có nghĩa là gì?

Điều đó có nghĩa là bất kỳ toán tử nào bạn đã sử dụng trước đây trên trường hợp có thể quan sát được đều có sẵn dưới dạng các hàm thuần túy rxjs/operators. Điều này làm cho việc xây dựng thành phần các toán tử hoặc sử dụng lại các toán tử trở nên thực sự dễ dàng, mà không cần phải sử dụng đến tất cả các loại thể dục lập trình, nơi bạn phải tạo một mở rộng có thể quan sát tùy chỉnh có thể quan sát được, sau đó ghi đè thang máy chỉ để tạo thứ tùy chỉnh của riêng bạn.

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 thay đổi mã và các thuộc tính đã cho. Xin lỗi vì sự bất tiện này.
Chanaka Weerasinghe

Cảm ơn bạn, tôi thậm chí còn không nhận ra rằng tôi có thể lưu trữ các toán tử có thể pipe làm tham chiếu hàm và sử dụng chúng trong lệnh gọi pipe (). Điều đó gọn gàng hơn nhiều so với việc luôn làm nội tuyến.
Alex. Một

9

Một bản tóm tắt hay mà tôi đã đưa ra là:

Nó tách các hoạt động phát trực tuyến (bản đồ, bộ lọc, thu nhỏ ...) khỏi chức năng cốt lõi (đăng ký, đường ống). Bằng cách vận hành đường ống thay vì chuỗi, nó không làm ô nhiễm nguyên mẫu của Observable, giúp việc rung cây dễ dàng hơn.

Xem https://github.com/ReactiveX/rxjs/blob/master/doc/pipable-operators.md#why

Các vấn đề với các toán tử được vá cho chuỗi chấm là:

Bất kỳ thư viện nào nhập một toán tử vá lỗi sẽ tăng cường Observable.prototype cho tất cả người dùng của thư viện đó, tạo ra các phụ thuộc mù. Nếu thư viện loại bỏ cách sử dụng của họ, họ sẽ vô tình phá vỡ những người khác. Với pipeables, bạn phải nhập các toán tử bạn cần vào mỗi tệp bạn sử dụng chúng.

Các nhà khai thác được vá trực tiếp trên nguyên mẫu không thể "rung cây" bằng các công cụ như cuộn lên hoặc gói web. Các toán tử Pipable sẽ giống như chúng chỉ là các hàm được lấy trực tiếp từ các mô-đun.

Các toán tử không sử dụng đang được nhập trong ứng dụng không thể được phát hiện một cách đáng tin cậy bởi bất kỳ loại công cụ xây dựng hoặc quy tắc xơ vải nào. Điều đó có nghĩa là bạn có thể nhập bản quét, nhưng hãy ngừng sử dụng nó và nó vẫn được thêm vào gói đầu ra của bạn. Với các toán tử có thể phân phối, nếu bạn không sử dụng nó, một quy tắc lint có thể chọn nó cho bạn.

Thành phần chức năng là tuyệt vời. Việc xây dựng các toán tử tùy chỉnh của riêng bạn trở nên dễ dàng hơn rất nhiều và giờ đây chúng hoạt động và trông giống như tất cả các toán tử khác từ rxjs. Bạn không cần phải mở rộng mức tăng có thể quan sát hoặc ghi đè nữa.


8

Sự khác biệt là gì? Như bạn thấy trong ví dụ của mình, điểm khác biệt chính là cải thiện khả năng đọc của mã nguồn. Chỉ có hai hàm trong ví dụ của bạn, nhưng hãy tưởng tượng nếu có hàng tá hàm? sau đó nó sẽ diễn ra như thế nào

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

nó thực sự trở nên xấu xí và khó đọc, đặc biệt là khi bạn điền vào bên trong các hàm. Trên hết, một số trình soạn thảo nhất định như mã Visual Studio không cho phép độ dài dòng hơn 140. nhưng nếu nó diễn ra như sau.

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

Điều này cải thiện đáng kể khả năng đọc.

Nếu không có sự khác biệt, tại sao đường ống chức năng tồn tại? Mục đích của hàm PIPE () là tổng hợp tất cả các hàm sử dụng và trả về có thể quan sát được. Ban đầu cần quan sát, sau đó có thể quan sát được sử dụng trong suốt hàm pipe () bởi từng hàm được sử dụng bên trong nó.

Hàm đầu tiên nhận chức năng có thể quan sát, xử lý nó, sửa đổi giá trị của nó và chuyển đến chức năng tiếp theo, sau đó hàm tiếp theo nhận đầu ra có thể quan sát được của chức năng đầu tiên, xử lý nó và chuyển cho chức năng tiếp theo, sau đó nó tiếp tục cho đến khi tất cả các chức năng bên trong của hàm pipe () sử dụng có thể quan sát được, cuối cùng bạn có thể quan sát đã xử lý. Cuối cùng, bạn có thể thực thi hàm subscribe () có thể quan sát được để trích xuất giá trị của nó. Hãy nhớ rằng, các giá trị trong bản gốc có thể quan sát được không thay đổi. !! 

Tại sao những chức năng đó cần nhập khẩu khác nhau? Nhập phụ thuộc vào nơi hàm được chỉ định trong gói rxjs. Nó diễn ra như thế này. Tất cả các mô-đun được lưu trữ trong thư mục node_modules trong Angular. nhập {class} từ "mô-đun";

Hãy lấy đoạn mã sau làm ví dụ. Tôi vừa viết nó trong stackblitz. Vì vậy, không có gì được tạo tự động hoặc sao chép từ một nơi khác. Tôi không thấy có ích khi sao chép những gì đã nêu trong tài liệu rxjs khi bạn cũng có thể đọc và đọc nó. Tôi cho rằng bạn đã hỏi câu hỏi này ở đây, bởi vì bạn không hiểu tài liệu. 

  • Có các lớp bản đồ dạng ống, có thể quan sát được, được nhập từ các mô-đun tương ứng. 
  • Trong phần nội dung của lớp, tôi đã sử dụng hàm Pipe () như đã thấy trong mã. 
  • Hàm Of () trả về một hàm có thể quan sát, phát ra các số theo thứ tự khi nó được đăng ký.

  • Có thể quan sát chưa được đăng ký.

  • Khi bạn sử dụng nó như Observable.pipe (), hàm pipe () sử dụng Observable đã cho làm đầu vào.

  • Hàm đầu tiên, hàm map () sử dụng Observable đó, xử lý nó, trả lại Observable đã xử lý trở lại hàm pipe (),

  • sau đó, có thể quan sát được đã xử lý được cấp cho hàm tiếp theo nếu có,

  • và nó tiếp tục như vậy cho đến khi tất cả các chức năng xử lý Observable,

  • ở cuối, Observable được trả về bởi hàm pipe () thành một biến, trong ví dụ sau đây là obs của nó.

Bây giờ điều trong Observable là, miễn là người quan sát không đăng ký nó, nó sẽ không phát ra bất kỳ giá trị nào. Vì vậy, tôi đã sử dụng hàm subscribe () để đăng ký Observable này, sau đó ngay sau khi tôi đăng ký nó. Hàm of () bắt đầu tạo ra các giá trị, sau đó chúng được xử lý thông qua hàm pipe () và bạn nhận được kết quả cuối cùng ở cuối, ví dụ: 1 được lấy từ hàm of (), 1 được thêm 1 vào hàm map (), và quay trở lại. Bạn có thể lấy giá trị đó làm đối số bên trong hàm subscribe (function ( đối số ) {}).

Nếu bạn muốn in nó, hãy sử dụng như

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.