龙空技术网

Next.js 事件管理应用程序使用基于文件的路由

科技狠活与软件技术 129

前言:

目前大家对“js获取选择的文件”大体比较注意,咱们都需要分析一些“js获取选择的文件”的相关资讯。那么小编在网络上搜集了一些关于“js获取选择的文件””的相关内容,希望姐妹们能喜欢,小伙伴们快快来了解一下吧!

Next.js 可以说是使用 React 构建 Web 应用程序的最通用的框架。在这里,学习如何构建 Next.js 事件管理应用程序。

在使用 React 构建 Web 应用程序时,Next.js 可以说是最通用的框架。Next.js 使构建生产就绪的应用程序变得容易。在这篇文章中,我们将着眼于构建Next.js 事件管理应用程序。

完成后,我们的应用程序将如下图所示:


这个应用程序将是纯 Next.js 组件与其他 React 组件的混合。此外,还会使用一些 CSS 来设置组件的样式。最后,我们将使用Next.js 基于文件的路由来连接应用程序。
最终,这个应用程序的想法是展示我们如何使用 Next.js 和普通 React 构建一个更大的应用程序。

1. 建立一个新的 Next.js 项目
作为使用 Next.js 的先决条件,我们需要在系统上安装 Node.js。您可以从官方网站为您的操作系统安装 Node.js。

设置好 Node.js 后,我们可以使用 Next.js 开始我们的事件管理应用程序。

首先,我们将使用以下命令创建一个新的 Next.js 项目。

$ npx create-next-app



上面的命令会生成一个启动项目。

无论如何,这里要注意的重要一点是package.json文件。如果打开文件,您将能够看到各种脚本和依赖项。

{
"name": "nextjs-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "12.2.3",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"eslint": "8.20.0",
"eslint-config-next": "12.2.3"
}
}



2. 创建虚拟事件管理数据
虽然一个典型的 prod 应用程序可能有一个数据库来存储信息,但我们将使用一些虚拟事件数据来让我们的应用程序运行。通常,开发人员更喜欢以这种方式开始,以便他们可以在干预与数据库相关的更改等之前先了解应用程序。

在我们项目的根目录下,我们创建了一个文件dummy-data.js. 见下文:

const DUMMY_EVENTS = [
{
id: 'event1',
title: 'Programming for Everyone',
description: 'Everyone can learn to code! Yes, everyone! Live Event',
location: 'A street 25, San Francisco',
date: '2022-06-14',
image: 'images/coding-event.jpg',
isFeatured: false
},
{
id: 'event2',
title: 'Networking Basics',
description: 'Making networking for introverts fun',
location: 'Street 47, New York',
date: '2022-06-21',
image: 'images/network-event.jpg',
isFeatured: true
},
{
id: 'event2',
title: 'Networking Advanced',
description: 'Making networking for advanced use-cases',
location: 'Street 47, New York',
date: '2022-07-25',
image: 'images/network-event-advanced.jpg',
isFeatured: true
}
]

export function getFeaturedEvents() {
return DUMMY_EVENTS.filter((event) => event.isFeatured);
}

export function getAllEvents() {
return DUMMY_EVENTS;
}

export function getFilteredEvents(dateFilter) {
const { year, month } = dateFilter;

let filteredEvents = DUMMY_EVENTS.filter((event) => {
const eventDate = new Date(event.date);
return eventDate.getFullYear() === year && eventDate.getMonth() === month - 1;

})

return filteredEvents;
}

export function getEventById(id) {
return DUMMY_EVENTS.find((event) => event.id === id);
}

如您所见,我们有DUMMY_EVENTS一个包含事件列表及其各种详细信息的数组。此外,我们从这个文件中导出了一堆函数。

基本上,这些函数用于从事件数组中获取或过滤事件。以下是每个功能的详细信息。

getFeaturedEvents()– 此函数返回一个事件列表,其isFeatured标志设置为 true。
getAllEvents()– 此函数返回所有事件。
getFilteredEvents()– 此函数根据过滤条件返回事件列表。在当前的实现中,我们支持按年和月过滤。
getEventById()– 最后,此函数为输入事件 ID 返回单个事件。
您可以将这些函数视为获取事件日期的接口。我们没有将这些函数公开为外部 REST API,因为我们只会在应用程序内部使用它们。

3. 使用基于文件的路由创建 Next.js 路由
此时,我们可以开始为我们的事件管理应用程序的各个页面构建 Next.js 路由。

