rxjs 状态超时_没有rxjs的角度状态管理实验

rxjs 状态超时

Implementing a state management in modern web Angular applications can be tricky.

在现代Web Angular应用程序中实现状态管理可能很棘手。

There are many libraries, such Ngrx, ngxs, Akita, which can be integrated to manage stores but, these are strongly opinionated and have impact on the architecture of the solution.

有很多库,例如NgrxngxsAkita ,可以集成以管理商店,但是这些库都受到了强烈质疑,并且对解决方案的体系结构有影响。

If we omit the concept displayed by Jonas Bandi in his interesting article, a common alternative to not using 3rd party libraries, is the development of custom stores with RxJS.

如果我们忽略了乔纳斯·班迪( Jonas Bandi)在他有趣的文章中显示的概念,那么不使用3rd party库的常见替代方法就是使用RxJS开发自定义商店。

In both cases, libraries or custom, RxJS is used 🤷‍♂️.

在两种情况下(库还是自定义),都使用RxJS 🤷‍♂️。

Even though RxJS is a wonderful piece of technology, is nowadays a de facto standard when it comes to Angular development, and installed per default with almost any starter kits, it can be still opted-out.

尽管RxJS是一项很棒的技术,但如今已成为Angular开发的事实上的标准,并且默认情况下几乎与所有入门工具包一起安装,但仍可以选择退出。

That’s why I was interested to get to know if it would be possible to develop an Angular application using a modern state management but, without using RxJS.

这就是为什么我有兴趣了解是否有可能使用现代状态管理而不使用RxJS开发Angular应用程序。

目标 (Goals)

To narrow the goals of the experiment, these are those I was looking to test:

为了缩小实验目标,以下是我要测试的目标:

  • Can a property be bind and updated in a template without having to write extra code or trigger the change detection, as it would be solved with an observable?

    是否可以在模板中绑定和更新属性,而不必编写额外的代码或触发更改检测(如可观察到的那样)?
  • Can store’s values be accessed in different routes?

    可以在不同的路径中访问商店的值吗?
  • Can store’s values be retrieved in child components?

    可以在子组件中检索存储的值吗?
  • Can store’s values be used in providers?

    可以在提供程序中使用商店的值吗?
  • Is it easy to integrate it in unit tests?

    将它集成到单元测试中容易吗?

Let’s try to answer these questions, but first, let setup another kind of state management.

让我们尝试回答这些问题,但首先,让我们设置另一种状态管理。

模具商店 (Stencil Store)

The @stencil/store is a lightweight shared state library by the StencilJS core team. It implements a simple key/value map that efficiently re-renders components when necessary.

@ stencil / storeStencilJS核心团队的轻量级共享状态库。 它实现了一个简单的键/值映射,可以在必要时有效地重新渲染组件。

Image for post

I use it in our web open source editor for presentations, DeckDeckGo, and I have to admit, I kind of have a crush for this lightweight store. It is so bare minimum simple, and effective, I obviously selected it to perform my experiment.

我在Web开放源代码编辑器DeckDeckGo中使用了它,我不得不承认,我对这个轻量级商店有些迷恋。 它是如此的简单和有效,我显然选择了它来进行实验。

Even though it would work out of the box with Angular, note that I had to create a fork. The Webpack’s build was complaining about it and, since we do not need this requirement in case of an Angular usage, I just removed it.

即使使用Angular可以立即使用,请注意我必须创建一个fork 。 Webpack的构建对此有所抱怨,由于在使用Angular时我们不需要此要求,因此我删除了它。

If I, or anyone, would use it for a real application, the library dependency could be patched easily I am guessing.

如果我或任何人将其用于实际应用程序中,我猜很容易对库依赖项进行修补。

源代码 (Source Code)

Before going further, note that this experiment’s source code is available on GitHub.

在继续之前,请注意该实验的源代码可在GitHub上获得

建立 (Setup)

To set up such a store for an application, we can create a new TypeScript file, such as clicks.store.ts , and use the createStore function exposed by the Stencil Store.

要为应用程序设置这种存储,我们可以创建一个新的TypeScript文件,例如clicks.store.ts ,并使用Stencil商店公开的createStore函数。

import {createStore} from '@stencil/store';

const { state } = createStore({
clicks: 0
});

export default {state};

That’s it. It is the minimum to expose a global clicks store for an app.

而已。 这是公开应用程序全局clicks存储的最低要求。

Because I was eager to give a try to the few other features of the store, I also added the usage of the functions onChange , to test if property listening to changes would also be re-rendered, and the dispose feature needed for testing purpose.

因为我很想尝试商店的其他一些功能,所以我还添加了onChange函数的用法,以测试是否还会重新呈现侦听更改的属性,以及用于测试目的的dispose功能。

import {createStore} from '@stencil/store';

const { state, onChange, reset, dispose } = createStore({
clicks: 0,
count: 0
});

