VScode跑通Remix.js官方的contact程序开发过程

目录

1 引言

2 安装并跑起来

3 设置根路由

4 用links来添加风格资源

​5 联系人路由的UI

6 添加联系人的UI组件

7 嵌套路由和出口

8 类型推理

9 Loader里的URL参数

10 验证参数并抛出响应


书接上回,我们已经跑通了remix的quick start项目,接下来看看第2个教程,官网宣称耗时30分钟来学习。

1 引言

We'll be building a small, but feature-rich app that lets you keep track of your contacts. There's no database or other "production ready" things, so we can stay focused on Remix. We expect it to take about 30m if you're following along, otherwise it's a quick read.

我们将建立一个小而功能丰富的联系人管理程序。没有数据库或其他东西,所以我们可以专注于Remix。如果你跟上写的话,我们预计大概要花30分,否则就很快就能读完了。

下图就是最后的效果。

2 安装并跑起来

首先,新建一个目录并转至目录下

在这个目录下,测试下面命令,目的是生成一个基本模板。

npx create-remix@latest --template remix-run/remix/templates/remix-tutorial

也可能不需要创建新目录,不过我们还是稳妥起见,在目录下执行这个命令,选择y后,一顿安装。

这就是全部过程了,会让你选择路径,我们输入了/d/web/...这里和我们创建的路径不一致。

最后git初始化失败了,没关系。文件夹快200m了。

接下来执行:

npm install

npm run dev

虽然过程有些错误,但是不影响。

启动app 成功。

3 设置根路由

根路由是要渲染的UI的基本组件。我们这里是用typescript来写的。

4 用links来添加风格资源

这里其实就是设置css。

While there are multiple ways to style your Remix app, we're going to use a plain stylesheet that's already been written to keep things focused on Remix.

虽然有多种方式来设置Remix应用程序风格,我们将使用一个简单的样式表。

You can import CSS files directly into JavaScript modules. The compiler will fingerprint the asset, save it to your assetsBuildDirectory, and provide your module with the publicly accessible href.

​你可以将CSS文件直接导入到JavaScript模块中。编译器将对资产进行识别,将其保存到你的assetsBuildDirectory中,并为你的模块提供可公开访问的href。

import type { LinksFunction } from "@remix-run/node";
// existing imports

import appStylesHref from "./app.css";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: appStylesHref },
];

​我们直接把这个段代码复制到root中,然后再执行依次npm run dev

看起来css已经加载了。成功。

Every route can export a links function. They will be collected and rendered into the <Links /> component we rendered in app/root.tsx.

​每条路由都可以导出一个links功能。它们将被收集并呈现到我们在app/root.tsx中呈现的<Links />组件中。

​The app should look something like this now. It sure is nice having a designer who can also write the CSS, isn't it? (Thank you, Jim ).

应用程序现在看起来应该是这样的。有一个能写CSS的设计师真是太好了,不是吗?(谢谢你,吉姆)。

​5 联系人路由的UI

If you click on one of the sidebar items you'll get the default 404 page. Let's create a route that matches the url /contacts/1.

如果您单击其中一个侧边栏项,您将获得默认的404页面。让我们创建一个匹配url /contacts/1的路由。

我试过了,确实是404.

创建app/routes目录以及相应的文件

mkdir app/routes
touch app/routes/contacts.\$contactId.tsx

In the Remix route file convention, . will create a / in the URL and $ makes a segment dynamic. We just created a route that will match URLs that look like this:

​在Remix路由文件约定中,.将在URL中创建一个/,而$将创建一个动态段。我们刚刚创建了一个路由,它将匹配如下所示的url:​

  • /contacts/123
  • /contacts/abc

6 添加联系人的UI组件

It's just a bunch of elements, feel free to copy/paste.

它只是一堆元素,随意复制/粘贴。

import { Form } from "@remix-run/react";
import type { FunctionComponent } from "react";

import type { ContactRecord } from "../data";