从广义上讲,我们将为我们的申请提供以下途径:

The root path (/) – 这是起始页面,将显示特色事件列表。换句话说,那些isFeatured标志设置为真的事件。
All events page (/events)– 此页面将显示所有事件的列表。
Single Event (/events/<some_id>) – 此页面将根据输入 id 显示单个事件的详细信息。
Filtered Events (/events/…slug) – 此页面将显示基于条件的过滤事件列表。例如,如果我们访问 /events/2022/06,它应该显示 2022 年 6 月的事件列表。
对于上述每个路径,让我们创建适当的 Next.js 组件。

3.1:主页
Next.js 有一个特殊的系统来处理路由。

基本上,pages我们的项目中有一个特定的文件夹。我们在这个文件夹中创建的任何组件都会被 Next.js 公开为路由。这也称为 Next.js 基于文件的路由。

在pages目录中,我们将创建一个名为index.js. 这是用于渲染我们应用程序主页的文件。

见下文:

import { getFeaturedEvents } from '../dummy-data';
import EventList from '../components/events/EventList';

function HomePage() {

const featuredEvents = getFeaturedEvents();

return (
<div>
<EventList items={featuredEvents} />
</div>)
}

export default HomePage;
导出 默认 主页;


如您所见,这是一个普通的 React 组件。它从作为dummy-data.js文件一部分公开的适当函数中获取特色事件列表。

一旦获得数据,它就会将列表传递给另一个 React 组件EventList。下一节将介绍 React 组件的详细信息以供参考。

3.2:所有活动页面
此页面显示所有事件的列表。为了更好的隔离,我们将此页面的组件文件放在目录内的文件夹events中pages。

import { useRouter } from 'next/router';

import EventList from "../../components/events/EventList";
import EventSearch from "../../components/events/EventSearch";
import { getAllEvents } from "../../dummy-data";

function AllEventsPage() {
const router = useRouter();
const events = getAllEvents();

function findEventsHandler(year, month) {
const fullPath = `/events/${year}/${month}`;
router.push(fullPath);
}

return (
<div>
<EventSearch onSearch={findEventsHandler} />
<EventList items={events} />
</div>
)
}

export default AllEventsPage;
导出 默认 AllEventsPage;


在这个组件中有几个要点需要注意:

首先,我们使用该getAllEvents()函数从虚拟事件数据中获取所有事件。EventList该列表使用通用组件呈现。
其次,我们还具有根据此页面上的过滤条件搜索事件的功能。为此,我们有一个EventSearch组件。onSearch这个组件接受一个指向findEventsHandler函数的 prop 。基本上,EventSearch组件将过滤年份和过滤月份传递给AllEventsPage组件。使用过滤器年份和过滤器月份,我们构建了一个路由路径并使用该router.push()实用程序以编程方式更改我们应用程序的路由。
您可以在下面查看 EventSearch 组件的代码:

component/events/EventSearch.jsimport { useRef } from 'react';

import Button from "../ui/Button";
import classes from "./event-search.module.css";

function EventSearch(props) {

const yearInputRef = useRef();
const monthInputref = useRef();

function submitHandler(event) {
event.preventDefault();

const selectedYear = yearInputRef.current.value;
const selectedMonth = monthInputref.current.value;

props.onSearch(selectedYear, selectedMonth);
}

return (
<form className={classes.form} onSubmit={submitHandler}>
<div className={classes.controls}>
<div className={classes.control}>
<label htmlFor="year">Year</label>
<select id="year" ref={yearInputRef}>
<option value="2021">2021</option>
<option value="2022">2022</option>
</select>
</div>
<div className={classes.control}>
<label htmlFor="month">Month</label>
<select id="month" ref={monthInputref}>
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>
<Button>Find Events</Button>
</form>
)
}

export default EventSearch;


基本上,这个组件只是简单地处理过滤年份和过滤月份的表单字段。

目前,根据此文件,我们仅支持 2021 年和 2022 年。在实际应用程序中,我们将在此组件中有一个日历小部件。

当用户通过单击表单按钮提交表单时,我们称之为props.onSearch将selectedYearand传递selectedMonth给父组件。

3.3:单一事件页面
单一事件页面基本上是特定事件的页面。它只显示所选事件的详细信息。

从实现的角度来看,它是一个极其简单的组件。见下文:

import { useRouter } from 'next/router';
import EventItem from '../../components/events/EventItem';
import { getEventById } from '../../dummy-data';

function EventDetailPage() {
const router = useRouter();
const eventId = router.query.eventId;

const event = getEventById(eventId);

if (!event) {
return <p>No Event Found</p>
}

return (
<EventItem
id={event.id}
title={event.title}
location={event.location}
date={event.date}
image={event.image} />
)
}

export default EventDetailPage;
导出 默认 EventDetailPage;


唯一需要注意的是,这是一个动态页面。换句话说,内容取决于eventId路径中的。

在 Next.js 中,我们创建了这样的组件,其命名约定为[eventId].js. 基本上,这表示它eventId是动态的并且在浏览器路径中可用。为了提取eventId,我们利用useRouter()钩子然后调用getEventById()函数。

这个页面也使用了一个通用组件EventItem。我们将在下一节中介绍它。

3.4:过滤事件页面
最后,我们还可以为过滤后的事件创建一个页面。

由于这也是一个取决于年月值的动态页面,我们将文件命名为[...slug].js. 在这里,slug 将包含路径所有部分的列表。

例如,如果我们访问/events/2022/06,则 slug 数组将包含 values ['2022', '06']。

查看以下实现:

import { useRouter } from 'next/router';
import EventList from '../../components/events/EventList';
import { getFilteredEvents } from '../../dummy-data';

function FilteredEventsPage() {

const router = useRouter();
const filterData = router.query.slug;

if (!filterData) {
return <p className='center'>Loading...</p>
}

const filteredYear = filterData[0];
const filteredMonth = filterData[1];

const numYear = +filteredYear;
const numMonth = +filteredMonth;

if (isNaN(numYear) || isNaN(numMonth)) {
return <p className='center'>Invalid Filter Criteria. Please check...</p>
}

const filteredEvents = getFilteredEvents({
year: numYear,
month: numMonth
});

if (!filteredEvents || filteredEvents.length === 0) {
return <p>No Events Found!!</p>
}

return(
<div>
<EventList items={filteredEvents} />
</div>
)
}

export default FilteredEventsPage;
导出 默认 FilteredEventsPage;


就像之前的组件一样,这里我们也使用useRouter()钩子来提取slug. 然后,我们确保年份和月份具有数值。如果值没问题,我们只需调用该getFilteredEvents()函数。

再一次,我们使用相同的EventList组件来呈现事件列表。

4. 处理事件数据的常用 React 组件
现在我们的应用程序的主要页面已经完成,让我们看看我们在应用程序中使用的常见 React 组件。

为了更好地管理我们的源代码,我们将通用组件保存在一个名为components. 请注意,我们不能将这些组件保留在pages目录中。这是因为pagesNext.js 使用目录中的任何内容来创建路由。

在该components目录中,我们为与事件相关的组件创建一个文件夹。

第一个重要的组件是EventList组件。

components/events/EventList.jsimport EventItem from './EventItem';
import classes from './event-list.module.css';

function EventList(props) {
const { items } = props;

return (
<ul className={classes.list}>
{items.map(event => <EventItem key={event.id}
id={event.id}
title={event.title}
location={event.location}
date={event.date}
image={event.image} />)}
</ul>
)
}

export default EventList;


基本上,该组件接收事件列表并生成无序列表。它还使用EventItem组件。见下文:

components/events/EventItem.jsimport Link from 'next/link';
import Button from '../ui/Button';

import classes from './event-item.module.css';

function EventItem(props) {

const { title, image, date, location, id } = props;

const humanReadableDate = new Date(date).toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric'
});

const formattedAddress = location.replace(', ', '\n')

const exploreLink = `/events/${id}`

return (
<li className={classes.item}>
<img src={'/' + image} alt={title} />
<div className={classes.content}>
<div className={classes.summary}>
<h2>{title}</h2>
<div className={classes.date}>
<time>{humanReadableDate}</time>
</div>
</div>
<div className={classes.address}>
<address>{formattedAddress}</address>
</div>
</div>
<div className={classes.actions}>
<Button link={exploreLink}>Explore Event</Button>
</div>
</li>
)
}

export default EventItem;


基本上,该组件接收单个事件的数据。它重新格式化数据以用于演示目的:例如,将日期更改为人类可读的格式并格式化地址。此外,它为Explore Event的按钮构造了适当的链接。

