0%

接上上一篇

文件结构

任何一个再app/routes/ 目录下的路由都会转换为对应名字的 URL 路由。一个目录里也可以放一个 index.tsx 文件

1
2
3
4
5
6
7
8
app/
├── routes/
│ │
│ └── blog
| | ├── index.tsx ## The /blog route
│ └── about.tsx ## The /about route
│ ├── index.tsx ## The / or home route
└── root.tsx

如果路由与目录同名,则命名文件将成为目录内文件的布局文件,并且布局文件需要一个 Outlet 组件来放置嵌套路由。

1
2
3
4
5
6
7
8
app/
├── routes/
│ │
│ └── about
│ │ ├── index.tsx
│ ├── about.tsx ## this is a layout for /about/index.tsx
│ ├── index.tsx
└── root.tsx

布局组件(Layout)也可以用双下划线开头命名的文件来创建。

1
2
3
4
5
6
7
8
9
app/
├── routes/
│ │
│ └── about
│ │ ├── index.tsx
│ ├── index.tsx
│ ├── about.tsx
│ ├── __blog.tsx ## this is also a layout
└── root.tsx

https://your-url.com/about 还是会渲染 /routes/about.tsx 文件, 但是也会渲染 app/routes/about/index.tsx,如果它用了 Outlet 组件。

动态路线

动态路由是根据 url 中的信息发生变化的路由。 这可能是博客文章的名称或客户 ID,但无论添加到路由前面的 $ 语法是什么,都向 Remix 表明它是动态的。 除了 $ 前缀之外,名称无关紧要。

1
2
3
4
5
6
7
8
9
app/
├── routes/
│ │
│ └── about
│ │ ├── $id.tsx
│ │ ├── index.tsx
│ ├── about.tsx ## this is a layout for /about/index.tsx
│ ├── index.tsx
└── root.tsx

获取数据!

由于 Remix 在服务器上呈现其所有数据,因此在 Remix 中你看不到很多已成为 React 应用程序标准的东西,例如 useState() 和 useEffect() 挂钩。 对客户端状态的需求较少,因为它已经在服务器上进行了评估。

你使用哪种类型的服务器来获取数据也无关紧要。 由于 Remix 位于请求和响应之间并进行适当的翻译,因此你可以使用标准的 Web Fetch API。 Remix 在仅在服务器上运行的加载器函数中执行此操作,并使用 useLoaderData() 挂钩来呈现组件中的数据。 这是一个使用 Cat as a Service API 渲染随机猫图像的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Outlet, useLoaderData } from '@remix-run/react'

export async function loader() {
const response = await fetch('<https://cataas.com/cat?json=true>')
const data = await response.json()
return {
data
}
}

export default function AboutLayout() {
const cat = useLoaderData<typeof loader>()
return (
<>
<img
src={`https://cataas.com/cat/${cat}`}
alt="A random cat."
/>
<Outlet />
</>
)
}

路由参数

在动态路由中,前缀为 $ 的路由需要能够访问 URL 参数来处理应该呈现的数据。 loader 函数可以通过 params 参数访问这些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { useLoaderData } from '@remix-run/react'
import type { LoaderArgs } from '@remix-run/node'

export async function loader({ params }: LoaderArgs) {
return {
params
}
}

export default function AboutLayout() {
const { params } = useLoaderData<typeof loader>()
return <p>The url parameter is {params.tag}.</p>
}

其他 Remix 功能

Remix 有一些其他的辅助函数,可以为路由模块 API 中的普通 HTML 元素和属性添加额外的功能。每个路由都可以定义自己的这些类型的功能。

动作功能

操作函数允许你使用标准 Web FormData API 向表单操作添加额外的功能。