onChange('clicks', value => {
state.count = value * 2;
});

export default {state, dispose};

Pretty slim in my humble opinion 😉.

以我的拙见opinion相当苗条。

It is also worth to notice that it is possible to create as many stores as we would need.

还值得注意的是,可以创建所需数量的商店。

#1:属性绑定和重新渲染 (#1: Property Binding And Re-render)

I tried different ways to use the properties of the store in the templates and, figured out that the easiest way was to bind the state with a component’s variable.

我尝试了不同的方式在模板中使用存储的属性,并且发现最简单的方法是将state与组件的变量绑定。

import { Component } from '@angular/core';

import store from '../../stores/clicks.store';

@Component({
selector: 'app-page1',
templateUrl: './page1.component.html',
styleUrls: ['./page1.component.css']
})
export class Page1Component {

state$$ = store.state;}

It can then be use in a template to display the values of the store.

然后可以在模板中使用它来显示存储的值。

<p>Clicks: {{state$$.clicks}}</p>

<p>Count: {{state$$.count}}</p>

Does it get re-rendered, when the store changes?

当商店更改时,它会重新呈现吗?

To try out this hypothesis, I added a function to the component, which increments the clicks.

为了尝试这种假设,我向组件添加了一个函数,该函数增加了clicks

inc() {
store.state.clicks++;
}

Therefore, if everything works as expected, each time I would call the above function, the clicks should be incremented and displayed. Because I registered an onChange on such property too, the count should be actualized with twice the value.

因此,如果一切正常,每次我调用上述函数时, clicks都应该增加并显示。 因为我也在这样的属性上注册了onChange ,所以count应该用两倍的值来实现。

Image for post

Success

成功

It exactly behaves as expected. Store properties are modified and, the layout is re-rendered. In addition, I did not have to implement any custom change detection calls or what so ever.

它的行为完全符合预期。 商店属性被修改,并且布局重新呈现。 此外,我不必实施任何自定义更改检测调用或任何其他方式。

#2:路线 (#2: Routes)

The second question I was looking to answer, was related to sharing data between routes. To answer it, I created another page component, added it to the routing and used the store exactly in the same way as previously.

我要回答的第二个问题与路线之间的数据共享有关。 为了回答这个问题,我创建了另一个页面组件,将其添加到路由中,并以与以前完全相同的方式使用了商店。

import { Component } from '@angular/core';

import store from '../../stores/clicks.store';

@Component({
selector: 'app-page2',
template: `<h1>Page 2</h1>

<p>Clicks: {{state$$.clicks}}</p>

<p>Count: {{state$$.count}}</p>`
})
export class Page2Component {

state$$ = store.state;

}

If this would work out, once I would navigate, I would find the exact same value in each page without having to implement anything else respectively without the need to pass values between routes.

如果可以解决,在导航后,我将在每个页面中找到完全相同的值,而不必分别执行其他任何操作,而无需在路由之间传递值。

Image for post

Success

成功

Indeed, stores data can be shared between routes.

实际上,商店数据可以在路线之间共享。

#3:组件 (#3: Components)

Likewise, instead of routes, are data accessible from a component?

同样,可以从组件访问数据而不是路由吗?

To test this hypothesis, I refactored the page2 to move the code to a separate component card .

为了验证这一假设,我重构了page2 ,将代码移至单独的组件card

import { Component } from '@angular/core';

import store from '../../stores/clicks.store';

@Component({
selector: 'app-card',
template: `<p>Clicks: {{state$$.clicks}}</p>

<p>Count: {{state$$.count}}</p>`,
styleUrls: ['./card.component.css']
})
export class CardComponent {

state$$ = store.state;

}

I then used it in page2 . Note that doing so, this component, page, does not have to include the store anymore.

然后,我在page2使用了它。 请注意,这样做后,此组件页面不再需要包含商店。

import { Component } from '@angular/core';

@Component({
selector: 'app-page2',
template: `<h1>
Page 2
</h1>

<app-card></app-card>`
})
export class Page2Component {

}

As for previous test, this would be validated, if values would be displayed and updated even if use in a child component.

对于先前的测试,即使值在子组件中使用也能显示和更新,也将对此进行验证。

Image for post

Success

成功

As previously, it works as expected.

和以前一样,它按预期工作。

#4:服务 (#4: Services)

I was asking my self if data could also be used in providers , therefore I added a service to test this specific question.

我问自己是否也可以在providers使用数据,因此我添加了一项服务来测试此特定问题。

import { Injectable } from '@angular/core';

import store from '../stores/clicks.store';

@Injectable({
providedIn: 'root'
})
export class AlertService {

show() {
alert(`Count: ${store.state.count}`);
}
}

If I call the service’s function, an alert should be triggered and the current count value of the store should be displayed.

如果我调用该服务的函数,则应触发警报并显示商店的当前count数值。