此外,我们还有一个特殊的Button组件。

components/ui/Button.jsimport Link from 'next/link';
import classes from './button.module.css';

function Button(props) {
if (props.link) {
return <Link href={props.link}>
<a className={classes.btn}>{props.children}</a>
</Link>
}

return <button className={classes.btn} onClick={props.onClick}>{props.children}</button>

}

export default Button;


基本上,该Button组件处理它充当Link. 此外,如果props.link未定义,则它充当普通按钮。

5. Next.js 事件管理应用导航
虽然我们的应用程序显示了各个页面,但没有适当的导航栏。我们在每个页面上都需要这个导航栏。

因此,我们为此创建了另一个通用组件。

首先是Layout组件。

components/layout/Layout.jsimport { Fragment } from "react";
import MainHeader from "./MainHeader";

function Layout(props) {
return <Fragment>
<MainHeader />
<main>
{props.children}
</main>
</Fragment>
}

export default Layout;


第二个是MainHeader组件。

components/events/MainHeader.jsimport Link from 'next/link';
import classes from './main-header.module.css';

function MainHeader() {
return (
<header className={classes.header}>
<div className={classes.logo}>
<Link href="/">Next Events</Link>
</div>
<nav className={classes.navigation}>
<Link href="/events">All Events</Link>

</nav>
</header>
)
}

export default MainHeader;


基本上,这是我们定义应用程序徽标和导航到AllEvents页面的链接的地方。为了导航,我们使用LinkNext.js 附带的特殊组件。Next.js Link 组件在不使用 React 路由器的情况下帮助应用导航。

最后,为了在每个页面上显示此内容,我们将应用程序的主要组件,即目录中的MyApp组件(_app.js 文件)包装起来pages。
import Layout from '../components/layout/layout'
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
return <Layout>
<Component {...pageProps} />
</Layout>
}

export default MyApp



请注意,此文件存在于启动项目中。我们只需要修改相同的。

6. 为 Next.js 应用程序设计样式
最后,您可能已经注意到我们在各种组件中使用了一堆 CSS 类。为了将 CSS 限定到特定组件,我们使用了 CSS 模块系统。

虽然 CSS 是完全可选的,但它确实有助于我们项目的外观和感觉。

下面是各种组件的 CSS 文件。

.item {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3), 0 1px 12px 2px rgba(0, 0, 0, 0.2);
border-radius: 8px;
overflow: hidden;
background-color: white;
margin: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
}

.item img {
width: 100%;
object-fit: cover;
height: 10rem;
}

.content {
width: 100%;
padding: 0 1rem;
text-align: center;
}

.content h2 {
margin: 0.5rem 0;
}

.date,
.address {
display: flex;
gap: 0.5rem;
align-items: center;
}

.date svg,
.address svg {
width: 1.25rem;
height: 1.25rem;
color: #666666;
}

.content time {
color: #666666;
font-weight: bold;
}

.content address {
margin: 0.5rem 0;
color: #666666;
white-space: pre;
}

.actions {
display: flex;
flex-direction: column;
padding: 1rem;
}

.actions a {
display: block;
}

.actions a span {
vertical-align: middle;
}

.icon {
margin-left: 0.5rem;
display: inline-flex;
justify-content: center;
align-items: center;
}

.icon svg {
width: 1.25rem;
height: 1.25rem;
}

@media (min-width: 768px) {
.item {
flex-direction: row;
}

.item img {
width: 40%;
height: 14rem;
}

.content {
width: 60%;
padding: 0;
text-align: left;
}

.content h2 {
margin: 1rem 0;
}

.actions {
flex-direction: row;
justify-content: flex-end;
}
}

您可以将这些 CSS 文件放在项目层次结构中的组件文件旁边。这将有助于轻松参考和维护。

结论
有了这个,我们的Next.js 事件管理应用程序就准备好了。

我们使用基于文件的路由来使高级页面工作。但是,对于个别功能,我们利用了基本的 React 组件。将 Next.js 概念与 React 结合使用,这使得 Next.js 成为构建复杂应用程序的绝佳工具。

此应用程序的代码可在GitHub上找到。

我们可以使用Next.js Firebase 集成进一步增强应用程序,用于静态和服务器端呈现和存储数据。

如果您对这篇文章有任何意见或疑问,请随时在下面的评论部分中提及。


标签: #js获取选择的文件