导出异步函数操作({请求}){

1
2
3
4
5
6
7
export async function action({ request }) {
const body = await request.formData();
const todo = await fakeCreateTodo({
title: body.get('title'),
});
return redirect(`/todos/${todo.id}`);
}

标头功能

任何 HTTP 标准标头都可以放入标头函数中。因为每个路由都可以有一个标头,为了避免与嵌套路由冲突,最深的路由 - 或具有最多正斜杠 (/) 的 URL - 获胜。还可以通过 actionHeaders、loaderHeaders 或 parentHeaders 来获取 headers

1
2
3
4
5
export function headers({ actionHeaders, loaderHeaders, parentHeaders }) {
return {
'Cache-Control': loaderHeaders.get('Cache-Control'),
};
}

元函数

此函数将为 HTML 文档设置元标记。默认情况下在 root.tsx 文件中设置一个,但可以为每个路由更新它们。

1
2
3
4
5
6
export function meta() {
return {
title: 'Your page title',
description: 'A new description for each route.',
};
}

链接功能

HTML 链接元素位于 HTML 文档的 标记中,它们会导入 CSS 等。 链接功能,不要与 组件混淆,它允许你只导入需要它们的路由中的东西。 因此,例如,CSS 文件可以限定范围,并且仅在需要这些特定文件的路由上导入。 链接元素作为对象数组从 links() 函数返回,可以是来自链接 API 的 HtmlLinkDescriptor,也可以是可以预取页面数据的 PageLinkDescriptor。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export function links() {
return [
// add a favicon
{
rel: 'icon',
href: '/favicon.png',
type: 'image/png',
},
// add an external stylesheet
{
rel: 'stylesheet',
href: '<https://example.com/some/styles.css>',
crossOrigin: 'true',
},
// add a local stylesheet,
{ rel: 'stylesheet', href: stylesHref },

// prefetch a page's data
{ page: '/about/community' },
];
}

总结

看完上面的文章你应该对 Remix 的基础知识有一了解了,并且已经准备好开始实际构建应用程序了,对吧? Remix 提供了一个 Jokes 应用程序和一个博客教程,让您开始实施这些基本知识。 您也可以从头开始创建一个全新的 Remix 应用程序。 或者,如果您准备好深入探掘,不妨试试 K-Pop Stack。 现在轮到你开始创作了!

前言

你可能听说过很多关于 Remix 框架的炒作。它其实再 2019 就已经开发使用,但它最初仅提供给高级框架的订阅者使用。2021 年,创始人筹集种子资金并将框架开源,让用户开始免费使用 Remix。闸门打开了之后,每个人似乎都在谈论它,无论好坏。让我们深入了解 Remix 的一些基础知识。

Remix 是一个服务器“边缘”优先的 JavaScript 框架。它至少目前使用 React 作为前端,并优先考虑服务器端在边缘渲染应用程序。平台可以获取服务器端代码并 ​​ 将其作为无服务器或边缘功能运行,使其比传统服务器更便宜,并更贴近用户。 Remix 的创始人喜欢将其称为“中心堆栈”框架,因为它可以针对正在运行的平台调整服务器和客户端之间的请求和响应。

部署 Remix

因为 Remix 需要服务器,所以让我们说说如何部署它。 Remix 不提供服务器本身——你自带服务器——允许它在任何 Node.js 或 Deno 环境中运行,包括 Netlify Edge 和 DigitalOcean 的应用平台。 Remix 本身是一个编译器,一个为它运行的平台翻译请求的程序。此过程使用 esbuild 为对服务器的请求创建处理程序。它使用的 HTTP 处理程序建立在 Web Fetch API 之上,并通过调整它们以适应将要部署到的平台而在服务器上运行。

混音堆栈

混音堆栈是为你预先配置了一些常用工具的项目。 Remix 团队维护了三个官方堆栈,它们都以音乐流派命名。还有许多社区混音堆栈,包括由 Netlify 的模板团队创建的 K-Pop 堆栈。这个堆栈非常强大,包括 Supbase 数据库和身份验证、用于样式的 Tailwind、Cypress 端到端测试、Prettier 代码格式化、ESLint linting 和 TypeScript 静态类型。查看 Tara Manicsic 关于部署 K-Pop 堆栈的帖子。

缓存路由

即使 Remix 需要服务器,它仍然可以通过缓存路由来利用 Jamstack 的优势。 静态站点或静态站点生成 (SSG) 是指你的所有内容都在构建时呈现并保持静态直到另一个重建。 内容是预先生成的,可以放在 CDN 上。 这为最终用户提供了许多好处和快速的站点加载。 但是,Remix 不像其他流行的 React 框架(包括 Next.js 和 Gatsby)那样做典型的 SSG。 要获得 SSG 的一些好处,你可以在 Remix 标头函数中使用本机 Cache-Control HTTP 标头来缓存特定路由或直接在 root.tsx 文件中。

1
2
3
4
[[headers]]
for = "/build/*"
[headers.values]
"Cache-Control" = "public, max-age=31536000, s-maxage=31536000"

Then add in your headers function where you want it. This caches for one hour:

1
2
3
4
5
6
export function headers() {
return {
"Cache-Control": "public, s-maxage=360",
};
};

Remix 路由

许多框架都倾向于基于文件系统的路由。这是一种使用指定文件夹为应用程序定义路由的技术。它们通常具有用于声明动态路由和端点的特殊语法。目前 Remix 和其他流行框架之间最大的区别是能够使用嵌套路由。

每个 Remix 应用程序都以 root.tsx 文件开头。这是呈现应用程序的整个基础的地方。你会在这里找到一些常见的 HTML 布局,例如 标签、 标签,然后是带有渲染应用程序所需组件的 标签。这里要指出的一件事是 组件是在网站上启用 JavaScript 的原因。没有它,有些事情会起作用,但不是一切。 root.tsx 文件充当 routes 目录中所有内容的父布局,路由中的所有内容都在 组件在 root.tsx 中的位置呈现。这是 Remix 中嵌套路由的基础。

嵌套路由

Remix 不仅是由 React Router 的一些成员创建的,它还使用 React Router。事实上,他们将 Remix 的一些优点带回了 React Router。 Next.js 和 SvelteKit 的维护者现在试图解决的一个复杂问题是嵌套路由。

嵌套路由不同于传统路由。在新路由将用户带到新页面的情况下,每个嵌套路由都是同一页面的单独部分。它允许通过保持业务逻辑仅与需要它的文件相关联来分离关注点。 Remix 能够处理仅限于嵌套路由所在页面部分的错误。页面上的其他路由仍然可用,并且中断的路由可以为错误提供相关上下文,而不会导致整个页面崩溃。

当 app/routes 中的根文件与将在基本文件中加载的文件目录命名相同时,Remix 会执行此操作。通过使用 组件告诉 Remix 在哪里加载其他路由,根文件成为目录中文件的布局。

Outlet 组件

组件是 Remix 的一个信号,用于指示它应该在哪里渲染嵌套路由的内容。它放在 app/routes 目录根目录下的文件中,与嵌套路由同名。以下代码位于 app/routes/about.tsx 文件中,并包含 app/routes/about 文件夹中文件的出口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Outlet } from '@remix-run/react';

