Reactive Responsive Design is a term I coined to describe a new paradigm for front-end design. It’s rooted in responsive design and builds into it the principles of reactive programming with RxJS. What we get in return for this is a cleaner, more maintainable, more flexible, and more testable app.
响应式响应设计是我创造的一个术语,用于描述前端设计的新范例。 它扎根于响应式设计,并在其中内置了使用RxJS进行React式编程的原理。 为此,我们得到的回报是更清洁,更可维护,更灵活且可测试的应用程序。
At the heart of responsive design is the media query. The media query is a css3 standard that conditionally applies styling based on a specific css query. The most commonly used options are min-width
and max-width
, which when used together, provides a range of screen widths where styles are applied.
响应式设计的核心是媒体查询。 媒体查询是css3标准,可根据特定的css查询有条件地应用样式。 最常用的选项是min-width
和max-width
,当一起使用时,它们提供了应用样式的屏幕宽度范围。
The problem is that media queries are a mess. Here are some reasons why:
问题在于媒体查询是一团糟。 原因如下:
Where is my code!?
我的代码在哪里!
This is something I’ve often observed. Let’s say you have a web page with a lot of styling, and it has 5 responsive breakpoints. You need to take a look at the styling for the title on the page. You open the css file and find 1000+ lines of css with the title code spread throughout.
这是我经常观察到的。 假设您有一个样式丰富的网页,并且有5个响应断点。 您需要查看页面标题的样式。 您打开css文件,找到1000多个css行,其标题代码遍布整个目录。
Teams can end up with different queries.
团队可能会遇到不同的查询。
Especially when using modern JS frameworks you run the risk of different parts of a page having different breakpoints. This is an issue because your app could end up in a weird state on some screen sizes. What if a phone with an extra wide screen became popular and most of your app adjusted to mobile view on the expected width but the menu bar was built with a different query and was showing a desktop version. While we can address this issue with standards I find it much more dependable to have things enforced by code. This enforcement can not be achieved with media queries.
尤其是在使用现代JS框架时,您冒着页面的不同部分具有不同断点的风险。 这是一个问题,因为在某些屏幕尺寸上,您的应用最终可能会处于怪异状态。 如果具有超宽屏幕的手机开始流行,并且您的大多数应用程序已调整为可移动宽度(以预期的宽度),但菜单栏是使用其他查询构建的,并且显示的是桌面版本,该怎么办? 虽然我们可以用标准解决这个问题,但我发现由代码强制执行的事情更加可靠。 媒体查询无法实现这种实施。
Can only query screen width.
只能查询屏幕宽度。
Media queries are very limited in their abilities. This severely restricts the layout options you have with them. When using the
媒体查询的能力非常有限。 这严重限制了您使用的布局选项。 使用时
width
query, all you can do is apply different styles depending on the full width of the screen. That means you have to resort to more complicated processes to change layouts when, for instance, a sidebar menu is expanded or collapsed.width
查询,您所能做的就是根据屏幕的整个宽度应用不同的样式。 这意味着,例如,在展开或折叠侧边栏菜单时,您必须诉诸更复杂的过程来更改布局。Code will always load.
代码将始终加载。
This is one of the most irritating things about media queries. The devices with the most constrained resources (phones) are also the devices that display the most abbreviated user interface. With media queries, all of the elements that are hidden still have to be generated. That means that the devices with the greatest overhead to generate a screen are the devices with the smallest screens (phones).
这是有关媒体查询的最令人讨厌的事情之一。 资源(电话)最受限制的设备也是显示最简洁的用户界面的设备。 对于媒体查询,仍然必须生成所有隐藏的元素。 这意味着生成屏幕的开销最大的设备就是屏幕(电话)最小的设备。
Hard to test.
很难测试。
I am a big fan of testing. The problem with media queries is that if we were to test them it would have to be from an E2E test where we actually build the app and validate that the elements are laying out the desired way. Yuck.
我是测试的忠实粉丝。 媒体查询的问题在于,如果要对其进行测试,则必须来自实际构建应用程序并验证元素是否按所需方式进行布局的E2E测试。 uck
什么是React式响应设计 (What is Reactive Responsive Design)
Reactive responsive design is the idea that we can observe screen size changes using an RxJS Observable. This will allow us to group classes in the css together without query bloat, codify breakpoints, break on things other than screen width, conditionally load components, and test.
响应式响应设计是我们可以使用RxJS Observable观察屏幕尺寸变化的想法。 这将使我们能够将css中的类分组在一起,而不会产生查询膨胀,编码断点,中断除屏幕宽度之外的其他事情,有条件地加载组件以及进行测试。
这个怎么运作 (How it Works)
The first question is, how do we know when the screen is in a size range? What I do is use the window.matchMedia
function. This is a native javaScript function that takes a string argument holding a media query. I then watch for changes in the status of the query (matched/not matched) and store those results in RxJS Subjects.
第一个问题是,我们如何知道屏幕何时处于尺寸范围内? 我要做的是使用window.matchMedia
函数。 这是一个本机javaScript函数,该函数采用一个字符串参数来保存媒体查询。 然后,我观察查询状态(匹配/不匹配)的变化,并将这些结果存储在RxJS主题中。
Here is what my class looks like:
这是我的班级样子:
import { BehaviorSubject, Observable } from 'rxjs';
export class RxRs {
private topWindow: Window;
private windowSizeSubjects: SizeSubjects = {};
constructor() {
this.topWindow = this.getWindow();
}
observe(query: string): Observable<boolean> {
const mql = this.topWindow.matchMedia(query);
let subject = this.windowSizeSubjects[mql.media];
if (!subject) {
this.windowSizeSubjects[mql.media] = new BehaviorSubject(mql.matches);
mql.addListener(this.testQuery.bind(this));
subject = this.windowSizeSubjects[mql.media];
}
return subject.asObservable();
}
private testQuery(e: any, subjects = this.windowSizeSubjects): void {
const subject = subjects[e.media];
if (subject) {
subject.next(e.matches);
}
}
private getWindow(): Window {
return window.top;
}
}
interface SizeSubjects {
[key: string]: BehaviorSubject<boolean>;
}
让我们分解一下 (Lets break it down)
constructor() {
this.topWindow = this.getWindow();
}
Next we have the core of the Reactive Responsive paradigm.
接下来,我们将掌握响应式响应范式的核心。
observe(query: string): Observable<boolean> {
const mql = this.topWindow.matchMedia(query);
let subject = this.windowSizeSubjects[mql.media];
if (!subject) {
this.windowSizeSubjects[mql.media] = new BehaviorSubject(mql.matches);
mql.addListener(this.testQuery.bind(this));
subject = this.windowSizeSubjects[mql.media];
}
return subject.asObservable();
}
First, observe passes the query argument to the window matchMedia
function. That will give us a MediaQueryList
object which we will use to check our cache. If we are already tracking that query we will just return the existing Observable. Otherwise, we create a BehaviorSubject, set its initial value, and call the addListener
function on the MediaQueryList
which triggers whenever the matching state on the query changes.
首先,观察将查询参数传递给窗口matchMedia
函数。 这将为我们提供一个MediaQueryList
对象,该对象将用于检查缓存。 如果我们已经在跟踪该查询,我们将只返回现有的Observable。 否则,我们将创建一个BehaviorSubject,设置其初始值,并在MediaQueryList
上调用addListener
函数,该函数将在查询的匹配状态更改时触发。
The result is a class we can call, pass a media query to, and reference an Observable that emits when the query state changes! It even caches the queries, so if you request the same query again you will get the same observable back.
结果是一个我们可以调用的类,将其传递给媒体查询,并引用当查询状态更改时发出的Observable! 它甚至会缓存查询,因此,如果再次请求相同的查询,您将获得相同的可观察到的结果。
The observe
function accepts any valid media query as an input; not just width and height. Do you want an observable returning you the orientation of the screen? What about the use of a pointing device (like a mouse)? Or, how fast the user’s device can update the screen? These options and more are available in the media query spec allowing you to do very complex layouts in a unified way all without bloating your css into an unreadable blob.
observe
功能接受任何有效的媒体查询作为输入; 不只是宽度和高度。 您是否想得到一个可观察到的返回屏幕方向? 使用指针设备(例如鼠标)怎么样? 或者,用户设备更新屏幕的速度有多快? 媒体查询规范中提供了这些选项以及更多选项,使您能够以统一的方式进行非常复杂的布局,而不会使CSS膨胀为不可读的Blob。
I’ve made a npm library called rxrs so that you don’t need to write your own implementation of this class. The next article will illustrate using rxrs to standardize on breakpoints and address the other issues with media queries discussed in this article.
我制作了一个名为rxrs的npm库,因此您无需编写自己的此类实现。 下一篇文章将说明使用rxrs标准化断点并解决本文讨论的媒体查询的其他问题。
翻译自: https://medium.com/ngconf/reactive-responsive-design-part-1-b9c4be427b3f