export default function Contact() {
  const contact = {
    first: "Your",
    last: "Name",
    avatar: "https://placekitten.com/g/200/200",
    twitter: "your_handle",
    notes: "Some notes",
    favorite: true,
  };

  return (
    <div id="contact">
      <div>
        <img
          alt={`${contact.first} ${contact.last} avatar`}
          key={contact.avatar}
          src={contact.avatar}
        />
      </div>

      <div>
        <h1>
          {contact.first || contact.last ? (
            <>
              {contact.first} {contact.last}
            </>
          ) : (
            <i>No Name</i>
          )}{" "}
          <Favorite contact={contact} />
        </h1>

        {contact.twitter ? (
          <p>
            <a
              href={`https://twitter.com/${contact.twitter}`}
            >
              {contact.twitter}
            </a>
          </p>
        ) : null}

        {contact.notes ? <p>{contact.notes}</p> : null}

        <div>
          <Form action="edit">
            <button type="submit">Edit</button>
          </Form>

          <Form
            action="destroy"
            method="post"
            onSubmit={(event) => {
              const response = confirm(
                "Please confirm you want to delete this record."
              );
              if (!response) {
                event.preventDefault();
              }
            }}
          >
            <button type="submit">Delete</button>
          </Form>
        </div>
      </div>
    </div>
  );
}

const Favorite: FunctionComponent<{
  contact: Pick<ContactRecord, "favorite">;
}> = ({ contact }) => {
  const favorite = contact.favorite;

  return (
    <Form method="post">
      <button
        aria-label={
          favorite
            ? "Remove from favorites"
            : "Add to favorites"
        }
        name="favorite"
        value={favorite ? "false" : "true"}
      >
        {favorite ? "★" : "☆"}
      </button>
    </Form>
  );
};

Now if we click one of the links or visit /contacts/1 we get ... nothing new?

现在,如果我们点击其中一个链接或访问/联系人/1,我们得到…没有什么新的吗?

我试了下,其实就是不报404了。里面还用到了twitter的网页,确定我们能打开吗??

7 嵌套路由和出口

Since Remix is built on top of React Router, it supports nested routing. In order for child routes to render inside of parent layouts, we need to render an Outlet in the parent. Let's fix it, open up app/root.tsx and render an outlet inside.

​由于Remix是建立在React Router之上的,所以它支持嵌套路由。为了让子路由在父布局中呈现,我们需要在父布局中呈现一个Outlet。让我们修复它,打开app/root。并在里面渲染一个出口。

​这里涉及到一些代码的修改。import里新增一个outlet,然后在body里还要新增一个div。

import {
  Form,
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

import type { LinksFunction } from "@remix-run/node";
// existing imports

import appStylesHref from "./app.css";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: appStylesHref },
];


export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <div id="sidebar">
          <h1>Remix Contacts</h1>
          <div>
            <Form id="search-form" role="search">
              <input
                id="q"
                aria-label="Search contacts"
                placeholder="Search"
                type="search"
                name="q"
              />
              <div id="search-spinner" aria-hidden hidden={true} />
            </Form>
            <Form method="post">
              <button type="submit">New</button>
            </Form>
          </div>
          <nav>
            <ul>
              <li>
                <a href={`/contacts/1`}>Your Name</a>
              </li>
              <li>
                <a href={`/contacts/2`}>Your Friend</a>
              </li>
            </ul>
          </nav>
        </div>

        <div id="detail">
          <Outlet />
        </div>
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

刷新一下,重新run,确实出来联系人界面了,只是猫猫和示例的姿势不一样。

8 客户边路由

You may or may not have noticed, but when we click the links in the sidebar, the browser is doing a full document request for the next URL instead of client side routing.

您可能注意到,也可能没有注意到,当我们单击侧边栏中的链接时,浏览器正在对下一个URL进行完整的文档请求,而不是客户端路由。

Client side routing allows our app to update the URL without requesting another document from the server. Instead, the app can immediately render new UI. Let's make it happen with <Link>.

​客户端路由允许我们的应用程序更新URL,而不需要从服务器请求另一个文档。相反,应用程序可以立即呈现新的UI。让我们用<Link>实现它。

这段有点看不懂,先跟着做吧。

Change the sidebar <a href> to <Link to>

​将侧边栏的...改为...

这里又是改代码,import里新增Link,然后在div里做相应修改即可。

做完以后网页没啥变化。

You can open the network tab in the browser devtools to see that it's not requesting documents anymore.

您可以在浏览器devtools中打开network选项卡,查看它不再请求文档了。

这里没试,暂时没啥影响,不管了。

8 加载数据

URL segments, layouts, and data are more often than not coupled (tripled?) together. We can see it in this app already:

URL段、布局和数据通常是耦合在一起的(三倍?)我们可以在这个应用程序中看到它:

Because of this natural coupling, Remix has data conventions to get data into your route components easily.

由于这种自然耦合,Remix具有数据约定,可以轻松地将数据放入路由组件中。