export default function About() {
return (
<>
<section>
I am the parent layout. I will be on any page inside of my named
directory.
</section>
{/* All of my children, the files in the named directory, will go here. */}
<Outlet />
</>
);
}

未完待续…

前言

现在有越来越多的平台支持用户分享自己创作的文章,常见的就有知乎、微信公众号、简书、CSDN 等,这些平台为你的内容带来流量的同时,也要你承担相应的限制,比如 CSDN 就会出现各种广告,微信公众号就不能添加外部链接等等。总的来说还是不自由,内容创作者只是把内容托管到了它们的平台上面而已。尽管博客在中国流行的时间还是在十几年前,这个时间点搭建一个博客甚至还有点逆时代潮流的感觉,但是也正是自己的站点,能给予自己更多的自由,更多的表达空间,尤其是程序员、摄影师等,更需要一个展示自己内容的空间。

Hexo

Hexo 博客本质上是一个 CMS(Content Management System),内容管理系统,从官网的解释里面可以看到,这是一个静态的博客,也就是说,写作的流程是这样的:

  • 用 Markdown 写作
  • 使用 Hexo 生成静态页面
  • 使用 Hexo 部署静态页面

Netlify

Netlify 是一个提供 Web 项目托管的平台,支持 Github、Gitlab、Bitbucket 等平台的项目,这里我们主要提供 Netlify 来部署我们的网站,并且自动发布最新的网站更改,其次 Netlify 还提供免费的二级域名。

创建发布博客

创建 Hexo 博客项目

1
2
npm i -g hexo-cli # 安装hexo cli
hexo init project-name # 创建项目

创建 Github 仓库 push 代码

Github 就不多提了,push 更新到 GitHub 的仓库,让 Netlify 自动部署就行。

创建 Netlify 账户

  • 使用 GitHub 账号登录
  • Add a new site 然后选择 Import an existing project ,最后选择对应的 Github 仓库
  • 填写表单如下,然后点击Deploy Site
  • 设置域名,点击Add a custom domain to your site。根据提示在 DNS 平台设置 CNAME 的 DNS,然后点击Verify,基本两分钟内搞定
  • 使用 HTTPS,可以使用自定义的证书,也可以使用 Netlify 申请免费证书

大功告成 Demo

当使用 Github 将网站项目文件夹里的所有东西上传完毕之后,那么就可以打开 Netlify 给予它访问 Github 仓库的权限。
在此之后,这一切都是自动的:包括当修改了哪篇文章之后:只要 Github 上你网站的仓库发生了变化,Netlify 就会自动重新构建网页文件并且自动发布。

前言

我们都熟悉将 CSS 样式以 link 的形式引用到 HTML 文档<head>标签的方法,这只是我们写 CSS 的几种方法之一。但在一个单页面应用程序(SPA)中,比如在 React 项目中,它是什么样子的呢?有几种方法可以对 React 应用程序增加样式。有些与传统方式重叠,有些则没有那么多规则。

引入外部样式表

就像标题所说,React 可以引入样式文件。这个过程有点像 html 用 link 引入 CSS

  • 在项目目录创建创建新的 CSS 文件
  • 写 CSS 样式
  • 在 React 的文件头部 import CSS 文件,如下:
1
2
3
4
5
6
7
import { React } from 'react';
import './Components/css/App.css';

function App() {
return <div className="main"></div>;
}
export default App;

写内联样式

您可能经常听到内联样式对于可维护性和诸如此类的东西并不是那么好,但是在某些情况下内联样式是有意义的。在 React 中,可维护性不是什么大问题,因为 CSS 通常已经放在同一个文件中了。

这是 React 中一个超级简单的内联样式示例:

1
<div className="main" style={{color:"red"}}>

更好的方式是是先创建样式对象,然后放入 style 参数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { React } from 'react';
function App() {
const styles = {
main: {
backgroundColor: '#f1f1f1',
width: '100%',
},
inputText: {
padding: '10px',
color: 'red',
},
};
return (
<div className="main" style={styles.main}>
<input type="text" style={styles.inputText}></input>
</div>
);
}
export default App;

CSS Module

CSS 模块具有局部作用域变量的优点,可以与 React 一起使用。它到底是什么呢?

CSS 模块的工作原理是将单个 CSS 文件编译成 CSS。CSS 输出是正常的全局 CSS,可以直接注入到浏览器中,也可以连接在一起并写入文件以供生产使用。数据用于将您在文件中使用的人类可读的名称映射到全局安全的输出 CSS。

简单地说,CSS Modules 允许我们在多个文件中使用相同的类名而不会发生冲突,因为每个类名都有一个唯一的 id 名。这在较大的应用程序中特别有用。每个类名的局部作用域都限定在它被导入的特定组件上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* styles.module.css */
.font {
color: #f00;
font-size: 20px;
}

import { React } from "react";
import styles from "./styles.module.css";
function App() {
return (
<h1 className={styles.heading}>Hello World</h1>
);
}
export default App;

styled-components

Have you used styled-components? It’s quite popular and allows you to build custom components using actual CSS in your JavaScript. A styled-component is basically a React component with — get ready for it — styles. Some of the features include unique class names, dynamic styling and better management of the CSS as each component has its own separate styles.

Install the styled-components npm package in the command line:
你使用过styled-components吗? 可以使用它在 JavaScript 中使用实际的 CSS 构建自定义组件。一个styled-components基本上是一个带有样式的 React 组件。其中的一些特性包括惟一的类名、动态样式和更好的 CSS 管理,因为每个组件都有自己独立的样式。

在命令行中安装styled-components的 npm 包:

1
npm install styled-components
1
2
3
4
5
6
7
8
9
10
11
12
import { React } from 'react';
import styled from 'styled-components';
function App() {
const Wrapper = styled.div`
width: 100%;
height: 100px;
background-color: red;
display: block;
`;
return <Wrapper />;
}
export default App;

条件样式

styled-components的优点之一是组件本身是具有函数功能的,因为你可以在 CSS 中使用 props。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { React, useState } from 'react';
import styled from 'styled-components';
function App() {
// display state
const [display, setDisplay] = useState(true);
return (
<>
<Wrapper $display={display} />
<button onClick={() => setDisplay(!display)}>Toggle</button>
</>
);
}
// the wrapper styled component
const Wrapper = styled.div`
width: 100%;
height: 100px;
background-color: red;
display: ${(props) => (props.$display ? 'block' : 'none')};
`;
export default App;

总结

我们研究了几种在React应用程序中编写样式的不同方法。并不是说哪一个比其他的好,因为每种方法的好坏取决于具体的情况。希望现在你已经很好地理解了它们,并知道在React样式工具中有找到合适的。

前言

首先,如果你不知道 Netlify 有一个 CLI,他们知道。我最喜欢的一件事就是在我的静态页面的项目里运行netlify dev命令时看到它检测到它正在做什么并在开发服务器启动站点,进度条慢慢填满。但不仅仅是一个开发服务器,而是复制 Netlify 环境的开发服务器,这意味着你可以在本地运行微服务和使环境变量可用之类的事情。

这里还有五件你可能没有意识到的事情。

1.使用模版创建网页

是的,就一行命令这么简单

1
netlify sites:create-template=

2.管理环境变量

你可以使用netlify env命令(Beta)来管理环境变量。

  • netlify env:list:列出所有环境变量
  • netlify env:get / set:获取设置变量
  • *env:migrate –to *:迁移整个项目的变量到另外一个

3.测试无服务器功能

通过使用 Netlify CLI在本地启动站点,你的微服务器功能将被运行。你可以测试它们是否正常工作并检查网络流量。但是 CLI 也可以帮助您,netlify functions 命令能够在命令行级别测试函数。例如,netlify functions:invoke 可以触发带有模拟数据的函数。

4.直播开发环境