Image for post

Success

成功

Providers have access to the store.

提供者可以访问商店。

#5:测试 (#5: Test)

In addition to the runtime, I was also curious about the integration in unit tests. Probably even more than the integration in applications, the usage of stores and RxJS in tests can be also tricky.

除了运行时之外,我还对单元测试中的集成感到好奇。 甚至可能比应用程序中的集成还要复杂,在测试中使用存储和RxJS也会很棘手。

Therefore, I created a test which should increment the clicks and validate that the value has, well, been incremented.

因此,我创建了一个测试,该测试应该增加clicks并验证该值是否已经增加。

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { Page1Component } from './page1.component';

import store from '../../stores/clicks.store';

describe('Page1Component', () => {
let component: Page1Component;
let fixture: ComponentFixture<Page1Component>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ Page1Component ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(Page1Component);
component = fixture.componentInstance;
fixture.detectChanges();
});

beforeEach(() => {
store.dispose();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should increment', () => {
component.inc();

fixture.detectChanges();

const paragraph =
fixture.nativeElement.querySelector('p:first-of-type');

expect(paragraph.textContent).toEqual('Clicks: 1');
});
});

If this would be correct, the test should pass.

如果这是正确的,则测试应通过。

Image for post
Image for post

Success

成功

It is possible to use the store in unit tests and thus, without any particular headache. It works in tests the same manner as it work when used in the application.

可以在单元测试中使用商店,因此没有任何特别的麻烦。 它在测试中的工作方式与在应用程序中使用时的工作方式相同。

概要 (Summary)

All hypothesis, re-rendering data, accessing these and testing the store were a success ✅.

所有的假设,重新渲染数据,访问这些数据并测试商店都成功了。

Image for post

注意事项 (Considerations)

The scope of this experiment was to some extension, limited and, it might need a bit more analysis before being applied to a real life application. I think in particular at the following questions:

该实验的范围在一定程度上是有限的,并且可能需要更多分析才能应用到现实生活中。 我特别认为以下问题:

  • Would it be possible to scope the store, not to the root, but to a particular module? Even though providers provided in root are often used, I think, it would be a nice add-on.

    是否有可能将存储范围而不是根范围,而只限于特定模块? 尽管我经常使用root提供的提供程序,但我认为这将是一个不错的附加组件。

  • How does the rendering performs with a lot of nodes contained in the store? My spontaneous guess is that it behaves exactly like it would behave with or without any other stores but, it is probably worth a try to go a step further and try to render a lot of information.

    渲染如何与商店中包含的许多节点一起执行? 我自发地猜测,它的行为与在有或没有任何其他商店的情况下的行为完全一样,但是,可能值得尝试进一步并提供大量信息。
  • What’s the cost of the Stencil Store in comparison to any other libraries based on RxJS or RxJS itself. If I would have to bet right now, I would bet on the fact that the Stencil Store is maybe the lightest. According bundlephobia, it costs only 899 bytes (minified + gzipped) 🤯.

    与任何其他基于RxJS或RxJS本身的库相比,Stencil Store的成本是多少? 如果我现在必须下注,我会打赌Stencil商店可能是最轻的。 根据bundlephobia ,它仅花费899字节(最小+ gzip压缩)🤯。

  • Stencil is server side rendering (SSR) and pre-rendering compatible. Therefore, as the store has been developed in first place for such technology, I am guessing that it would be also the case with Angular. However, this would have to be tested too.

    模具与服务器端渲染(SSR)和预渲染兼容。 因此,由于商店最初是针对此类技术而开发的,所以我猜想Angular也是如此。 但是,这也必须进行测试。

If you are interested in these questions, let me know. I would love to hear from you, to get your feedbacks and would be happy to continue the experiment 😃.

如果您对这些问题感兴趣,请告诉我。 我希望收到您的来信,以获取您的反馈,并很高兴继续进行实验experiment。

带走 (Take Away)

Honestly? I am that close to find a new idea of application, just to try out concretely the Stencil Store in a modern web Angular application. There is often no better way to experiment, than developing a real application.

老实说我已经接近找到一种新的应用程序概念,只是在现代的Web Angular应用程序中具体尝试Stencil商店。 通常,没有比开发真实应用程序更好的实验方法了。

To infinity and beyond!

超越无限!

David

大卫

Reach me out on Twitter and, why not, give a try to DeckDeckGo for your next presentations!

Twitter上与我联系,为什么不尝试DeckDeckGo进行下一个演示!

It deploys your decks online as Progressive Web Apps and can even push your slides’ source code to GitHub.

它可以将您的卡片组作为Progressive Web Apps在线部署,甚至可以将幻灯片的源代码推送到GitHub。

翻译自: https://medium.com/swlh/angular-state-management-without-rxjs-an-experiment-243de024d396

rxjs 状态超时

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值