There are two APIs we'll be using to load data, loader and useLoaderData. First we'll create and export a loader function in the root route and then render the data.

​我们将使用两个api来加载数据,loader和useLoaderData。首先,我们将在根路由中创建并导出一个加载器函数,然后渲染数据。

从root.tsx中导出一个loader函数,然后渲染数据。

import { json } from "@remix-run/node";

import {
  Form,
  Link,
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useLoaderData,
} from "@remix-run/react";

import { getContacts } from "./data";

import type { LinksFunction } from "@remix-run/node";
// existing imports

import appStylesHref from "./app.css";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: appStylesHref },
];

export const loader = async () => {
  const contacts = await getContacts();
  return json({ contacts });
};

export default function App() {
  const { contacts } = useLoaderData();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <div id="sidebar">
          <h1>Remix Contacts</h1>
          <div>
            <Form id="search-form" role="search">
              <input
                id="q"
                aria-label="Search contacts"
                placeholder="Search"
                type="search"
                name="q"
              />
              <div id="search-spinner" aria-hidden hidden={true} />
            </Form>
            <Form method="post">
              <button type="submit">New</button>
            </Form>
          </div>
          <nav>
            {contacts.length ? (
              <ul>
                {contacts.map((contact) => (
                  <li key={contact.id}>
                    <Link to={`contacts/${contact.id}`}>
                      {contact.first || contact.last ? (
                        <>
                          {contact.first} {contact.last}
                        </>
                      ) : (
                        <i>No Name</i>
                      )}{" "}
                      {contact.favorite ? (
                        <span>★</span>
                      ) : null}
                    </Link>
                  </li>
                ))}
              </ul>
            ) : (
              <p>
                <i>No contacts</i>
              </p>
            )}
          </nav>
        </div>

        <div id="detail">
          <Outlet />
        </div>
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

​这个文件写完后,有2个报错

其中contacts上红线显示:

property contacts doesnot exist on type unknown ts2339

查了一下,挺复杂,主要是也不懂ts语法。

先不管,运行一下,结果居然能显示。也是服了。

可以看到,侧边栏有很多名字了,这就是数据。这些数据都是存在data.ts里的。

虽然报错,但总算是能运行,神奇啊。

8 类型推理

You may have noticed TypeScript complaining about the contact type inside the map. We can add a quick annotation to get type inference about our data with typeof loader:

你可能已经注意到TypeScript在抱怨map中的contact类型。我们可以通过typeof加载器添加一个快速注释来获取数据的类型推断:

onst { contacts } = useLoaderData<typeof loader>();

上面还说有错呢,结果这里就更正了。所以说一直往下看可能有惊喜,没必要浪费那20分钟。试一下也许就行呢。

虽然也不知道到底是啥错误,主要是不懂ts

9 Loader里的URL参数

Click on one of the sidebar links

We should be seeing our old static contact page again, with one difference: the URL now has a real ID for the record.

单击其中一个侧边栏链接

我们应该再次看到旧的静态联系人页面,但有一点不同:URL现在有了记录的真实ID。

Remember the $contactId part of the file name at app/routes/contacts.$contactId.tsx? These dynamic segments will match dynamic (changing) values in that position of the URL. We call these values in the URL "URL Params", or just "params" for short.

还记得app/routes/contacts文件名称中的$contactId部分吗?这些动态段将匹配URL中该位置的动态(变化)值。我们将URL中的这些值称为“URL参数”,或者简称为“参数”。

These params are passed to the loader with keys that match the dynamic segment. For example, our segment is named $contactId so the value will be passed as params.contactId.

这些参数与匹配动态段的键一起传递给加载器。例如,我们的段命名为$contactId,因此值将作为params.contactId传递。

These params are most often used to find a record by ID. Let's try it out.

这些参数最常用于按ID查找记录。我们来试一下。

Add a loader function to the contact page and access data with useLoaderData

往联系人页面添加一个loader函数,用useLoaderData来与数据交互。

只修改标住的地方就可以。我们修改完以后会发现报错,真是坑啊然而作者知道这个情况···这时候还不能运行···只能接着往下走。

10 验证参数并抛出响应

TypeScript is very upset with us, let's make it happy and see what that forces us to consider

TypeScript对我们非常不满,让我们让它高兴一下,看看这会迫使我们考虑什么

还是照着改。

First problem this highlights is we might have gotten the param's name wrong between the file name and the code (maybe you changed the name of the file!). Invariant is a handy function for throwing an error with a custom message when you anticipated a potential issue with your code.

这突出的第一个问题是,我们可能在文件名和代码之间获得了错误的参数名称(可能您更改了文件名!)。当您预计代码可能出现问题时,Invariant是一个方便的函数,用于抛出带有自定义消息的错误。

Next, the useLoaderData<typeof loader>() now knows that we got a contact or null (maybe there is no contact with that ID). This potential null is cumbersome for our component code and the TS errors are flying around still.

接下来,useLoaderData<typeof loader>()现在知道我们得到了一个联系人或null(可能没有联系人的ID)。对于我们的组件代码来说,这个潜在的null很麻烦,TS错误仍然在到处乱飞。

We could account for the possibility of the contact being not found in component code, but the webby thing to do is send a proper 404. We can do that in the loader and solve all of our problems at once.

我们可以考虑在组件代码中找不到联系人的可能性,但是web要做的事情是发送适当的404。我们可以在加载器中这样做,并一次解决所有问题。

上面一大堆,我页看不懂,只能是照着往下走

修改到这里,错误就全部没有了,神奇啊。

Now, if the user isn't found, code execution down this path stops and Remix renders the error path instead. Components in Remix can focus only on the happy path 😁

现在,如果没有找到用户,代码将停止沿此路径执行,Remix将呈现错误路径。Remix中的组件只能关注快乐路径。

运行一下

我只知道,这一步把头像加载出来了。里面全是胡子哥和大姐。要不就是秃头。没个能看的。

接下来的内容还挺多,今天暂时就到这里吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/273328.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开源verilog模拟 iverilog verilator +gtkwave仿真及一点区别

开源的 iverilog verilator 和商业软件动不动几G几十G相比&#xff0c;体积小的几乎可以忽略不计。 两个都比较好用&#xff0c;各有优势。 iverilog兼容性好。 verilator速度快。 配上gtkwave 看波形&#xff0c;仿真工具基本就齐了。 说下基本用法 计数器 counter.v module…

HarmonyOS4.0开发该怎么系统学习,适合哪些人?

对于想要系统学习HarmonyOS 4.0开发的人来说&#xff0c;以下是一些建议&#xff1a; 1.了解HarmonyOS基础&#xff1a; 首先&#xff0c;你需要对HarmonyOS有一个基本的了解&#xff0c;包括它的核心概念、系统架构、分布式技术等。可以通过官方文档、教程和在线课程来深入了…

学习笔记15——前端和http协议

学习笔记系列开头惯例发布一些寻亲消息&#xff0c;感谢关注&#xff01; 链接&#xff1a;https://baobeihuijia.com/bbhj/ 关系 客户端&#xff1a;对连接访问到的前端代码进行解析和渲染&#xff0c;就是浏览器的内核服务器端&#xff1a;按照规则编写前端界面代码 解析标准…

园艺伴侣应用程序Plant-it

什么是 Plant-it &#xff1f; Plant-it 是一款园艺伴侣应用程序&#xff0c;可帮助您照顾植物。它不会建议您采取哪些操作&#xff0c;而是旨在记录您正在执行的活动。这是故意的&#xff0c;软件作者坚信&#xff0c;唯一负责知道何时给植物浇水、何时施肥等的人是你&#xf…

HBase深度历险 | 京东物流技术团队

简介 HBase 的全称是 Hadoop Database&#xff0c;是一个分布式的&#xff0c;可扩展&#xff0c;面向列簇的数据库&#xff0c;是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。本文会像剥洋葱一样&#xff0c;层层剥开她的心。 特点 首先我…

GaN HEMT的大信号(RF PA)性能

来源&#xff1a;Novel Drain-Connected Field Plate GaN HEMT Designs for Improved VBD −RON Tradeoff and RF PA Performance (IEEE TRANSACTIONS ON ELECTRON DEVICES) 使用 TCAD 提取的 I-V 和 C-V 曲线族&#xff0c;结合 Keysight 的 IC-CAP 器件建模套件和先进SPICE模…

工具系列:TimeGPT_(2)使用外生变量时间序列预测

文章目录 TimeGPT使用外生变量时间序列预测导入相关工具包预测欧美国家次日电力价格案例 TimeGPT使用外生变量时间序列预测 外生变量在时间序列预测中非常重要&#xff0c;因为它们提供了可能影响预测的额外信息。这些变量可以包括假日标记、营销支出、天气数据或与你正在预测…

JavaSE语法之十:抽象类(超全!!!)

文章目录 一、抽象类的概念二、抽象类的语法三、抽象类的特征四、抽象类的作用 一、抽象类的概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的。 如果一个类没有包含足够的信息来描绘一个…

Postman接口测试(附教程)

前言 之前还没实际做过接口测试的时候呢&#xff0c;对接口测试这个概念比较渺茫&#xff0c;只能靠百度&#xff0c;查看各种接口实例&#xff0c;然后在工作中也没用上&#xff0c;现在呢是各种各样的接口都丢过来…

1. pytorch mnist 手写数字识别

文章目录 一、数据集介绍1.1、简介1.2 详细介绍1、数据量2、标注量3. 标注类别4.数据下载5.数据集解读 二、读取、加载数据集1、pytorch 自带库函数2、通过重构Dataset类读取特定的MNIST数据或者制作自己的MNIST数据集 三、模型构建四、 runtraintest评估模型的性能检查点的持续…

vue 导出 HTML 结构为 Word 文档(.docx)-支持表格、css样式、图片

在 Web 开发中&#xff0c;有时我们希望用户能够将网页上的 HTML 内容保存为 Word 文档&#xff0c;以便更方便地分享和打印。本文将介绍如何使用 html-docx-js 和 file-saver 这两个 JavaScript 库&#xff0c;实现将 HTML 结构导出为 Word 文档的功能。 工具简介 1. html-d…

智能优化算法应用:基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鱼鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

楼宇对讲门铃的芯片选型分析

目前很多的高层住宅都使用了对讲门铃了&#xff0c;在频繁使用中&#xff0c;门铃会出现的越来越多种类&#xff0c;下面我就简单的介绍会有用到的几款芯片. 语音通话芯片&#xff1a;D34018,D34118,D5020,D31101; D34018 单片电话机通话电路&#xff0c;合并了必 需的放大器…

Android原生实现单选

六年前写的一个控件&#xff0c;一直没有时间总结&#xff0c;趁年底不怎么忙&#xff0c;整理一下之前写过的组件。供大家一起参考学习。废话不多说&#xff0c;先上图。 一、效果图 实现思路使用的是radioGroup加radiobutton组合方式。原理就是通过修改RadioButton 的backgr…

双向循环链表实现C语言关键字中英翻译机 ฅ( ̳• · • ̳ฅ)

目录 1.双向循环链表的声明与定义&#xff1a; 2. 创建链表并对节点中的数据赋初值 3. 插入节点并链接 4.中英翻译 5. 小游戏的实现 6.菜单的实现 7. 释放内存 8.在主函数中用刚才定义的函数实现各种代码 输入样例&#xff1a; 实现方法&#xff1a;双向循环链表来实…

7.7、kali linux环境下搭建DVWA

目录 一、资料下载准备工作 1.1、DVWA源代码下载 二、开启Apache、mysql服务 2.1、下载Apache2文件 2.2、开启Apache2服务 方法一&#xff1a;开启Apache2服务&#xff08;手动&#xff09; 方法二&#xff1a;开启Apache2服务&#xff08;系统自启动&#xff09; 2.3、…

基于springboot超市进销存系统

**&#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;**摘要 随着信息化时代的到来&…

【数字孪生】数字工厂三维可视化大屏,智慧养殖三维可视化大屏,智慧农田三维可视化大屏,智慧运维三维可视化大屏,三维在线展示

个人主页&#xff1a; 左本Web3D&#xff0c;更多案例预览请点击》 在线案例 个人简介&#xff1a;专注Web3D使用ThreeJS实现3D效果技巧和学习案例 &#x1f495; &#x1f495;积跬步以至千里&#xff0c;致敬每个爱学习的你。喜欢的话请三连&#xff0c;有问题请私信或者加微…

通过MobaXterm远程连接Anolis

目录 前言&#xff1a; 一.设置ip 二.远程连接 前言&#xff1a; 小编已经阐述了如何安装Anolis系统&#xff0c;如果有不了解的小伙伴可以查看这一篇博客Anolis安装 这篇博客将会讲述如何远程连接Anolis系统。各位看官拿好板凳&#xff01; 一.设置ip 打开网卡所在位…

logstash收集华为、H3C、Cisco交换机日志

网络设备配置 将 syslog-ip 替换成服务器的IP地址。 Huawei info-center loghost source interface info-center loghost syslog-ip local-time facility local6 H3C info-center loghost source interface info-center loghost syslog-ip facility local5 Aruba logging arm …