记得之前提到你如何使用 netlify dev 在本地创建一个开发环境吗?这里的诀窍是执行 netlify dev –live。因此,你获得一个全世界都可以看到的特殊 netlify.live URL,而不是localhost URL

5.运行netlify switch在不同的 Netlify 帐户之间切换

比如从你的个人项目到工作项目切换,实际上是使用 CLI 进行身份验证(netlify 登录,想象一下),这样您就可以代表你自己的 Netlify 帐户进行操作。部署网站等等。但是,你拥有多个 Netlify 帐户(如工作帐户和个人帐户)是完全合理的。

BONUS!

推荐一个视频时长 50 秒,展示了如何从在本地城建一些静态文件到使用 CLI 进行部署的过程。

前言

1
2
1) https://website.com/foo/
2) https://websites.com/foo

第一个选项有一个“斜线”。第二个没有。

我一直倾向于这种想法:如果该页面有子页面,则使用尾部斜杠(例如,它是一个目录页面,即使它有自己的独特内容)。如果是行尾(内容),则没有尾随斜杠。

我这么说,但这个网站css-tricks.com并没有使用它。这个网站上的博客文章就像*css-tricks.com/blog-post/*带有斜杠,如果你去掉斜杠,WordPress 将重定然后主动带上斜杠。重定向会带来性能损失,因此最好尽可能少地发生。

性能是一回事,但 SEO 又是另一回事。如果您渲染相同的内容,无论是否带有斜线,这在理论上都是重复的内容惩罚和禁忌。
(虽然这对我来说似乎很奇怪,但我认为百度,谷歌的那个搜索引擎足够聪明,所以无需过分担心。)

资源解析似乎是最重要的。在一些 web hosting 的平台上使用相对路径时,一些资源是无法加载的。

1
<img src="image.avif" />

在 /resource/ 指向 /resource/image.avif

1
<img src="image.avif" />

在 /resource/ 指向 /image.avif

这是一个很大的区别,对我来说,重定向是值得的一个原因。不能因为这么愚蠢的事情让网页找不到资源。更复杂的是,网站建设框架可能对此有意见,而托管服务提供商可能对此有意见。主持人之间存在一些分歧,因此值得关注。

前言

Ahmad Shadeed 的文章css-object-view-box写得很好,他可以让我们对object-view-box属性的有一定的了解,他将其描述为在浏览器中使用 CSS 裁剪图像的原生方式。

如何使用

使用案例呢?好吧,Ahmad 开头展示了如何在没有css-object-view-box的情况下来完成一些图片裁剪需求。

  1. codepan例子1:一个带有隐藏溢出的包装元素,该元素围绕一个尺寸和定位在该元素内的图像或
  2. codepan例子2:背景图background-image

如果使用 object-view-box,我们基本上可以像使用 SVG 的 viewbox 一样绘制图像边界。因此,我们可以使用普通的 <img> 标签并调用 object-view-box 以使用 inset 函数修剪边缘。我把Ahmad的例子放这里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
img {
width: 220px;
aspect-ratio: 3 / 2;
object-fit: cover;
}

.img-0 {
aspect-ratio: 1 / 2;
}

.img-1 {
object-view-box: inset(0% 19% -33% 57%);
}

.img-2 {
object-view-box: inset(0% 15% -52% 3%);
}

.img-3 {
object-view-box: inset(0% 0% 17% 0%);
}

.group {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1rem 1.5rem;
}

/* Other styles */
.item {
text-align: center;
margin-bottom: 2rem;

p {
padding: 0.5rem;
}

code {
font-size: 14px;
}
}
.alert {
display: block;
max-width: 70ch;
padding: 1rem;
text-align: center;
margin: 0 auto 1rem;
background-color: #ffeeee;
border: 1px solid red;
border-radius: 5px;
font-size: 0.75rem;
}

@supports (object-view-box: inset(0)) {
.alert {
display: none;
}
}

body {
font-family: "system-ui";
padding: 1rem;
line-height: 1.45;
}

img {
max-width: 100%;
box-shadow: 0 8px 15px 0 rgba(#000, 0.1), 0 2px 5px 0 rgba(#000, 0.2);
border-radius: 12px;
}

*,
*:before,
*:after {
box-sizing: border-box;
}

结尾

可惜css-object-view-box现在只支持Chrome Canary。 但是谷歌计划在[Chrome 104]中(https://chromestatus.com/feature/5213032857731072)支持!

前言

你可能已经习惯了在 React 中使用 axios 或 fetch 来获取数据。处理数据获取的通常方法是:

  • 进行 API 调用。
  • 如果一切按计划进行,使用响应更新状态。
  • 或者,在遇到错误的情况下,会向用户显示一条错误消息。

当通过网络处理请求时,总是会有延迟。当你提出请求并等待回应时,这只是过程的一部分。这就是为什么我们经常使用加载转轮来告诉用户预期的响应正在加载。

例子

所有这些都可以使用一个名为React Async的库来完成。

React Async 是一个基于承诺的库,可以让你在 React 应用程序中获取数据。让我们来看看各种使用组件、钩子和帮助器的例子,看看我们如何在发出请求时实现加载状态。

在本教程中,我们将使用 Create React App。你可以运行以下命令来创建一个项目:

完成后,运行 yarn 或 npm 命令在你的项目中安装 React Async:

1
2
3
4
5
## yarn
yarn add react-async

## npm
npm install react-async --save

组件中的加载器

这个库允许我们在 JSX 中直接使用。因此,组件的示例如下所示;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Let's import React, our styles and React Async
import React from 'react';
import './App.css';
import Async from 'react-async';

// We'll request user data from this API
const loadUsers = () =>
fetch('https://jsonplaceholder.typicode.com/users')
.then((res) => (res.ok ? res : Promise.reject(res)))
.then((res) => res.json());

// Our component
function App() {
return (
<div className="container">
<Async promiseFn={loadUsers}>
{({data, err, isLoading}) => {
if (isLoading) return 'Loading...';
if (err) return `Something went wrong: ${err.message}`;

if (data)
return (
<div>
<div>
<h2>React Async - Random Users</h2>
</div>
{data.map((user) => (
<div key={user.username} className="row">
<div className="col-md-12">
<p>{user.name}</p>
<p>{user.email}</p>
</div>
</div>
))}
</div>
);
}}
</Async>
</div>
);
}

export default App;

首先,我们创建了一个名为 loadUsers 的函数。这将使用 fetch API 进行 API 调用。当它这样做时,它返回一个得到解决的承诺。之后,组件就可以使用所需的包了。

这些包为:

  • isLoading:处理尚未从服务器接收到响应的情况。
  • err:用于遇到错误的情况。您也可以将其重命名为 error 错误.
  • data:这是从服务器获得的预期数据。

正如你在这个例子中看到的,我们要显示一些东西给依赖于包的用户。

示例 2: 钩子上的加载器

如果你喜欢钩子,在使用 React Async 时有一个钩子选项可用。它看起来是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Let's import React, our styles and React Async
import React from 'react';
import './App.css';
import {useAsync} from 'react-async';

// Then we'll fetch user data from this API
const loadUsers = async () =>
await fetch('https://jsonplaceholder.typicode.com/users')
.then((res) => (res.ok ? res : Promise.reject(res)))
.then((res) => res.json());

// Our component
function App() {
const {data, error, isLoading} = useAsync({promiseFn: loadUsers});
if (isLoading) return 'Loading...';
if (error) return `Something went wrong: ${error.message}`;
if (data)
// The rendered component
return (
<div className="container">
<div>
<h2>React Async - Random Users</h2>
</div>
{data.map((user) => (
<div key={user.username} className="row">
<div className="col-md-12">
<p>{user.name}</p>
<p>{user.email}</p>
</div>
</div>
))}
</div>
);
}

export default App;

这看起来与组件示例类似,但在本场景中,我们使用的是 useAsync 组件,而不是 Async 组件。响应返回一个被解析的承诺,我们也可以访问类似的包,就像我们在上一个例子中做的,然后我们可以返回到渲染的 UI。

示例 3: 帮助程序中的加载器

Helper 组件在提高我们的代码的可读性方面很有帮助。这些帮助程序可以在使用 useAsync 钩子或 Async 组件时使用,这两个都是我们刚刚看到的。下面是一个在 Async 组件中使用 helper 的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Let's import React, our styles and React Async
import React from 'react';
import './App.css';
import Async from 'react-async';

// This is the API we'll use to request user data
const loadUsers = () =>
fetch('https://jsonplaceholder.typicode.com/users')
.then((res) => (res.ok ? res : Promise.reject(res)))
.then((res) => res.json());

// Our App component
function App() {
return (
<div className="container">
<Async promiseFn={loadUsers}>
<Async.Loading>Loading...</Async.Loading>
<Async.Fulfilled>
{(data) => {
return (
<div>
<div>
<h2>React Async - Random Users</h2>
</div>
{data.map((user) => (
<div key={user.username} className="row">
<div className="col-md-12">
<p>{user.name}</p>
<p>{user.email}</p>
</div>
</div>
))}
</div>
);
}}
</Async.Fulfilled>
<Async.Rejected>
{(error) => `Something went wrong: ${error.message}`}
</Async.Rejected>
</Async>
</div>
);
}

export default App;

这看起来和我们使用包的时候很类似。完成这些之后,你可以将应用程序的不同部分分解成微小的组件。

总结

如果你已经厌倦了走我在本教程开始部分提到的路线,你可以在你正在做的项目中开始使用 React Async。本教程中使用的源代码可以在 GitHub 上的不同分支中找到。

前言

傍晚的阳光透过沙沙作响的树叶照射进来,给人宁静而温暖的感觉。许多艺术家使用斑驳的光来营造一种柔和、催眠的效果。

斑驳效果

我们可以在网页设计中使用同样的斑驳效果。在照片和插图上使用这个效果,可以为原本单调乏味的内容墙增添魔力,让它们恢复生机。有一种只用 CSS 的简单而快速的方法来添加这个效果。

在我们开始谈代码之前,要先了解斑驳光的组成。它是光透过树叶和树枝形成的大光点组成的。因为我们正在谈论的是穿过许多不确定的空间的光,阴影区域有时会有清晰的边缘,但更多时他的边界候是模糊的。当光从更远的距离投射阴影时,会扩散和扭曲光线附近的墙壁,使得阴影的边界变得模糊。
这是有和没有斑驳光照射的白墙的区别:

效果

我将会使用纯文本和有趣的表情符号来创造斑驳的灯光效果,使用 CSS 阴影和混合来模仿自然。我也将会介绍一些其他的方法。

设置场景

我们将使用文本(字母表中的字母、特殊字符、表情符号等)来创建光的形状。我所说的光,是指半透明的乳白色。同时,我们想要的是柔和的灯光效果,而不是那种锐利、清晰或鲜明的效果。

由斑驳的光产生的斑点有多种形状,所以最好的选择是椭圆或长方形的字符。我会使用 🍃和🍂,因为它们是椭圆的、长方形的和倾斜的,有点混乱且难以预测。

我将它们包装在 .backdrop 父元素中包含的段落中:

1
2
3
4
5
<div class="backdrop">
<p class="shapes">🍃</p>
<p class="shapes">🍂</p>
<p class="shapes">\</p>
</div>

我使用父元素作为投射斑驳光影的表面,为其纹理应用背景图像。我不仅给表面一个明确的 width 和 height,而且在它上面设置了隐藏的溢出,这样我就可以投射超出表面的阴影而不暴露它们。由于 CSS 网格,投射出斑驳光效果的对象在背景表面的中间对齐:

1
2
3
4
5
6
7
8
9
10
.backdrop {
background: center / cover no-repeat url('image.jpeg');
width: 400px;
height: 240px;
overflow: hidden;
display: grid;
}
.backdrop > * {
grid-area: 1/1;
}

如果形状没有完全对齐也没有关系,只要它们能够达到你想要的斑驳的效果,就可以了。所以没有必要完全按照我的做法来放置这些东西,你可以尝试调整这些值来获得不同图案的斑驳光!

在 CSS 中设计斑驳的灯光

这些是表情符号应该具有的关键属性——transparent 颜色、黑色半透明背景(使用 alpha 通道 inrgbargba())、font-size 漂亮的模糊白色 text-shadow,最后是 mix-blend-mode 来平滑过渡。
mix-blend-mode 设置元素的颜色与其容器的内容的融合方式。该 multiply 值使元素的背景通过元素的浅色显示并保持深色相同,从而形成更好、更自然的斑驳光效果。

1
2
3
4
5
6
7
.shapes {
color: transparent;
background-color: rgba(0, 0, 0, 0.3); // Use alpha transparency
text-shadow: 0 0 40px #fff; // Blurry white shadow
font: bolder 320pt/320pt monospace;
mix-blend-mode: multiply;
}

细化颜色和对比度

我希望背景上的 background-image 更亮一点,所以我还添加了 filter: brightness(1.6). 另一种实现方法是 background-blend-mode,将一个元素的所有不同背景混合在一起,而不是将表情符号添加为单独的元素,我们将它们添加为背景图像。
请注意,在最后一个示例中,我使用了不同的表情符号,以及 floralwhite 一些比纯白色的光线强度更低的颜色。这是展开的表情符号背景图像之一:

1
2
3
4
5
6
7
8
9
10
11
12
<svg xmlns='http://www.w3.org/2000/svg'>
<foreignObject width='400px' height='240px'>
<div xmlns='http://www.w3.org/1999/xhtml' style=
'font: bolder 720pt/220pt monospace;
color: transparent;
text-shadow: 0 0 40px floralwhite;
background: rgba(0, 0, 0, 0.3);'
>
🌾
</div>
</foreignObject>
</svg>

如果想将自己的图像用于形状,要确保边界模糊以创造柔和的光线。CSS blur()过滤器可以很方便地处理同样的事情。我还使用 CSS@supports 来调整某些浏览器的阴影模糊值作为备用。
现在让我们回到第一个示例并添加一些内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="backdrop">
<p class="shapes">🍃</p>
<p class="shapes">🍂</p>
<p class="shapes">\</p>
</div>

<p class="content">
<img
width="70px"
style="float: left; margin-right: 10px;"
src="image.jpeg"
alt=""
/>
Top ten tourists spots for the summer vacation <br /><br /><i
style="font-weight: normal;"
>Here are the most popular places...</i
>
</p>

动画光源

我还决定添加一个简单的 CSS 动画,并将@keyframes 其应用于.backdrop :hover:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.backdrop:hover > .shapes:nth-of-type(1) {
animation: 2s ease-in-out infinite alternate move;
}
.backdrop:hover > .shapes:nth-of-type(2):hover {
animation: 4s ease-in-out infinite alternate move-1;
}

@keyframes move {
from {
text-indent: -20px;
}
to {
text-indent: 20px;
}
}
@keyframes move-1 {
from {
text-indent: -60px;
}
to {
text-indent: 40px;
}
}

为 emoji 上的 text-indent 属性设置动画会产生一种非常微妙的运动,就像移动的云层改变光线的方向。

总结

好了!我们从自然和艺术中汲取了一些灵感,模仿了太阳穿过树木和灌木丛,将斑驳的光和阴影点投射到物体的表面。我们用 CSS 和几个表情符号完成了所有这些。

这个方法的关键在于我们如何在表情符号上应用颜色,在浅色中使用额外的模糊 text-shadow 来设置光线,半透明的 background-color 定义阴影斑点。我们所要做的就是确保光影的背景使用具有足够对比度的逼真的纹理,这样就能看到斑驳的光线了。

前言

在 Wikipedia 的Common.css有一段 CSS

1
2
3
4
5
6
.mw-collapsible-leftside-toggle .mw-collapsible-toggle {
/* @noflip */
float: left;
/* @noflip */
text-align: left;
}

这里的@noflip 有什么用呢?这就是一些人所说的“CSS 装饰”,我觉得这很形象。实际上,它们只是 CSS 注释而已,但显然这里还有更多内容,因为它们看起来像具有功能的语句。

如果没有某种 CSS 处理过程,这些注释将无济于事。我不是很确定这里使用的是什么 CSS 处理器,但我认为假设它运行时会产生一个“从右到左”的样式表是合理的,它会变成浮动:左 进入浮动:右和文本对齐:左进入文本对齐:右。

替代方案?

我认为值得注意的是,现在使用大多数浏览器支持的 text-align: start 效果可能更好,这样就不必依赖 CSS 处理和备用样式表来修改 CSS。但是我觉得 float 是没有“逻辑”等价物,但可能有一种方法可以重构布局(使用grid?),这样“翻转”就是不必要的。将元素包裹在一个元素周围对于浮动来说是非常独特的,所以这里可能没有简单的替代方案。

稍微搜索了一下,似乎是 /* @noflip */ 的来源is CSSJanus

网页截图

这个仓库显示它是由维基媒体制作的,所以我认为这是一个已解决的案例。看起来这项技术已经运用到其他,比如样式组件的插件,Sublime Text 的插件,Salesforce 甚至在他们的设计系统中使用了它。

还有另一个名为 css-flip 的处理器(现在已归档,出自 Twitter)看起来它做了完全相同的事情,自述文件显示了有多少属性可能需要这个:

1
background-position, background-position-x, border-bottom-left-radius, border-bottom-right-radius, border-color, border-left, border-left-color, border-left-style, border-left-width, border-radius, border-right, border-right-color, border-right-style, border-right-width, border-style, border-top-left-radius, border-top-right-radius, border-width, box-shadow, clear, direction, float, left, margin, margin-left, margin-right, padding, padding-left, padding-right, right, text-align transition transition-property

总结

It would have hugely surprised me if there wasn’t a PostCSS plugin for this, and a little searching turned up postcss-rtl, but alas, it’s also been deprecated by the owner.

This all started with talking about “CSS decorators” though, which I guess we’re defining as “CSS comments that have processor directives in them.” The one I personally use the most is this:

我想一定会一个实现 flip 的PostCSS 插件,然后我就搜索到了 postcss-rtl,但是它也被所有者弃用了 ☹️。

不过,这一切都始于谈论“CSS 装饰”,我想我们应该将其定义为“其中包含处理器指令的 CSS 注释”。 我个人用得最多的是这个:

1
2
3
4
5
6
7
8
9
10
/* prettier-ignore */
.cool {
linear-gradient(
to left,
pink
pink 20%
red 20%
red
)
}

我很喜欢 Prettier,但如果我花时间自己格式化一些 CSS 以提高可读性,我会在前一行中添加一个 /* prettier-ignore */ 以格式化的时候弄乱它。